Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWT access token in-memory?

I’ve been spending hours and hours on this, this is the first time I am using JWT and would really need some of your thougts.

Right now I store my tokens in separate httpOnly cookies (my access token expires after 15 min and refresh token after 7 days).

I have read that the most secure way to store the tokens is actually using a cookie for the refresh token and in-memory (like in a variable) for the access token.

While I understand this is secure, I do not really understand how it would work in practice. Would it mean that we have to create a new access token with our refresh token on each request? Or is there a way we can make it valid and copied to new variables until it is expired?

I am using react and node btw.

like image 778
Hejhejhej123 Avatar asked Mar 01 '23 19:03

Hejhejhej123


1 Answers

I spent days reading about this too. From what I gathered a solution would be something like this:

  1. User logs in with login and password.

  2. Server generates a refresh token long lived to be stored as an HttpOnly Cookie, preventing XSS attacks as it can not be accessed by Javascript.

    • Ideally some sort of blacklist can be used server-side to prevent re-use of refresh tokens that have not reached their expiry but have been replaced.
  3. Generate an access token which can either be stored in localStorage or in-memory (in a variable). The access token has a short expiry life of a few minutes.

    • If stored in localStorage, the token will not disappear on a reload of the page/browser (F5). It will also be visible in the console/storage.
    • When using localStorage to check if user is authenticated, the code will try to read the token from localStorage, jwt_decode it and set a user variable with the data that is in the token.
    • As tokens are not encrypted, just base64, their values can be changed in the dev console. A page that is "role: admin" only will be rendered if the permission is changed. The API will be responsible to check for permissions and reject the request if the token has been tampered.
    • Afaik, if it is stored in a variable it is a little less visible, it also gets wiped when reloading the page/browser.
    • When using a variable, to avoid refreshing the access token on every request, we can use the Context API, by creating a Component with the authenticated user context that will wrap the App/Router and then on every page that needs to be protected import and use this context and redirect if needed.
  4. When the access token is not valid anymore, because it has reached its expiry, or because it has been wiped, the API call will get rejected. Intercept this call then call the API refresh route to use the refresh token to generate a new access token.

    • I use axios with axios interceptor to intercept the failed request, call the refresh route, set the renewed access token, then retry the failed request. (needs to be a GET request to avoid CSRF errors apparently).
    • In addition (not in place of), a setTimeout can be used to automatically refresh the access token every X minutes to prevent letting it expire.
  5. To log out, remove the cookie (eventually blacklist) and wipe the context / localStorage.

Using axios, axios.defaults.withCredentials = true; makes sure that the cookie is sent with the requests and { headers: { 'Authorization': `Bearer ${access_token}` } } makes sure the access "bearer" token is sent with the request. These can either be set as defaults for every request or per request hence these 2 syntaxes.

Github example with Flask and React

like image 163
dorsk Avatar answered Mar 08 '23 23:03

dorsk