How to handle JWT refreshing on server side (Next.js or any other) - authentication

I have two cookies being stored: JWT and refresh token, both are httponly; path=/, so they are send on all requests, including the API (which doesn't use Bearer, but instead reads the JWT directly from the cookies).
The implementation is as usual, the JWT is short lived, and the refresh token is used to get a new one. The refresh token is rotating and after used is invalidated.
On the client, refreshing the token is no issue. When a 401 is returned by the API a call is made to /auth/refresh-token and the request is retried.
On the server however, (e.g. on getServerSideProps) it seems to be quite difficult to refresh the JWT. What I have attempted is to create a custom server and a middleware that checks when a JWT is expired and attempts to refresh it.
I can think of two issues with that, first is that the custom server is called on every resource, that includes all json, js, static files, etc... that Next.js serves. When two requests are made with the same tokens (I can handle this when making API calls, but Next.js also sends requests to the server and I cannot control those):
1. Two requests with expired JWT are sent to the server
2. The back-end receives the requests and on both determines it needs to refresh the token
3. Eventually one of the requests will complete, invalidating the refresh-token
4. The other request now has an invalidated refresh token and cannot get a new JWT
Second issue, what if the user doesn't receive the response, scenario:
1. A request with an expired JWT is sent
2. The back-end refreshes it and sets the new cookies
3. The back-end then has to read lots of data from a database which takes a few seconds
4. User closes the page before receiving the response
5. At this point the user has an invalidated refresh token and an expired JWT because the response with the new cookies was never received
How are these cases usually handled? It seems like it would be a common issue with rotating refresh tokens, but I couldn't find anything useful on this online.

You can follow this practice.
Save refresh token in the http-only cookie
No need to save JWT in the cookie for better security and keep it in the response of refresh token as well as login endpoint.
Save JWT expiry in a normal cookie
Call refresh token endpoint to get new JWT when expiry token is not present or getting 401 error.
In getServerSideProps also you can call the refresh token endpoint always as you don't need to persist JWT anywhere.
You may need to get the cookie from req.headers.cookie and pass it in the header when you are calling the refresh token endpoint from the server.
//inside getServerSideProps
const browserCookie = req.headers.cookie;
const refreshJWTTokenResponse: any = await refreshJWTToken(browserCookie);
//service call
export async function refreshJWTToken(refreshTokenCookie: any): Promise<{}> {
let headers = new Headers();
headers.append("Cookie",`${refreshTokenCookie}`);
const options = {
method: 'POST',
headers: headers,
credentials:'include',
};
...
}

Related

Secure way to store authorization data inside a PWA

I am trying to design a secure API, which then will be used by a PWA on desktop and mobile.
I have the following flow:
A POST request with the user's credentials is issued to endpoint /session at login.
If the the credentials are correct it will set a Secure, HttpOnly cookie which contains a refresh token with Path=/session/renew. This way it's only sent to that endpoint, cannot be accessed from JavaScript, and will only be set on HTTPS.
The response will also contain a JSON object with an access token. This will be used for every other authorized request. It will be passed along in a Authorization: Bearer ${access_token} header. The token's max age is 30 minutes.
Before the access token expires I can call to /session/renew with the cookie set and this will give me new access token and will also set the new refresh token in the cookie with the same properties as above. The old tokens will be revoked on the server.
The access token is stored in a Redux store. Unfortunately, this poses a problem.
Now if open a new tab, I can request a new token on /session/renew, but with this setup I will "stole" the previous tab's ability to request new tokens as those will be revoked by this call and the user will just get logged-out on that tab.
I see a few solutions, but none seems to be secure enough to me:
Don't revoke the old access and renew tokens when /session/renew is called
Store the access token in session or local storage
I did quite some research and still don't see what would be the correct (and not too complicated) solution here.

Proper way to do jwt refresh tokens with express

I'm thinking of a proper pattern for implementing refresh tokens, but on few steps, I have some questions. I am using nextjs with axios on the frontend and express with cookie-session on the backend.
Here are steps I'm thinking of:
Client sends a log-in request. After logging in I put the access token in the session object and the refresh token into res.cookie, both secure and httpOnly.
// for simplicity options are left out
req.session = { accessToken };
res.cookie("refreshToken", refreshToken)
On every request with axios I supply just the access token. Here's where the first question arises. How would I supply only the access token, without sending the refresh token too, if both are httpOnly?
If the access token is valid do whatever is needed. If not, need to get the refresh token and compare it to a stored one, and so on. Here's the second question. While I understand I need to retrieve the refresh token in this step, how would I do it properly?
Any more advice would be great, thanks.
[EDIT] After some thought I realised that making access token not httpOnly would allow me to send just access token, which would solve my problem. But if that is incorrect please let me know.
Here is the standard pattern - as in this code of mine:
Client sends access token (which may be in an HTTP Only cookie)
When it expires they try to refresh the access token by calling a /refresh endpoint
If the refresh succeeds they retry the API request with the new access token
Otherwise the client redirects the user to sign in again
Don't make the access token non HTTP Only, since any malicious code could then grab it from document.cookie.
This also allows you to set a path of /refresh for the RT cookie, so that it is only sent on refresh requests.
Also ensure that cookies containing tokens are strongly encrypted (AES256) using a symmetric key only known server side. This Express library will do the work for you.

check expire date of auth token saved to redux before every fetch() in react-native

In my react-native app, I have a jwtsaved to localStorage by redux-persist. I need to check the expire date of token before every fetch request to API. But I don't want to implement this process of date comparing within every page, because there are dozens of pages where I make a request to API. Maybe I can write custom method that wrappes fetch and check the expire date inside or before every fetch to dispatch action that check whether token expired? I'm stuck here. Do you have any idea how can I solve this problem?
This is an overview of how you can solve your problem.
Step by Step
/login
Successful login
Server returns { token, refreshToken }
Now for making any request ( protected i.e only logged in user needs to be shown or access a particular resource on server)
send token with each protected request in Authorization header
server validates the token
in case of expired token server returns expired token error
now client receives the expired token error
next client dispatches an action to get new token
new action should send an api call to server to get new token passing refreshToken
after validation of refreshToken server returns new accessToken and refreshToken, in case passed refreshToken was invalid return an error and client should logOut the user
If new accessToken is received update the app state with new accessToken and refreshToken
This is just an overview but you can add more security measures to it such as only few times token can be requested via refreshToken and after that user must login again.

Using Firebase for server side authentication

I have Firebase authentication set up on the client side, and on the server side I can take a JWT token and decode the account ID. But how does this work when I want each page load to be authenticated on the server before the page is generated?
On login or each page load, can I simply store the last JWT token into cookies so that I can read them on my server? Aside from that, I don't know how else for my server to get that information aside from each link being an AJAX call.
This seems to be the only way to do it presuming you're using the firebase auth library to decode and verify the tokens on the server and not some kind of JWT parsing library.
Firebase admin includes methods for decoding and verifying id tokens and extrapolating all the information. This prevents cookie forgery and keeps firebase auth as your single source of truth for whether or not a user is authenticated.
for example (in node):
const admin = require("firebase-admin");
admin.initalizeApp({
credential: admin.credential.cert(yourServiceAccountJsonHere),
databaseURL: "http://YOURDATABASE.firebaseio.com/"
});
admin.verifyIdToken("usertokenasastring") //returns a promise
.then(function(decodedToken){
//process the decoded user token somehow
});
Firebase makes a call to your database, and makes sure that that user is logged in and that all the other information in the JWT makes sense, so you don't have to worry about implementing any kind of verification server side.

Web API 2, OWIN Authentication, SignOut doesn't logout

I'm doing some research for work with a view to using Bearer tokens as an authentication mechanism (i.e. AngularJS UI, authenticates via OWIN in a Web API [2] project).
I have the login working fine, role information and all that is fine, but I cannot get the token to logout.
My startup configuration is this:
OAuthOptions = new OAuthAuthorizationServerOptions() {
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AccessTokenExpireTimeSpan = SESSION_TIMEOUT,
AllowInsecureHttp = true
};
And my logout action is simply this:
public HttpResponseMessage Logout() {
var authentication = HttpContext.Current.GetOwinContext().Authentication;
authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
return new HttpResponseMessage(HttpStatusCode.OK);
}
I've left all the authentication stuff out for brevity, but to confirm I am using ExternalBearer when setting up the token.
In my UI I'm storing the token in local storage (no cookies are involved here, which is a deliberate design decision). So I have a logout button on my UI, the Logout action is hit and the code runs fine.
However if I subsequently hit the an action on the API which requires authorisation, the request still goes through (i.e. the user is still authenticated even though they should have been signed out.
Either I'm missing something really obvious (wouldn't be the first time ;-) or there's something more fundamental going on here - finally I'm pinging #leastprivilege as I know this is their area.
Any help or insight would be gratefully received.
Only thing I can think of is that the token is stateless on the server/API side and hence can't be expired or signed out.
If that is the case I guess I could either:
a) Add a refresh token which creates a new token that expires in the past - would this even work? - actually cancel that, it would issue a new token ... the old one would still be valid
b) Store the bearer token in the database and check each time, removing the token on logout (naturally salted, hashed, etc). However this is just bringing us back to having a stateful server.
c) I can (and will) be removing the token from local storage when someone explicitly logs out, however the token is still technically valid if a baddy can intercept the token. Naturally all the above will be over SSL anyway, which should inhibit the bad guys/girls.
d) Perhaps this is why lots of people are storing the Bearer token in a cookie (as a storage mechanism) so once you logout as least the cookie will be removed on the next refresh.
Sorry the above is a bit of a brain dump, just wanting to pre-empt any questions
Since OAuth is not an authentication protocol, there is no notion of signout. Delete the access token on the client - that's all you can do.
If you want to invalidate the token on the server side, add a unique id to it and keep track in your service - you would need to manually build something like that.
I have a beautiful solution here: http://www.nakov.com/blog/2014/12/22/webapi-owin-identity-custom-login-service/. It is custom user session implementation for Web API OAuth bearer token authorization based on OWIN and the standard ASP.NET Identity (Microsoft.AspNet.Identity.EntityFramework). It works as most people may expect:
Web API sessions die after 30 minutes of inactivity.
Session’s life is extended at each authorized HTTP request with additional 30 minutes.
Logout works correctly: after logout the bearer access_token becomes invalid (its is revoked).
Full working source code is available at GitHub: https://github.com/SoftUni/SPA-with-AngularJS/tree/master/Ads-REST-Services
This question has been here for ages (and answered too), but I only wanted to chime in my thoughts.
I would do similar to your (C) option, but use a shorter expiry on the bearer access token something like 10 or 20 minutes, so that when you have logged out and deleted the token on the client, although technically the token is still valid, the bad man will have only the remainder of the expiry time to play with your valid token.
In practice, I would use this together with a long-lived refresh token, so that I can get a new bearer token if it expires and want to continue interacting with the API resources, without having to authenticate again.
As long as I know the bearer token lives in the client side so I don't think that you need a server side "logout" function. Just remove the token from the client local storage should log you out.