Should I always check user existence in the process of authenticating? How if users deleted the account while they still have their API KEY containing user id? It can mess up some part of the system that involve the user.
Related
According to Next-auth documentation as we are using credentials provider to connect to our users collection for usernames and passwords Next-Auth does not use the session database to check if a session is active.
If you use a custom credentials provider user accounts will not be persisted in a database by NextAuth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider.
I am wanting to add a _middleware that will allow me to store and check that the latest JWT sessions inside our session database matches the latest one that the user is currently using.
Reason being is that if I have two devices technically I would be able to login on both devices and at the moment their is no real way to discern if the user from PC2 is also login on PC1.
So my theory and not sure if this will work is to add the following.
callbacks: {
jwt: async ({ token, user }) => {
console.log("running JWT - because of custom login")
user && (token.user = user)
(ADD CODE HERE TO SAVE TOKEN & CHECK IF TOKEN IS LATEST TOKEN + VALID - INSIDE SESSION DATABASE)
(IF OLD-TOKEN IS NO LONGER VALID OR THE LATEST TOKEN LOG THE USER OUT)
console.log("TOKEN IS "+ JSON.stringify(token))
return token
},
session: async ({ session, token, user }) => {
console.log(JSON.stringify(session) +" / "+ JSON.stringify(token) +"/"+ JSON.stringify(user));
session.user.tokenID = token //ADD CODE HERE TO SAVE TOKEN TO SESSION COOKIE
session.user = user
return session
}
},
Then if I create a middleware that checks this tokenID and matches it with the session database and if it is the latest result from said user.
For example.
Say PC1 (user1) login here
{
_id: 1
tokenID: 918171-918171-81716-0887
userid: 00-00-00-001
expire: "2022-05-23T12:47:04.593Z"
}
But then PC2 also (user1) login again and created a new session
{
_id: 2
tokenID: 71888-651777-616666-0117
userid: 00-00-00-001
expire: "2022-05-24T12:47:04.593Z"
}
What I would need the middleware to do (which a simple mongodb query could do) is check if their is an older session stored for the same userID if so then logout from PC1.
Now there are a few things that I can see going wrong with this idea.
Other Provider Sessions (which use session DB) making it harder to validate
Every time you call the signup page or session it seems to re-run the JWT section - which in theory is fine, as we could use a findOne Update function which if token is in session then just update the expiry - however that would cause say PC1 refreshes after PC2 logged in then PC1 expire time might be longer then PC2 expire time (but a simple sort function would allow us to see if the ID was older then PC2 if so logout).
JWT changes token every time you reload the page
How would this help privacy and the user data?
By not storing the user details inside a session cookie we would not be exposing the data to hackers or other plugins like FB or Google as the user data would only be linked to a token ID. Which to request the user data you would have to make sure the tokenID was valid first and then be allowed to fetch user data.
I understand that Next-Auth may not want too do this, and this is why I ask the question what is the best practice to do what I am wanting to achieve.
This answer is based on the confirmation that the issue is that you want to be able to only have users able to be signed in to one computer/device at time and that you are using usernames and passwords to authenticate them.
In that scenario you also need to have a database that records a token in each JWT issued. It is not possible to solve for that problem without a database.
Here is how you can solve for it it using JWT and a database:
On every new sign in, you would need to use the jwt callback to add something like a UUID to each JWT and then record that UUID , the User ID and the time the JWT expires in a database.
In that callback, if there are other entries in the database for the same User ID you should mark them as invalid (or delete them from the database).
Everytime an existing JWT is read in that same callback you would need to check to see if the UUID in the database was still valid (i.e. still exist / doesn't point to a UUID that corresponds to a JWT flagged as expired) and if it is no longer valid, don't return a valid JWT.
You might also want to add special handling in the session callback that does something similar to improve the user experience by gracefully handling it in the User Interface of the computer they are being signed out of.
Effectively this has all the downsides of a JWT with all the downsides of a session database (although there are niche cases where this is a preferable approach).
I would not recommend using usernames and passwords or limiting users to only being able to sign in to one computer at at time, however given those unusually specific constraints (which also necessitates a solution that has a negative impact on performance) you may want to consider a different authentication solution and/or think how else you could address the underlying need this is trying to address (and if it's worth the cost and complexity).
So im using Firebase to authenticate a user and that works fine. In the end I get a uid, which I can uniquely identify a user. I dont want to use any other firebase tools since I dont believe they are suited for my application, so I want to use mongoDB for document storage. I can thus use the uid as the key to the person ( and other ) data.
My question is about security. What is someone gets access to the uid? and since this uid might link to other user's uid, if someone gets access, then they might be able to just call a collection.get on any uid and get potentially user sensitive data. How do I prevent this interaction? Am I overthinking this and mongo somehow handles this? Im not quite sure how to authorize that the current user is the current uid and hes not calling any document retrieval that is not allowed. Thanks
A UID merely identifies a user. You should never use a UID as an authentication mechanism, but instead require that the user enters the required credentials that then lead them to get the same UID. See also my answer on Firebase - Is auth.uid a shared secret?
If you're accessing MongoDB from a server, you'll typically:
Sign the user in on the client-side app.
Get their ID token and pass that token to your server over a secure connection.
Verify the ID token on the server using a Firebase Admin SDK, or other library.
Then determine what data the user is authorized based on their UID or other properties/claims from their ID token.
This process is pretty well documented in the Firebase docs on verifying ID tokens.
I'm working on a mobile application that uses Stormpath on the server side for authentication and authorization. I need to support the same user signing in on more than one device, but I want to be able to keep track of it and limit it if I want to.
My application currently uses Stormpath to sign in the user using email/password or MDN/password and upon successful login returns a JWT token to use for API access to the server.
I'm thinking of the following approach:
Keep a list of sessions in the user's account. Every time the user signs in, a new entry is added with the device_id and the JWT provided. When the user signs off, the entry is removed or marked inactive.
When a user tries to sign in on another device, if I want to restrict to only one active device, I would set the other entry to disabled and expire the JWT so the application can detect it and require login again.
If I wanted to restrict the user to a maximum of n sessions, I could just count the entries and force the user to sign off on one of the other sessions before allowing her/him to sign in on the new device
Is this a good approach? Is there a better way to do it? What are the issues with this method?
I work at Stormpath on the mobile SDKs. You can use the access / refresh token feature that we have to accomplish this.
Every time the user signs in, an access and refresh token are created. When the user signs off, the refresh token is deleted, as well as the access token.
When a user tries to sign in on another device, if you want to restrict to only one active device, you can delete all of the other access & refresh tokens.
If you wanted to restrict the user to a maximum of n sessions, I could just count the entries and force the user to delete one of the refresh tokens before allowing her/him to sign in on the new device. You would then go through the access tokens, and delete ones with a matching "rti" (refresh token ID)
Several notes about implementing this:
If you're using a Stormpath Framework integration, the default is to verify an access token locally (instead of sending it to Stormpath to validate). This is because they have a signature that can be validated by the SDK. However, to log out a user, you'll either have to set this to remote validation, or use a short access token life (and use the refresh tokens to control each "session")
Refresh tokens can't store "customData", so you'll have to maintain metadata about the refresh token in either the account's customData, or in your own database.
Alternatively, you could "create" API Keys for each user, and use that instead of sessions for each user. You can use the API Key name or description attributes to keep track of where the user signed in from / etc.
I'm doing a Scala - Play application and I want to disconnect an user when an admin change his right. For exemple, is an user is logged and an admin upgrade his account to the admin type, I want disconnect this user.How can I do that ?
If you stored the userId in the Session you will need to add the rights of the user in the Session.
So that when the user connects, you can check his rights from the session to the ones in your database. If they don't match you can redirect the user to the login page.
just to cover all the bases since the answer here depends specifically on how your application determines if a user is logged in:
if user auth is done with a token generated by a secret that is stored on your user model and checked for validity on every request, you can generate a new secret for the user and all existing tokens will become invalid.
Facing problem on how to design api for admin and non admin account. I following MVC pattern, where controller will map to a uri.
Make up scenario:
Let said I have a system that allow user to redeem gift after they spend money on buying merchandise and collect points.
My system has a user profile, gift, and redeem table.
Each user can redeem many gift and each gift can be redeem by many user. (Redeem is the junction table).
To access user profile, the uri will be like this:
GET /user/0001
To redeem a gift
POST /user/0001/redeem {"amount":1, "address":"old address"}
Everything seem nice and tidy up to this point. Now the problem arise when the redeem need to be approve by a admin user
So to update the redeem to approved status
PUT /user/0001/redeem/100 {"status":"approve"}
User can update the shipping address where the gift will be ship to
PUT /user/0001/redeem/100 {"address":"New address"}
Now I facing the following problem:
If 2 path point to uri, the controller will have 2 different handling code in the same function.1 if block for admin, 1 if block for user.
If I add a admin in front of the uri (admin/user/0001/redeem/100). 2 different path will identify the same resource.
To identify user identity was done on the Authorization header (Authorization="USERNAME:SECURITY_HASH"). Is this a typical restful approach?
I cannot differentiate user role because the admin and user was store in diffrent table. So if the uri indicate the user role, this will make things easier. I do not want to always query for admin table before user.
I would do the following:
1) have URL like POST /admin/user/1000/redeem/approve
Reasons:
I don't want to expose the knowledge about which field in user record has to be changed to approve voucher
I prefer to keep all admin actions separate from normal user actions
2) One of the "usual" ways is to have a (periodically renewed) session cookie (not related to username or password hash) that is issued after successful login and then separately a mechanism that would associate this cookie with username and check whether for this username the given action is allowed.
Reasons: you reduce the danger of exposing the information about your users. (This approach might be still vulnerable if the attacker steals the cookie, but so is the other one).
3) That you store admin and user data in different tables is not an excuse if we talk about the security :) Using the cookie mechanism described above might help here, but you'll still have to establish the way of verifying that given user can invoke given method.