I am trying to determine the best way to enforce password expiration rules in my solution.
The server-side exposes a REST API for operations with a custom active Security Token Service (of sorts). Client applications pass user credentials to a REST endpoint where the user is authenticated on the server. The response includes a custom security token representing the user which is then passed to other API methods so the call can be authorized. The server is stateless and does not maintain any reference to the identity or claims information (i.e. session-less).
We have password expiration rules that are enforced by the server. So, when authenticating a user, it is possible that their password has expired. I need to communicate this to the client so they can do whatever is needed to have the user change their password. (There is another REST endpoint for changing the password on the server.)
Questions
It seems to me that authenticating a user with an expired password should fail. However, I need to know the identity of the user changing the password when making the second API call, so should I go ahead and return a token even when the password has expired?
How should I inform the client that a password change is required? I thought about including this as a claim in the token, but that would require me to reissue a new token after the password has been changed or modify the original token which isn't allowed. My other thought was a custom HTTP Status Code that I would correspond to meaning Password Change Required.
The answer to this question probably depends on the previous two, but I don't want to authorize a user that has an expired password if the token is passed to any other APIs (besides changing the password). What's the best way to handle this?
So, what I ended up doing (certainly not the be-all-end-all solution) is having my Authenticate endpoint return an AuthenticationResultCode enumerated value in the response in lieu of a simple pass/fail boolean. Possible values for the enumeration include:
ValidCredentials - the user was authenticated and the AuthenticationToken is included in the response.
InvalidCredentials - the user was not authenticated
CredentialsExpired - the user was authenticated but their password has expired. I have yet to determine if the AuthToken will be included with this result.
NoCredentials - no credentials were provided to the request
Now the client has more information about the result than a pass/fail value (which I really never checked anyway) and I can take the appropriate action in response such as automatically displaying the ChangePasswordDialog when CredentialsExpired is received.
Like I said, still working out whether or not I should still send the token when the credentials are expired because I don't want to be able to authorize a user if their credentials are expired but they've already been authenticated once and I don't think it makes sense to re-authenticate after changing the password (after all, I have to be authenticated so I can change the password in the first place). Maybe just a simple IsLocked or IsExpired property on the client will suffice...
Related
I have recently started using json web tokens and I have a few unanswered questions
what would happen if a users token expires while they are online? would they be forced to log in again when they request the next protected route? and if so it feels like there has to be a way around this so that the user does not randomly get logged out
how would you log someone out before the jwts expiration date considering the client holds all the info
any clarification on those 2 questions would be appreciated
what would happen if a users token expires while they are online? would they be forced to log in again when they request the next protected route? and if so it feels like there has to be a way around this so that the user does not randomly get logged out
When you make a request in a webapp, you need to pull in the available token. So the answer is "what does your app do?". There are lots of things you can do, such as making the request with the invalid token, or no token at all. But obviously that's not really a good idea. The best strategy is to:
Before every API call needing a token, check if the current token is valid
If the token isn't valid ask your auth provider to get a new token. This is known as silent authentication. How the provider handles this is totally up to them, usually they have a HttpOnly cookie saved for a secure domain, and return a new JWT.
If the silent auth fails, then the provider will tell you to log the user in.
At this point the best course of action is to redirect to the user to the auth provider using the same "login strategy" they previously used, to get a new "session" and a new "token".
how would you log someone out before the jwts expiration date considering the client holds all the info
Logging out is completely separate from token expiration. Logging out from a user standpoint is:
Tell the auth provider to remove the HttpOnly secure cookie
Delete the JWT in the browser
Change the UI to display the user as no longer logged in.
But that doesn't expire the token. The token will always be valid until the exp date. If you want to also prevent that token being used as if it was valid, you must deny-list the token using your AuthZ IAM as a service. For instance, if you were using Authress to manage access control, a DELETE https://api.authress.io/v1/users/{userId}/tokens/{tokenId} would cause subsequent checks using that token to return Forbidden. However, I will stress, the token is still valid, but you've taken the step to mark it as Deny-listed.
would they be forced to log in again when they request the next protected route?
If there is no other information that associates the session with the logged-in user (for example, there aren't any cookies or other tokens the server can use), yes.
Yeah, it's not very user-friendly, and JWTs often have a short expiry time. A common solution to this issue is to provide a refresh token as well, which could be stored in a HttpOnly cookie:
A refresh token has 2 properties:
It can be used to make an API call (say, /refresh_token) to fetch a new JWT token before the previous JWT expires.
It can be safely persisted across sessions on the client!
This way, the client can always have a valid JWT at any one time; they won't have to log back in, or logout and then log back in.
how would you log someone out before the jwts expiration date considering the client holds all the info
You can implement logic on the server to somehow blacklist or ignore the JWT from a particular user until they log in again. For example, after parsing the JWT, you could check to see whether the server considers the token to still be usable or not. If not, return a 401 error.
I have a question regarding the concept of JWT token type of authentication.
So suppose I sign a jwt token after user logged in, the token payload contains the username and userRole. I then return the token to the user and store in user's localStorage.
Now, if i change the userrole in database, i.e. from normalUser to AdminUser, how can I then ensure the payload on user's localStorage also change?
I have an issue when if the user is an admin user, when he signs in, a jwt is stored in his localStorage. Afterwards, I set his role to normal user, he can still access the admin features because his localStorage contains the role of AminUser.
Can someone suggests a correct way to implement authorization so that the token is updated on user end?
Thanks.
What is your trigger for changing the user's authorization? If you're saying that you're opening your database client (Say for example MySQL or PostgreSQL) and manually changing the authorization for the user, then there's no way your server or your client to know of that change (As far as I know at least) and they cannot react to that particular change.
If your trigger was some request sent by the user, say logout or change authorization, then you should respond with the new token for that particular request and store it, easy peasy.
If your trigger wasn't related to your client, and it's somehow something happening on your serverside, then you should have a socket opened between your server and your client, and emit that change from your server to your client.
That way you ensure the client is always up-to-date.
But that's still not enough, because yeah you're keeping the client up to date, but what if he saved the token and replaced it after your update? He/She can still access admin features even though you told them they're just normal users. For that you're gonna need to validate every request done by any of your users, and check if they're allowed to make that request in the first place.
You cannot really trust clients. You should find a way to invalidate your jwt tokens. Your client should get a new token when you reject them. You can rotate your tokens using refresh key.
In order to do that:
Keep your token lifetimes short
Or:
Store blacklisted tokens in the database and reject the invalidated tokens.
I have read many articles and viewed many videos but there are a lot of contradictions. I try to avoid any external libraries and build the system from scratch, I have read about oAuth 2 but it is more confusing.
This is the flow that I think is ok untill now:
User fills a form using email and password and submits it.
Server verifies the password if it matches and responds back with a httponly cookie with a signed jwt token that expires in like 10
minutes. (I know I have to protect it against csrf attacks)
User gets logged in and every new request he is making to the server he will send the cookie in the header automatically and the
server will verify the token.
Everything is fine but I have encountered some issues and have some questions:
I want the user to stay logged in even after opening a new session so there is no need to login after the token expired or when he closes the browser.
What should happen if the access token expired?
There should be a refresh token attached to the user in database that gets added when the user logs in with an expiration of ex 7 days, then the server will respond with a cookie containing that refresh token?
On the new request while access token is expired,the user will send the refresh cookie to the server, if it matches the user database refresh token,server will respond with a separate cookie that will renew the access token?
If there is a refresh token where should you store it and what format? (cookie,database or where?)
Should I keep the user logged in based on this refresh token cookie?If is it httponly I can't read it and set the state that user is logged in. How should I do it?
I heard about that revoking the jwt token is problematic. How would you fix it?
How would you do this whole thing?Please explain the workflow, I try to avoid localstorage,as I read everywhere that is not safe for sensitive data.
I have implemented and deployed to production systems that do exactly the kinds of things that you are asking about here so I think that I am qualified to provide you with some guidance to solve your particular issues and answer your questions. The flow that you have listed above in the numbered list is definitely the correct path so far. I do understand your confusion going forward from there because there are many different options for how to approach this problem.
In addition to providing a login route that returns a new JWT to the client when the user submits a login form to the server, I would recommend also implementing a token refresh route that accepts a still valid JWT that was received from the initial login process and returns a new JWT with an updated expiration time. The logic for this new token refresh route should first verify that the provided JWT is still valid by matching it with a user in the database. Then, it should generate a new token using the same JWT generation logic as the login route logic. Then, the application should overwrite the access token data in the database for the user replacing the old access token with the newly generated access token. It is not necessary to keep an old access token in the database once it is no longer valid, which is why I suggest simply overwriting it with a new one. Once all of that is finished and successful, you can return the new JWT to the client and then the client should now use that new JWT when making any additional authenticated calls to the server to maintain an authenticated interaction with the server. This logic flow would keep the user logged in, because the client would have a valid JWT before calling the refresh logic and it would have a valid JWT after calling the refresh logic. The user should only be recognized as not logged in and not authenticated if they are no longer able to provide a valid access token that is associated with a user in the database.
As far as cookies go, whichever method that you use for maintaining the cookies on your client should be used for setting the refreshed access token as it is for setting the initial access token that you receive on login. If the server finds that an access token is no longer valid at some point in the future, if for example your client is not used after login until some time after the access token has expired, then the client should recognize a server response indicating that this is the case and present the user with the login flow on the client again so that a new access token can be acquired and stored in a cookie on the client.
I would not worry about revoking JWTs and instead just let them expire if they do and initiate a new login flow if it is found that a JWT has expired. Also, instead of using local storage I would suggest using session storage to store your JWT so that you have it for the duration of your user's session on the website and it is removed as soon as the browser has been closed. This will prevent the JWT from persisting beyond the session and should assuage your fears about saving sensitive data in the session storage. Also, when generating your JWT, you should also make a point of not storing any sensitive data in it because JWTs are easily reverse-engineered. This can also prevent any sort of sensitive data from being exposed on the client.
EDIT:
The key thing to remember when developing your server API is that you should have two different classes of endpoints. One set should be unauthenticated and one set should be authenticated.
The authenticated set of endpoints would not require an access token to be included in the request. An example of this class of endpoint would be your login endpoint, which does not require an access token because it actually generates an access token for you to use later on. Any other endpoint that does not expose sensitive or important information could be included in this class of endpoints.
The unauthenticated set of endpoints would require an access token to be included in the request, and if no access token or an invalid access token is detected the endpoint would respond with a 401 HTTP response code (indicating an unauthorized request). An example of this class of endpoint would be an endpoint that allows a user to update their personal information. Obviously, a user cannot update their own information if they cannot provide credentials to prove that they are the user whose information they are attempting to update. If the client receives a response with a 401 response code, that would be the signal that the client would need in order to tell the user to re-login so that a new valid access token can be retrieved. This possibility can be avoided on the client if the client is programmed to periodically check the expiration of the JWT that is currently being held on the client and initiate an access token refresh, but obviously you should still have logic in place to detect and respond to a 401 response code so that the client user flow is managed properly.
is it possible to make jwt token expired after one of the user's property has changed ( for example - activated: false or role: [ADMIN] )? Or maybe there is another way to solve this problem?
Typically a JWT expires only when the expiry date for that token passes. But this doesn't mean that a JWT cannot expire logically for other reasons. One example would be that your Node application receives a request with a valid JWT, but the token belongs to a user whose account was recently deactivated. The workflow might look something like this:
Your Node app receives the JWT
It decrypts the JWT, and extracts the username
Then it queries a user table in your database to check the status of the account
Having discovered that the account is not active, your Node app redirects to the login page with an error message
It is generally up to you to determine how to proceed with a JWT. The JWT just protects the token from being manipulated by the user.
I am using JWT for authentication. However I do not want the user to be logged in from multiple devices. How do I ensure this?
Right now - All I can think of is to store the JWT into DB and then check if it exists . And if it exists, what was the time it was generated at. If too much time - we go and regenerate the token and pass on back to the 2nd device.
That's pretty much your only option, the JWT is pretty stateless on purpose. Similar to how you can't really do a server side logout without similar technique
As jfriend points out, storing the JWT alone is insufficient. What you need to do with it is ensure that the next time a user requests login, that they don't already have an unexpired JWT issued to them.
Going through the flow for completeness:
Case 1: User isn't logged in anywhere. In this case, JWT is issued and stored. Possibly in the user record for easy retrieval.
Case 2: User tries to log in on another device. Whether you make them explicitly log out of the first device or do it for them, you have to now send that stored token into a list of revoked tokens. Your token validation logic will have to take that list into account when determining if a token is valid or not.
/* Further clarification */
I feel like a bit more detail might be useful for folks, so I'm going to go into implementation a bit.
** Unauthenticated requests **
This shouldn't change, but it's worth mentioning that I'm assuming you have routes that require authentication, and requests to those routes which do not include an active and valid JWT get rejected with a 401 (and probably provided the URL to the login url).
Login
Login logic always includes a user lookup, so as described above, the flow in this application should include that lookup, but before logging the user in the application you will check to see if there's already an assigned token to the user which has not expired.
If there is not a token already assigned to the user, then check credentials however you normally would, generate a JWT (with an exp heading to indicate expiration time in the payload), save that token back in the user document/record for future reference.
If there is an assigned token which is also unexpired, then you either have to log the user out of the other device (more on that in a second), and log them in to the current device, or else you have to reject the login attempt and keep the person logged out of the new device. I think the former approach is more user friendly, but it depends on the needs of your application.
Logout
With JWT, the only way to guarantee a user is unable to use an issued token is to either include an expiration time (exp) in the payload, and use a verifier that checks that, or to know on the server which tokens are no longer valid and check against them. The most robust solutions do both.
So, assuming you're handling the expiration already the explicit logout feature would be handled by creating a revoked tokens list somewhere. If you're using MongoDB, for example, you'd create a collection to store those. You'd ideally also set a TTL on each one that is set to some point after the expiration date, so that Mongo will evict tokens that are otherwise expired anyway, to save yourself time and space.
If you're doing the auto-logout on a new login request, you'll hit this logic to place the old token into the revoked tokens list when you save the new token in the user's document.
The logout route should also be reachable by authenticated users, to explicitly logout if they want, regardless of whether you do the auto logout or not.
Authenticated Requests
By this point you should be reasonably certain that users can only login on one device. However, you also need to make sure that they aren't trying to make a request with a revoked token.
Your generalized route security middleware would then need to also check the revoked token list to see if the token offered by the client is on said list after checking to make sure it's not expired (since the expiry can be checked upon validation, saving a round-trip to the DB.
I totally agree with #Prateek Narendra.
once user is logged in store it's token in DB (i stored it in activeJWT field) then on further login check if activeJWT is null or not.
If yes then allow login else not.
At logout time u need to make activeJWT field null.
you can decode the current token for that.
Note: I am not an expert in auth.
I think you should save the last_login date time in DB.
Set JWT expire to 7 days. Renew JWT token for every request (for example user request /user this endpoint here sends new JWT token every time.)
On logout set last_login to null
When someone tries to log in
check the last_login field if it is null or last_login date-time is older then 7 day then procced otherwise
display a message to Logout first.