A refresh token in an OAuth setup using Json Web Tokens (JWT) is used to request a new access token. Because access tokens are short lived (to minimize the impact of a token being exfiltrated), a long lived refresh token is commonly used to, for example, stay logged into an app rather than need to log in every n minutes (the length of the access token).
Why have two tokens? In web apps this provides an additional layer of security and a better access story for your auth service (for high traffic app). Access tokens have a short life and the signed token is verified on every request (CPU bound), a refresh token affords a chance for the auth service to cut off access (using a deny list in a database i.e. IO bound) and is also stored separately (HttpOnly
cookie) to prevent cross-site-scripting (XSS) attacks. This also improves scalability, authentication happens less frequently (at login time and token refresh time) which can reduce load on an auth service.
The refresh token should be treated differently because it has a longer expiry. It should be stored in a more secure way to prevent leaking it. In the browser that means storing it in an HttpOnly
, Secure
, and SameSite
(to prevent CSRF attacks) cookie in the browser which can not be accessed from JS and refresh token rotation. This makes it possible to “refresh” an access token by making a request and including the cookie (using fetch
with credentials: 'include'
).
See also:
- Storing JWT in cookies creates a cross site request forgery (CSRF) vulnerability.
- Block old browsers if you use SameSite cookies to prevent CSRF.
- Read An in-depth look at refresh tokens in the browser.
Links to this note
-
Refresh Token Rotation Detects Token Theft
When using refresh tokens to enable clients to get new access tokens one danger is that the longer-lived refresh token can be stolen and used to grant access to your application by an attacker. This is especially tricky in the browser where CSRF and XSS are commonplace.
-
In React,
useEffect
let’s you perform side effects in function components. They are meant to run once when the component is mounted, however you can recursively call it by combining adding auseState
data dependency to it. By toggling the value of the data dependency inside the effect, it recursively call the effect. This is useful, for example, in implementing retry logic to an API call say for automatically retrying when an access token expires. -
Block Old Browsers If You Use Samesite Cookies to Prevent CSRF
One way to mitigate CSRF attacks is to only allow cookies to be forwarded along with a request if they are from the same site,
SameSite=Lax
orSameSite=Strict
. However, not all browsers support this setting yet.