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
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.
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.
When logging in, a JWT access token is sent from the server and saved in AsyncStorage in RN.
Now I want the user to stay logged for 5 years, until they either:
log out
admin revokes their token
they are logged in on 3 devices, change their password on one of the devices, which should log them out from the other 2 devices until they login again on those devices
lose their phone, and login from another device to log out from all devices
It looks like I'd have to store JWT tokens in the DB (I know that's not the point of JWT tokens and defeats the purpose they are serving based on my reading) but I would need to know the user's tokens, on their different devices, to be able to revoke them.
One thing that confuses me is reading that the access tokens should be short lived, say 60 mins, and refresh tokens long lived, say 5 years in my case.
What I don't understand is why can't we just use the access tokens to have a 5 year life span (for each device), save them against the user in the DB so we can identify their tokens and revoke their tokens based on the aforementioned points? What would be the point of a refresh token, would it even be needed in this case?
Note: I also read that we can't revoke access tokens, but can only revoke refresh tokens, so I am really confused. Would I have to send both an access token and a refresh token to RN, and only use the refresh token for the Authorization Bearer header and save only the refresh token in the DB? Then what would be the point of the access token if it's not the one in the DB?
I think this should be something simple to implement, but my criteria are the 5 year login and being able to revoke tokens based on the points above.
What's a correct solution for this situation?
Access Tokens are short lived, which is by default 24 hours. But why? Why not 5 years?
Anyone with the access token is guaranteed access to whatever the user (to whom it was originally issued) can access. This means the server cannot differentiate between that user and anyone else who has the access token.
There is NO logging out. What I mean here is that you can have your front-end redirect to sign-in page to have him enter the credentials, but truly logging out does not happen in the server. Technically, the user can use the same access token to continue getting access (until it expires)
Access Tokens canNOT be revoked. Access tokens are invalidated only upon expiry. Anyone can use it until the token expires. For instance, if the expiry is set to 5 years and I happen to get your token by some chance, I can have all access that you have till it expires which in this case would be 5 years. This is exactly what gives more sense to set the expiry time lesser than 24 hours.
Now lets address your queries. "I want to user to be signed in until he"
Logs out
Send refresh token to user after he signs in. Store both access token and refresh token very securely. After his access token has expired, use the refresh token to get a fresh access token. Loop this until he logs out. When he logs out, delete the access token and refresh token on the front-end and revoke the refresh token on the server side. (Again, if he somehow gets the access token, he still can access his account till it expires)
Admin revokes token
The server can't revoke access tokens as I told before, once issued its valid until expiry, no matter what -> But only as long as the user has the access token :P Delete the access token as soon as he opens the app, if he did not last open the app in the past 1 hour or so. Now the front-end is forced to get a new access token using the refresh token it has stored. Now, you want the user to force logout? Revoke his refresh token.
Logout on all devices after password change
Same as 2. After he changes password, revoke all refresh tokens issued (In case you don't want the user to sign in again, revoke all refresh tokens except for the current device). Your app on all devices will be forced to get a new access token using the refresh token, but since you revoked it, the user has no way other than to log in using his credentials.
User-triggered logout from all devices
Same as 3. Changing the password triggers logout on all devices and here you just need to add a "Logout on all devices" button that will send a server request which revokes all refresh tokens except for the current device.
Caveat: Current user session cannot be closed; You need to wait for the user the exit the app, so as to have the current access token deleted. Workaround is to delete the access token as immediately as he closes the app (or even he minimizes the app) or setting the access token expiration to 30 minutes, provided you can tolerate the latency caused by obtaining new access token using the refresh token everytime he does that. You need to tradeoff time for security or vice-versa, depending on the your app specifications.
"That's all fine, but I don't want a refresh token in the first place" (Alternative Solution):
I don't encourage storing tokens as it defeats the very purpose of scaling and preventing easy DDoSes, by increasing the response time which increases due to querying the db. But since Redis is amazingly fast key-value store that runs on memory, some prefer storing access tokens in it. Well how does that work?
Setup: Once the user logs in, issue an access token. store it in Redis, then send it to user.
Check JWT signature && token's integrity, if it fails hurray, no db query. Send back 404 user not found. This will be as quick as how JWTs without Redis function.
If it succeeds, check Redis for the token. IF it exists, grant access. If it doesn't, ask the user to log-in again. Note that this will be a bit slower than granting access using just JWTs, but hey, you aren't storing in it Postgres or Mongo, which might take a few milliseconds to respond; Redis being a key-value store - and for it sits on memory (and not storage) - is considerable faster than those.
Access is granted if and only if both the conditions are satisfied: JWT is valid. JWT is present in Redis
Answering your queries:
Logouts are now possible. When user hits logout, delete from Redis the access token. He can't login even if he has the access token. Access Token is now literally invalid.
Admin forces logout: Delete access tokens for that user from Redis.
After the user is successfully granted access by the server, you shall allow the user to issue a request to delete all other tokens with the same user-id (or uid) which will allow logout
After password change, issue such a request.
On Logout from other devices, issue such a request.
Finally the left out 1. Stay logged in until user logs out: Now that you have authority to invalidate access token which you didn't have when not using Redis, you can have a 5-year valid access token, provided you implement other required security measures to prevent misuse of the access token.
I was going through Oauth2 docs and thought it was kind of permissive security wise, so i tried to implement JWT tokens with a special scheme like in the picture for a mobile app communicating with a web API.
Notes : i didnt like Oauth2 refresh tokens idea as they might get stolen and allow parallel usage (by legit and malicious users) unless you implement theft detection by rotating them (refreshing refresh token upon each request) in this case why use them at all ?
How the auth flow works :
A user logs in with credentials gets a jwt of 20 minutes lifetime.
Upon expiry the jwt gets refreshed by hitting the db checking if it's blacklisted (relogin) and if not check if it was used to generate a new token.
If it was never used to refresh it is accepted and used to issue a low grade access token.
If the token was used before, or had different client+device+user than its parent offer a credential check (password or lockscreen code)
If passed, this check issues a new first grade token that blacklists all its parents and children on the db, its like a new first user login.
If lockscreen fails the user is presented with login screen.
The questions are :
What are possible security holes ? (I found two use cases : stolen valid access token lasts 20 minutes same issue as Oauth tokens. No gain no loss here. And stolen sleeping token : user not logged in for say 7 days, token gets stolen and used until user logs in again or token chain revoqued after 3months of persistance - our policy - and this theft has small chances since token has to be intercepted at the last request the user makes on the app , slimmer than stealing an Oauth2 refresh token )
What are user experience problems an attaker can cause on the app while on this scheme ?
OAuth2 refresh tokens are not meant to be used by mobile clients. Using refresh tokens requires client credentials, which cannot be stored securely in a mobile application.
Refresh tokens are used from confidentials clients (server side web applications for example). They are often renewed when used (server sends back new access and new refresh token). In contrast to access tokens, the refresh token is only sent to the authorization server, not the resource (API) server.
Regarding your auth flow. Step 2 is the weak link IMO. You allow the client to use an expired token to generate a new access token. So if I find your phone and access the device, it will allow me to get a fresh access token and impersonate you.
You could force the client to refresh the token every say 15 min., but then you have to define what happens if the app gets closed or the device is turned off? Is it okay to re-authenticate the user again?
I know there are already many posts about Oauth, Oauth2, JWT, etc.. I have read many and I more confused than ever so I am looking for some clarification. I will propose my view on the subject and I hope somebody can tell me if my implementation is secure enough or what I am doing wrong and how to improve it.
I am building an API Rest server for serving my resources to my users. Let's suppose it is a bank app where users can deposit, withdraw and transfer money.
I am using nodejs, hapijs, jsonwebtokens, and bcrypt for my server. I want to implement two token authentication flow (Oauth2).
This is the way I am doing it:
User logs in to the auth server by giving some credentials (username and password).
The server verifies the user's credentials, if they are valid, it will grant access to the user and return a refresh token and an access token.
These tokens are saved into the local storage of the browser or mobile device.
The access token:
is signed as a jsonwebtoken.
contains issued date, expiration date (5 min), user data (id, username).
The refresh token:
is signed as a jsonwebtoken and encrypted with bcrypt.
contains a unique identifier
may contain an expiration date
is saved in the database.
As long as the access token is valid, that means, it has not expired and contains valid user data, the resource server serves the user the requested resources.
When the access token is no longer valid, the auth server requests the client to provide a refresh token in order to issue a new access token
The server receives the refresh token from the user, decrypts it, compares it to the one in the database, checks if it has been revoked, and checks its unique identifier.
If the refresh token passes all tests, the server issues a new access token to the client.
If the refresh token fails one test, the server requests the user to re-authenticate.
Notes: I am trying to avoid the usage of cookies.
Questions:
If the user is able to steal an access token, I guess it can also steal the refresh token. So, how can I make the refresh token more secure?
Is my perspective of the Oauth2 flow correct?
What can I improve?
Am I missing something?
The reason OAuth2 is so confusion to many people is because it uses different authentication flows depending on what kind of client is used.
OAuth2 distinguishes two client type, confidential or public. Next to that, there are 2 grant flows that are redirection based (auth code and implicit) which are meant to be used with a browser or browser control.
The other two flows (resource owner password and client credentials) are meant to be used from non-browser apps (CLI, background services, trusted mobile clients).
I've described the different flows and when to use them in more detail in this answer here.