Verify Access Tokens for Custom APIs

Heads up! As part of our efforts to improve security and standards-based interoperability, we have implemented several new features in our authentication flows and made changes to existing ones. For an overview of these changes, and details on how you adopt them, refer to Introducing OIDC Conformant Authentication.

When a custom API receives a request with a bearer access token, the first thing to do is to validate the token. At Auth0, an access token used for a custom API is formatted as a JSON Web Token. Validating the token consists of a series of steps, and if any of these fails then the request must be rejected.

This document lists all the validations that your API should perform:

  • Check that the JWT is well formed
  • Check the signature
  • Validate the standard claims
  • Check the Client permissions (scopes)

JWT.io provides a list of libraries that can do most of the work for you: parse the JWT, verify the signature and the claims.

Parse the JWT

First, the API needs to parse the JSON Web Token (JWT) to make sure it's well formed. If this fails the token is considered invalid and the request must be rejected.

A well formed JWT, consists of three strings separated by dots (.): the header, the payload and the signature. Typically it looks like the following:

Sample JWT

The header and the payload are Base64Url encoded. The signature is created using these two, a secret and the hashing algorithm being used (as specified in the header: HMAC, SHA256 or RSA).

For details on the JWT structure refer to What is the JSON Web Token structure?.

How can I parse the JWT?

In order to parse the JWT you can either manually implement all the checks as described in the specification RFC 7519 > 7.2 Validating a JWT, or use one of the libraries listed in the Libraries for Token Signing/Verification section of JWT.io.

For example, if your API is implemented with Node.js and you want to use the node-jsonwebtoken library, then you would call the jwt.verify() method. If the parsing fails then the library will return a JsonWebTokenError error with the message jwt malformed.

We should note here that many web frameworks (such as ASP.NET Core for example) have JWT middleware that handle the token validation. Most of the times this will be a better route to take, rather that resorting to use a third-party library, as the middleware typically integrates well with the framework's overall authentication mechanisms.

How can I visually inspect a token?

A quick way to see what is inside a JWT is by using the JWT.io website (alternatively, you can use the JWT Debugger Chrome Extension). It has a handy debugger which allows you to quickly check that a JWT is well formed, and also inspect the values of the various claims.

Just paste your token at the Encoded text area and review the decoded results at the right.

Decode JWT with JWT.io

Check the Signature Algorithm

The API needs to check if the algorithm, as specified by the JWT header (property alg), matches the one expected by the API. If not, the token is considered invalid and the request must be rejected.

In this case the mismatch might be due to mistake (it is common that the tokens are signed using the HS256 signing algorithm, but your API is configured for RS256, or vice versa), but it could also be due to an attack, hence the request has to be rejected.

How can I check the signature algorithm?

To check if the signature matches the API's expectations, you have to decode the JWT and retrieve the alg property of the JWT header.

Alternatively, you can use one of the libraries listed in the Libraries for Token Signing/Verification section of JWT.io.

Following the Node.js example of the previous section, the jwt.verify() method of the node-jsonwebtoken library, supports an algorithms argument, that contains a list of strings with the names of the allowed algorithms.

Verify the Signature

The API needs to verify the signature. The signature is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn't changed along the way.

Remember that the signature is created using the header and the payload of the JWT, a secret and the hashing algorithm being used (as specified in the header: HMAC, SHA256 or RSA). The way to verify it, depends on the hashing algorithm:

  • For HS256, the API's Signing Secret is used. You can find this information at your API's Settings. Note that the field is only displayed for APIs that use HS256.
  • For RS256, the tenant's JSON Web Key Set (JWKS) is used. Your tenant's JWKS is https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json.

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

How can I verify the signature?

To verify a token's signature, you can use one of the libraries listed in the Libraries for Token Signing/Verification section of JWT.io.

Following the Node.js example of the previous section, the jwt.verify() method of the node-jsonwebtoken library, supports an secretOrPublicKey argument, that should be populated with a string or buffer containing either the secret (for HS256), or the PEM encoded public key (for RS256). If the verification fails you will get a invalid signature error.

Validate the Claims

Once the API verifies the token's signature, the next step is to validate the standard claims of the token's payload. The following validations need to be made:

  • Token expiration: The current date/time must be before the expiration date/time listed in the exp claim (which is a Unix timestamp). If not, the request must be rejected.
  • Token issuer: The iss claim denotes the issuer of the JWT. The value must match the one configured in your API. For JWTs issued by Auth0, iss holds your Auth0 domain with a https:// prefix and a / suffix: https://YOUR_AUTH0_DOMAIN/.
  • Token audience: The aud claim identifies the recipients that the JWT is intended for. For JWTs issued by Auth0, aud holds the unique identifier of the target API (field Identifier at your API's Settings). If the API is not the intended audience of the JWT, it must reject the request.

How can I validate the claims?

To validate the claims, you have to decode the JWT, retrieve the claims (exp, iss, aud) and validate their values.

The easiest way however, is to use one of the libraries listed in the Libraries for Token Signing/Verification section of JWT.io. Note that not all libraries validate all the claims. In JWT.io you can see which validations each library supports (look for the green check marks).

Following the Node.js example, the jwt.verify() method of the node-jsonwebtoken library, validates these claims, depending on the input arguments:

  • audience: set aud to the Identifier of the API
  • issuer: string or array of strings of valid values for the iss field
  • ignoreExpiration: set to false to validate the expiration of the token

Check the Permissions

By now you have verified that the JWT is valid. The last step is to verify that the client has the permissions required to access the protected resources.

To do so, you need to check the scopes of the decoded JWT. This claim is part of the payload and it is a space-separated list of strings.

How can I check the permissions?

To check the permissions granted to the client, you need to check the contents of the scope.

For example, a user management API might provide three endpoints to read, create or delete a user record: /create, /read and /delete. We have configured this API, so each endpoint requires a specific permission (or scope):

  • The read:users scope provides access to the /read endpoint.
  • The create:users scope provides access to the /create endpoint.
  • The delete:users scope provides access to the /delete endpoint.

If a request requests to access the /create endpoint, but the scope claim does NOT include the value create:users, then the API should reject the request with 403 Forbidden.

You can see how to do this, for a simple timesheets API in Node.js, in this document: Check the Client permissions.

Sample Implementation

You can find a sample API implementation, in Node.js, in Server Client + API: Node.js Implementation for the API.

This document is part the Server + API Architecture Scenario, an implementation of a Client Credentials grant for a hypothetical scenario. For more information on the complete solution refer to Server + API Architecture Scenario.

Read more