Token Best Practices

Here are some basic considerations to keep in mind when using tokens:

  • Keep it secret. Keep it safe: The signing key should be treated like any other credential and revealed only to services that need it.

  • Do not add sensitive data to the payload: Tokens are signed to protect against manipulation and are easily decoded. Add the bare minimum number of claims to the payload for best performance and security.

  • Give tokens an expiration: Technically, once a token is signed, it is valid forever—unless the signing key is changed or expiration explicitly set. This could pose potential issues so have a strategy for expiring and/or revoking tokens.

  • Embrace HTTPS: Do not send tokens over non-HTTPS connections as those requests can be intercepted and tokens compromised.

  • Consider all of your authorization use cases: Adding a secondary token verification system that ensures tokens were generated from your server may be necessary to meet your requirements.

  • Store and reuse: Reduce unnecessary roundtrips that extend your application's attack surface, and optimize plan token limits (where applicable) by storing access tokens obtained from the authorization server. Rather than requesting a new token, use the stored token during future calls until it expires. How you store tokens will depend on the characteristics of your application: typical solutions include databases (for apps that need to perform API calls regardless of the presence of a session) and HTTP sessions (for apps that have an activity window limited to an interactive session). For an example of server-side storage and token reuse, see Token Storage.

Tokens vs. Cookies

Web apps are typically single-page apps (such as Angular, Ember, and Backbone) or native mobile apps (such as iOS, and Android). Web apps consume APIs (written in Node, Ruby, ASP.NET, or a mix of those) and benefit from token-based authentication. Web APIs are traditional server-side applications that use cookie-based authentication.

Token-based authentication is implemented by generating a token when the user authenticates and then setting that token in the Authorization header of each subsequent request to your API. You want that token to be something standard, like JSON web tokens since you will find libraries in most of the platforms and you don't want to do your own crypto.

With both approaches you can get the same amount of information from the user. That's controlled by the scope parameter sent in the login request (either using the Lock, our JavaScript library or a plain link). The scope is a parameter of the .signin({scope: 'openid name email'}) method which ends up being part of the query string in the login request.

By default, we use scope=openid in token-based authentication to avoid having a huge token. You can control any standard OpenID Connect (OIDC) claims that you want to get in the token by adding them as scope values. For example, scope=openid name email family_name address phone_number. To learn more, see Standard Claims on openid.net.

You can mix token-based authentication with cookie-based authentication. Take into account that cookies will work just fine if the web app and the API are served from the same domain, so you might not need token based authentication. If you need to, we also return a JWT on the web app flow. Each of our SDKs will do it differently. If you want to call your APIs from JavaScript (instead of using the existing cookie), then you have to set the ID token in your webpage. One way of doing it is by setting it on your layout/master page such as window.token = ${"<%= id_token %>;"}, and then getting it from anywhere in your JavaScript code.

Refresh token usage

You can only get a Refresh Token if you are implementing the following flows:

If you limit offline access to your API, a safeguard configured via the Allow Offline Access switch at Auth0 Dashboard > Applications > APIs > Settings, Auth0 will not return a Refresh Token for the API (even if you include the offline_access scope in your request).

Rules will run for the refresh token exchange. To execute special logic, you can look at the context.protocol property in your rule. If the value is oauth2-refresh-token, then the rule is running during the exchange.

If you try to do a redirect with context.redirect, the authentication flow will return an error.

If you have added custom claims to your tokens using a rule, the custom claims will appear in new tokens issued when using a Refresh Token for as long as your rule is in place. Although new tokens do not automatically inherit custom claims, rules run during the refresh token flow, so the same code will be executed. This allows you to add or change custom claims in newly-issued tokens without forcing previously-authorized applications to obtain a new refresh token.

JWT validation

We strongly recommend that you use middleware or one of the existing open source third-party libraries to parse and validate JWTs. At JWT.io, you can find libraries for various platforms and languages, such as .NET, Python, Java, Ruby, Objective-C, Swift, and PHP.

Signing algorithms

The algorithm used to sign tokens issued for your application or API. A signature is part of a JWT and is used to verify that the sender of the token is who it says it is and to ensure that the message wasn't changed along the way. To learn more about JWTs, read JSON Web Tokens. To learn more about signatures, read JSON Web Token Structure.

You can select from the following signing algorithms:

  • RS256 (RSA Signature with SHA-256): An asymmetric algorithm, which means that there are two keys: one public key and one private key that must be kept secret. Auth0 has the private key used to generate the signature, and the consumer of the JWT retrieves a public key from the Metadata endpoints provided by Auth0 and uses it to validate the JWT signature.

  • HS256 (HMAC with SHA-256): A symmetric algorithm, which means that there is only one private key that must be kept secret, and it is shared between the two parties. Since the same key is used both to generate the signature and to validate it, care must be taken to ensure that the key is not compromised. This private key (or secret) is created when you register your Application (Client Secret) or API (Signing Secret) and choose the HS256 signing algorithm.

The most secure practice, and our recommendation, is to use RS256 because:

  • With RS256, you are sure that only the holder of the private key (Auth0) can sign tokens, while anyone can check if the token is valid using the public key.

  • With RS256, you can request a token that is valid for multiple audiences.

  • With RS256, if the private key is compromised, you can implement key rotation without having to re-deploy your application or API with the new secret (which you would have to do if using HS256).

  • With HS256, if the secret key is compromised you would have to redeploy the API with the new secret.

Signing keys

It's good practice to assume that multiple signing keys could be present in your JWKS. This may seem unnecessary since the Auth0 JWKS endpoint typically contains a single signing key; however, multiple keys can be found in the JWKS when rotating signing certificates.

We recommend that you cache your signing keys to improve application performance and avoid running into rate limits, but you will want to make sure that if decoding a token fails, you invalidate the cache and retrieve new signing keys before trying only one more time.

Learn more