Token Storage

Securing SPAs that make API calls come with their own set of concerns. You'll need to ensure that tokens and other sensitive data are not vulnerable to cross-site scripting (XSS) and can't be read by malicious JavaScript.

To learn more, see JWT Handbook and The Ultimate Guide to Next.js Authentication with Auth0.

Next.js static site scenarios

When you're building a Next.js application, authentication might be needed in the following cases:

  1. When accessing a page

  2. When accessing an API route

  3. When your application calls an API hosted outside of your Next.js application on behalf of the user

Where a server is available, your app can handle the interaction with Auth0 and create a session, but in this model, we don't have a backend. All of the work happens on the frontend:

  1. The user is redirected to Auth0.

  2. When the user is successfully signed in, they will be redirected back to the application.

  3. The client-side will complete the code exchange with Auth0 and retrieve the user's id_token and access_token which will be stored in memory.

    Token Storage Best Practices In-Memory Storage diagram

Traditional web app scenarios

If your app is using a sign in scenario that doesn't require API calls, only an ID token is required. There is no need to store it. You can validate it and get the data from it that you required.

If your app needs to call APIs on behalf of the user, access tokens and (optionally) refresh tokens are needed. These can be stored server-side or in a session cookie. The cookie needs to be encrypted and have a maximum size of 4 KB. If the data to be stored is large, storing tokens in the session cookie is not a viable option.

Use the following flow types in these scenarios:

Native/Mobile app scenarios

Store tokens in a secure storage that the OS offers and limit access to that storage. For example, leverage KeyStore for Android and KeyChain for iOS.

Use the following flow types in these scenarios:

Single-page app scenarios

We recommend using the Auth0 SPA SDK to handle token storage, session management, and other details for you.

When the SPA calls only an API that is served from a domain that can share cookies with the domain of the SPA, no tokens are needed. OAuth adds additional attack vectors without providing any additional value and should be avoided in favor of a traditional cookie-based approach.

When the SPA calls multiple APIs that reside in a different domain, access, and optionally, refresh tokens are needed.

  • If the SPA backend can handle the API calls, handle tokens server-side using:

  • If the SPA backend cannot handle the API calls, the tokens should be stored in the SPA backend but the SPA needs to fetch the tokens from the backend to perform requests to the API. A protocol needs to be established between the backend and the SPA to allow the secure transfer of the token from the backend to the SPA.

  • If you have a SPA with no corresponding backend server, your SPA should request new tokens on login and store them in memory without any persistence. To make API calls, your SPA would then use the in-memory copy of the token.

In compliance with the OAuth2 specifications, when a browser requests a refresh token from the /token endpoint, Auth0 will only return a Refresh Token if Refresh Token Rotation is enabled for that client.

For more details, see Auth0 SPA SDK in GitHub.

Browser in-memory scenarios

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.

If you cannot use Web Workers, Auth0 recommends as an alternative that you use JavaScript closures to emulate private methods.

Use Auth0 SPA SDK whose default storage option is in-memory storage to leverage both Web Workers and JavaScript closures depending on the type of token.

The in-memory method for browser storage does not provide persistence across page refreshes and browser tabs.

Browser local storage scenarios

Using browser local storage can be a viable alternative to mechanisms that require retrieving the access token from an iframe and to cookie-based authentication across domains when these are not possible due to browser restrictions (for example, ITP2).

Storing tokens in browser local storage provides persistence across page refreshes and browser tabs, however if an attacker can achieve running JavaScript in the SPA using a cross-site scripting (XSS) attack, they can retrieve the tokens stored in local storage. A vulnerability leading to a successful XSS attack can be either in the SPA source code or in any third-party JavaScript code (such as bootstrap, jQuery, or Google Analytics) included in the SPA.

To reduce security risks if your SPA is using implicit (we recommend using authorization code flow with PKCE instead) or hybrid flows, you can reduce the absolute token expiration time. This reduces the impact of a reflected XSS attack (but not of a persistent one). To reduce the expiration time, go to Dashboard > APIs > Settings > Token Expiration For Browser Flows (Seconds).

Reduce the amount of third-party JavaScript code included from a source outside your domain to the minimum needed (such as links to jQuery, Bootstrap, Google Analytics etc.) Reducing third-party JS code reduces the possibility of an XSS vulnerability. Performing Subresource Integrity (SRI) checking in third-party scripts (where possible) to verify that the resources fetched are delivered without unexpected manipulation is also more secure.

Learn more