Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guidelines to build a secure JWT authentication process?

Recently I needed to build a simple REST API and I read different articles on best practices to reduce as far as possible the vulnerabilities of my web app. Searching online I found different tutorials on how to implement JWT tokens, every one different in some aspects, and I couldn't find a well throttled "common approach". In the end, I achieved the solution that seemed most reasonable to me but I wanted to confirm that this is the most efficient way to handle this type of authentication.

Before starting:

  • Notice that CORS policy is implemented
  • To sign tokens I used "standard" libraries
  • Communication between clients and server is encrypted (HTTPS)

#STEP 1: Generating tokens after authentication:

  1. I generate a short-lived access token (15 mins) and a "longer-lived" refresh token (7 days) signed with the HS256 algorithm (key-length used: 512-bit)
  2. Furthermore I store the id of the refresh token in the cache.
  3. I send the access token back in the response (and on the client-side I keep it only in memory [Redux]) while I set the refresh token in an HTTPOnly cookie to make XSS attacks more difficult (not impossible, I know: the goal is to avoid/reduce the possibilities to steal the cookie)

#STEP 2: Authorizing requests

To authorize requests, I only use the access token that is sent in a field in the header of the requests. Requests are allowed only if the token is valid.

#STEP 3: Refreshing access token

To refresh the token I send a refresh request to the server. The server:

  1. Check if the refresh token signature is valid
  2. Check if the refresh token is in the cache (to check if expired and put another layer of defense against forged tokens)
  3. Generates both a new access token and a new refresh token, while the previous refresh token is revoked (and update cache accordingly)
  4. Send back the access token in the response and the refresh token in an HTTP only cookie

MORE INFORMATION on the refresh process

Refresh is called:

  1. Before the expiration of the access token
  2. At the exact moment the user opens/refresh the web app. In this way, if the refresh token is still valid, the user is automatically authenticated and ready to use the app. Notice that the refresh token can be used only to refresh the access token and not perform other requests (to avoid CSRF attacks the cookie can't be used to authenticate requests, but the user needs to load the website to get an access token)

FURTHER PRECAUTIONS: CRITICAL OPERATIONS

Refresh tokens have two expiration times. The first, for non-critical operations, is refreshed every time a new token is issued. In this way, if the user continues to use the app, he/she can stay logged potentially forever. The second (that lasts 3 hours), for critical operations, is "absolute". This means that, at every refresh, the "timer" for critical operations isn't refreshed.

//To make it simpler:
nextToken.criticalExpiration=previousToken.criticalExpiration

When the refresh request is called after the expiration of the "critical" timer, the access token generated has a field to false that symbolize the option to perform critical operations. If false, critical operations are not allowed (and so users have to re-authenticate to perform these requests)

Summary:

I'd like to understand if this process is executed correctly or if it generates vulnerabilities. I understand that vulnerabilities can be always possible in other parts of the app and/or in external libraries, however, I'd like to minimize the possibility of leaving something that can be exploited.

like image 600
AndreaCostanzo1 Avatar asked Jan 28 '21 22:01

AndreaCostanzo1


People also ask

How do I create a secure JWT?

There are two critical steps in using JWT securely in a web application: 1) send them over an encrypted channel, and 2) verify the signature immediately upon receiving it. The asymmetric nature of public key cryptography makes JWT signature verification possible.

How do I keep my JWT secure?

To keep them secure, you should always store JWTs inside an httpOnly cookie. This is a special kind of cookie that's only sent in HTTP requests to the server. It's never accessible (both for reading or writing) from JavaScript running in the browser.

What are the 3 parts of JWT?

Figure 1 shows that a JWT consists of three parts: a header, payload, and signature.


2 Answers

You have to create controllers, middlewares for login, generating the jwt token and also for the refresh token. See: https://stackabuse.com/authentication-and-authorization-with-jwts-in-express-js/ for more detail.

like image 66
Amir Achhodi Avatar answered Oct 16 '22 07:10

Amir Achhodi


I dont understand why you have 2 expiration times for refresh token. I think one is enough, just make sure you use refresh token rotation.

User login and get access token (AT1) and refresh token (RT1). If AT1 expires and the user use RT1, you need to invalidate the refresh token, then give new refresh token to the user, lets say RT2.

I save RT1 in redis (list of invalidated refresh token)

If someone steal RT1 and use it, the server can check and know that RT1 already in redis.

So you need to invalidates RT2 and all access tokens that belongs to RT 1 and RT2.

By doing this, the user need to login again.

See: https://auth0.com/docs/tokens/refresh-tokens/refresh-token-rotation

Refresh is called:

  1. Before the expiration of the access token

I agree, or maybe you can use axios interceptor, when the response 401 (unauthorized) you can send refresh token.

I agree with

  • store access token in memory (redux, vuex), or maybe webworkers
  • store refresh token in httpOnly cookie to prevent JavaScript from reading it.
  • use secure cookie, https

Auth0 recommends storing tokens in browser memory as the most secure option. Using Web Workers to handle the transmission and storage of tokens is the best way to protect the tokens, as Web Workers run in a separate global scope than the rest of the application. Use Auth0 SPA SDK whose default storage option is in-memory storage leveraging Web Workers.

From here: https://auth0.com/docs/security/data-security/token-storage#browser-in-memory-scenarios

Note that any values stored in memory are still vulnerable to XSS attacks, it just more hard for someone to get the token.
See https://community.auth0.com/t/why-is-storing-tokens-in-memory-recommended/17742/4

Another good read: https://indepth.dev/posts/1382/localstorage-vs-cookies

like image 1
Samuel Kristianto Avatar answered Oct 16 '22 07:10

Samuel Kristianto