I have a react app with a node/express back end. I am currently using JWT to make calls to protected routes on the back end. This all works fine. But I want to keep the user logged in for more than just 30 min or so.
What is the best way to handle securing sessions or refreshing an access token when it expires on a client side application?
My Solutions:
-One:
Create a refresh and an access token. Have the access token be short lived. And sign the refresh token with a unique id given to the user in the database. Then check verify the token with this id. Then when the access token expires send a 401 back and then get the refresh token from local storage to create new tokens and then try the call again.
Problem with this: there is a lot of back and forth going on and it seems slow.
-Two
Send both the refresh token and the access token on each call and if the access token is expired use the refresh token to create new tokens then proceed.
Problem with this: I don't see why I would then need to send 2 tokens I could just send one and get the job done with that. But then if one or both tokens become compromised they could regenerate forever.
-Three
Send the access token expiration to the front end and if the token is expired send the refresh token and verify it with the unique identifier in the database. Then generate new tokens and continue.
Problem with this: Not sure how secure this would be. But for now this seems like the best solution.
So I am wondering what is the best way to handle refreshing an access token when it expires so the user can keep using the platform?
I'm saying this from experience not a source but the whole idea of JWT s that it's sessionless. there's another solution for handling sessions.
With jwt, you don't need to save it in your database as you sign that JWT with a secret key and you can just verify that JWT has been issued using your secret key. and you can issue that JWT to be valid for more than 30 minutes. It's totally fine. I myself have set it to 180 days. and because it's so long, a user will log in again in this time so you don't have to worry about expiration. but if you want to handle that too, you can parse it in your front end and check it's expiration timestamp and get another JWT before it expires.
Related
I've been doing a lot of reading on this subject and I can see that there are many different opinions and approaches to authenticating using JWT.
My understanding is as follows:
In its simplest form, a JWT authentication mechanism should:
Verify username and password.
Create a signed JWT access token containing information (depending on the app's needs) on the user.
Send that token in the response.
The client then stores the token (which from my understanding there is some debate whether a secure cookie or localStorage is more secure), and sends it with each request's headers.
The server can then authorize the user using middleware verifying the JWT. No state, all information in contained within the JWT.
Assuming the JWT has no expiration (or perhaps a very long expiration date, maybe a couple of months), it sounds good because I can provide the user a persistent logged in state for a long time. The concern is, to my understanding, if the JWT was to be stolen, it is essentially an unlimited access card and a huge security breach.
So that's where the refresh token enters, the server issues both refresh and access tokens (refresh token with a long/unlimited expiration and the access token short).
The server database holds some kind of table of valid refresh tokens (so that if one is stolen it can be invalidated easily) and when issuing a new access token, validates the refresh token.
This also adds the need to add some sort of countdown mechanism on the front end where a refresh request is to be sent to the server prior to the access token expiration date so that the user won't be logged out.
And my question:
Why? If we go through all the trouble of creating a db table for refresh tokens, why not just make a table of valid access tokens and invalidate them if needed? How is that less secure than using refresh tokens?
Thank you
Access tokens aren't primarily used to provide extra security, but to provide efficiency and decoupling.
An access token can have a very short lifetime - maybe even less than a minute - but be used to authenticate multiple requests to different services within that time. Those services don't need to have any access to the authentication database, because they can trust the access token until its expiry date; that makes them faster and simpler.
For instance, if you're using a dynamic page with lots of AJAX requests, that might run in very quick succession. Those AJAX calls might be implemented as serverless functions (e.g. AWS Lambda), or as standalone scripts in different programming languages on different servers, or you might just want to make them as efficient as possible, and avoid any database access. The only information that needs to be shared between them is a public key to verify the signature on the JWTs they receive.
From a security a point of view, this is a trade-off: on the one hand, an access token for a user whose access has been revoked can still be used until it expires; on the other hand, the long-lived refresh token is transmitted much less than a traditional session token, so there are fewer chances for it to be intercepted.
To address your concrete concern:
This also adds the need to add some sort of countdown mechanism on the front end where a refresh request is to be sent to the server prior to the access token expiration date so that the user won't be logged out.
No "countdown" is needed. The code that has access to both tokens simply looks at its current access token before using it; if it has expired, or is about to expire, it requests a new one using the refresh token. It then gets a new access token, and probably a renewed refresh token - the expiry date on the refresh token represents how long the user can be idle before they are automatically logged out.
We don't need to make a table of access tokens and it is dangerous to secure.
We have to save only refresh token and add one field for valid/invalid in the table. And send access token and refresh token to the client side.
The clients send access token with each request's headers.
The server can authorize the user using middleware verifying the JWT.
After some time, the access token will be expired(access token's expired time is shorter than the refresh token's expired time).
The client sends refresh token to server.
Then the client will get new access token using refresh token(refresh token should be recreated, in other words, we can use only one-time refresh token, we have to update table of refresh token with new refresh token).
The client can get new access token and refresh token.
I hope it will be help you.
here is my auth's flow:
The user receives two tokens (access token with expiration time and refresh token without expiration time) after logging in
for each user , The refresh token is stored in the database in a json column called refreshTokens(which is an array).
on the client side, Both access token and refresh token are stored on the local storage.
when user needs to be verified, If the access token is expired, a new access token is created using the refresh token and sent back to the user and keeps the user logged in.
When the user logs out, the refresh token stored in the database (in the refreshTokens list) is removed.
my questions are:
is this flow, secure?
do i need to save refresh token on the cookie or is local storage good enough?
That flow is according to how OAuth works and how tokens can be stored in a secure way, so "yes" to both questions. The part that is missing is how the tokens are obtained in the first place: for that the Authorization Code grant type using PCKE is the preferred way, over the legacy Implicit grant type.
An important part of this flow being secured is that in point 4 you use the list of refresh tokens kept in the database to verify that the RT was not revoked. Other than that it looks ok. You can add more security by adding expiration times to refresh tokens. Then, even if the user doesn't actively log out (you don't clear RTs from the DB), the RT will not be usable after some time.
Keeping the tokens in local storage is good enough in my opinion.
Few stuffs that popped up in my mind while reading this :
Refresh Token also needs expiration time. In fact, I consider refresh tokens as a double edge sword. Imagine a scenario where a person gets access to the refresh token, not only he gets access to the resources, he will practically gain more time with the resources. I prefer to re-generate refresh token along with access token when refresh token is being used to re-generate the access token. And, also set an expiry to it.
It is cool to store the refresh token in database. However. I do have an alternative though, you can easily use RSA algorithm and then do token generation using private key and verify using public key. This is yo mitigate the scenario of needing to store the refresh tokens.
On client side, local storage is a BIG NO from my side. What I prefer is that you use HttpOnly Cookies and set flag as true. HttpOnly Cookies are not rendered in JS and is sent to server securely as per Http protocol. This is fix the chances of compromising tokens.
Your rest concept of it is good enough.
I am trying implementing JWT Tokens(Access tokens and Refresh tokens), but I come to an issue on requesting a protected resource with an expired access token, while the refresh token is still valid.
I know that I should not use refresh tokens to request resources, refresh tokens should be used against authorization validators to revalidate/regenerate access tokens.
In my app, the User can log in by POST request with a valid credential to get Access token(exp. in 1min) and Refresh token(exp. in 10min.). Say now User making a request 30 sec later of login and sends both tokens, then tokens get checked and resource comes back. If now user makes a request after 2min and sends tokens, his access token is Invalid, in this scenario how can I proceed with the request and revalidate tokens.
I can think of middleware to validate and provide tokens and send that with the response, but is this the right approach?
Then I need to handle and restore tokens on the client-side for every response. Don't I?
Also, I do not want to prompt users to re-login. I am using Node and Express for Server and React on Client.
Here are your steps:
Try to login
Receive 401 from server when token is invalid
Request a new access token by making a new refresh request.
Set the new access token and refresh token
Retry original request
This has to be done on the client side because it is the audience that gets validated for authorization.
Usually we don't set the access token to expire every minute because the described process would add too much latency to the process.
Edit from #MComment:
5 min for access tokens and 30 min up to a few hours is what is generally recommended for respectively access and refresh tokens. Usually Authorization Servers offer "rolling refresh" - refresh token's expiration is renewed whenever you use it. This way a user stays logged in as long as they are actively using the website
You can update expired date of access token in every request, no need to regenerate token.
I think session time you set is not normal and recommended.
If you dont want user must re-login, make a forever refresh token, create a function in reactjs for re-generate access token by refresh token if it expired.
Revoke refresh token only when u want to logout from this client.
I am implementing JWT inside a client mobile app with a separate back-end server, and I am looking for an optimum way to use refresh tokens without too much server calls and while keeping a good user experience.
I am new to implementing such a mechanism, and I am confused about many things, so what I am really looking for is a solid conceptual solution to secure the user access to the app, and keep him at the same time logged in indefinitely.
Any correction or suggestion would be most welcome:
Why refresh tokens anyway?
Using a JWT access token alone might compromise the user security: If an attacker holds the access token, he might run feature requests whenever he wants. Even when applying an expiry date to the access token, if the server issues a new access token whenever the old one expires, the attacker will receive this new access token using his old one, and keep accessing the user features.
Refresh tokens stop the attacker once the user regains access to his account using his login/password: When the user uses the app and the server detects that his refresh token is invalid, he will be logged out and a new refresh token and access token are issued after he's logged in with his credentials. The attacker won't be able then to use the old tokens.
My first question would be:
I. Regardless of how the attacker gets hold of the tokens from the user environment, would he be able to use them indefinitely as long as the user is still inactive and isn't logged in again with his credentials to create new tokens?
What about when the tokens are refreshed asynchronously?
Let's imagine a scenario where the user is inside the app, and at least two server calls are run asynchronously:
"Service1" makes a server call with an expired accessToken1 and a refreshToken1, and the server responds by sending a new accessToken2 and refreshToken2
Before receiving the "Service1" response, "Service2" makes an other server call with accessToken1 and refreshToken1, the server compares refreshToken1 to the previously saved refreshToken2 and finds them different. It responds then with an Invalid refresh token response, and this causes the user to be logged out!
To avoid this problem and keep the user logged in, there could be a centralized authentication service that checks first the validity of the tokens before any server call is made. Which means that any call won't be executed unless the authentication service is idle, or wait for the new tokens if it's already loading.
My second question here is:
II. Having such a service to avoid the asynchronous refresh token problem means more round trips to the server, which might prove costly. Is there a better solution?
There are some steps to login / revoke access to an api:
When you do log in, send 2 tokens (Access token, Refresh token) in response to the client.
The access token will have less expiry time and Refresh will have long expiry time.
The client (Front end) will store refresh token in his local storage and access token in cookies.
The client will use an access token for calling APIs. But when it expires, pick the refresh token from local storage and call auth server API to get the new token.
Your auth server will have an API exposed which will accept refresh token and checks for its validity and return a new access token.
Once the refresh token is expired, the User will be logged out.
JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with.
What about when the tokens are refreshed asynchronously?
that supposed be done with a single request to an endpoint, so there is a single accessToken
Having such a service to avoid the asynchronous refresh token problem means more round trips to the server, which might prove costly. Is there a better solution?
i think that's the best & secure solution for mobile and serverless apps, token are like ssh keys must be kept secure all the time :)
for more information check [question]: JWT refresh token flow
Here's the official introduction to JWT
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.