PingOne Platform APIs

Token storage

To ensure the security of your application and its resilience to malicious attacks, it is important to understand the best practices for storing JSON Web Tokens (JWTs). This will vary by client and application type, and the specific threat model and risks pertinent to your organization.

General recommendations

For more detailed information on token storage best practices, refer to the following:

JWTs should never be emailed, copied into documents, logged, included in code repositories, etc. JWTs are highly confidential and should only be shared between the client, the authorization server, and the resource server, only as required by the underlying authentication protocols.

This section is intended as general advice, and may not apply depending on the configuration and security requirements of your applications.

Single-page application

For a single-page application, the most secure way to store tokens is to use web workers. This method stores tokens in a separate thread from the main runtime, which isolates tokens from any malicious code.

Where the client uses implicit flows to acquire access tokens, tokens should be kept within the client’s runtime (e.g. JavaScript) memory. However, using implicit flows is not recommended as of OAuth 2.1.

In rare cases where the client needs to store the JWT, using sessionStorage is generally preferable to localStorage, which persists across browser restarts. When transmitting tokens, only do so in the Authorization header or in POST request bodies over HTTPS. Never include tokens in query parameters.

It is not recommended to store JWTs in localStorage or sessionStorage unless absolutely necessary. Doing so opens up the possibility of any client side JavaScript running in the browser being able to access your tokens, and subsequently, an attacker could exploit user information.

Web application

For a web application, where a backend server acquires access tokens on behalf of the client, tokens should be kept in the backend and only associated indirectly with the client through its authentication session. Tokens can also be associated with the client’s authentication session with a session cookie.

The tokens themselves should not be included in the session cookie unless the client requires the ability to make direct API requests to the resource server protected by the tokens, or the server is unable to maintain a session on the backend. In the former situation, the the tokens would be returned to the client in the response body instead of by cookies.

In many cases, storing JWTs in a session cookie is the recommended approach. Depending on the security requirements of your application, you may want to consider other approaches.

You can customize the behavior of cookies using the following flags:

  • The Domain flag defines the domain where the cookie is sent. If left unspecified, the cookie is only sent to the origin server by default, which is the most secure option.

  • The Path flag defines the directory and subdirectories where the cookie is sent. Set this flag as specific as possible to the path that uses the session ID.

  • The HttpOnly flag ensures that it’s only sent in HTTP requests to the server, thus it’s never revealed to JavaScript running in the browser.

  • The Secure flag ensures the cookie can only be used with secure, encrypted connections.

  • The SameSite flag ensures that cookies cannot be sent cross-site, which prevents cross-site request forgery (CSRF) attacks.

  • The MaxAge and Expires flags set the expiration time of the cookie. If left unspecified, the cookie will expire when the browser is closed, which is the most secure option.

The following is an example of setting a cookie in the HTTP response header:

Set-Cookie: <cookie-name>=<cookie-value
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict