2

I am trying to implement JWT in a secure way in Node.js and I am unsure when to request the refresh token.

I got one access token and one refresh token. Both are saved in two httpOnly cookies. When the access token expires the refresh token should check for a new one.

Right now I have implented it so that if the refresh token fails to validate the access token (if (err)) It requests the refresh token. The refresh token is then checked against the database and issues a new access token as well as validate the user for the route.

Is this a correct way of doing it?

I read somewhere that the refresh cookie should be set to a specific path instead for more security, but if so how do you call it when the access token expires?

1 Answer 1

5

A few thoughts:

  • Cookies aren't a great place to put refresh tokens at all, really. They're not the worst, but it's more common to use Local Storage and only ever transmit them when you're using them. Of course, they are script-accessible that way, but HttpOnly is actually very minimally useful as a protection and you need solid protection against XSS anyhow. When you want to use the refresh token, you POST it to the relevant URL.

  • Relatedly, I hope your cookies have Secure on them and are set to the right domain; and are ideally also SameSite; those attributes are vastly more important than HttpOnly. Not that HttpOnly is bad, it just doesn't do much. You can totally take over a session via XSS even if you can't read the cookies.

  • You say

    I have implented it so that if the refresh token fails to validate the access token

    I assume you mean if the server fails to validate the access token. Refresh tokens aren't code, or usually even structured data; they can't validate or even be used to validate anything else.

  • The method you've described is simple, in a way - the client neither knows nor cares about the refresh token and just occasionally a request takes a little longer than usual and comes back with a new Set-Cookie header or two - but it does require putting all the security-sensitive values into cookies and sending them with every request, and it means never pre-emptively refreshing the access token.

  • For clients that actively refresh (e.g. by POSTing the refresh token to the server's /refresh endpoint), there's two ways to know when to do it:

    1. Do it when a request comes back with access denied. This requires that the whole site's interaction with the server be script-mediated (done through XHR/Fetch) but that's common these days and you can put a common wrapper around that API which checks the error case for that specific problem and initiates a refresh.
    2. Do it on a timer, e.g. 90 seconds before JWT with a 10-minute lifetime expires, or perhaps cut it even closer if you don't expect peoples' computers' clocks to be off by very much. Requires knowing when the session token will expire, but that's easy if you can either read the JWT directly (it's a plain-text field in the JWT) or if you simply note whenever you successfully authenticate or refresh and just setInterval at that time if there isn't one already running, or setTimeout each time.
  • If you want to go the client-opaque route (via cookies or similar) but still want to do pre-emptive refresh, that's pretty easy too. Your server simply checks the expiry time in the JWT when validating it - that's part of the validation process anyhow - and if it's within some small window of its expiry but still valid, the server also tries to refresh the access token while serving the request. This can be done asynchronously, possibly on another thread, to avoid slowing down serving the actual request. (If the token has already expired, you need to confirm a successful refresh before starting to actually serve the request, which will slightly slow things down depending on how long a refresh takes.)

  • Sending both the access token and the refresh token on every request isn't ideal, but from a practical perspective the security model of a web app or web service is already predicated on both the connection and the server being secure. As such, it's not a meaningful vulnerability to transmit the refresh token more often than needed. Additionally, it still lets you get the primary benefit of JWTs (stateless authentication and authorization data that doesn't require either tons of DB hits or load-balancer-unfriendly caching) while addressing their biggest weakness (difficulty of revocation/cancelation meaning their lifetimes need to be short and some other, longer-lived token needs to be use for periodic refresh).

2
  • Wow, thats some massive information. Thank you for that, it gave some some insights! But I have some questions/comments to it: 1. I am a bit suprised by your first bulletpoint. I have been doing a lot of research and most people tend to reason that using cookies is more secure than local storage (where in-memory would be the most secure) 2. Yes, the cookies also have secure and SameSite. 3. Yes, it should say server, not refresh token. Regarding my question on when to send refresh token and your examples (access denied, timer or ”client-opaque”) which one would you choose for security? Commented Jan 5, 2021 at 16:54
  • 1. You can potentially achieve in-memory using session storage, or of course using a single-page app and holding the value in JS directly, but then you need to log in again every time you restart the browser. Between the other choices, I feel they're mostly a wash security-wise (CSRF-proof but slightly higher impact of XSS) and you get more functionality if you can inspect the tokens. 3. All those options are pretty equal, security-wise, and whether or not you do pre-emptive refresh you need to support refresh on demand by either server (the way you do it now) or client.
    – CBHacking
    Commented Jan 6, 2021 at 6:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .