How to get Sub Claim from Access Token


tl;dr I would like to understand how I can extract the User Sub claim from an Access Token.


We’re building an SPA that exclusively uses serverless function (from Vercel) to make authenticated calls to an IdP at throughput (60 calls/min, which are bursted in <1 sec, every sec) that is faster than the Auth0 Authentication /userinfo endpoint ratelimit (5 calls/min). We use this endpoint to hit the Management /api/v2/users/{id} endpoint and retrieve the IdP Access Tokens, which we then store in a Redis DB to be cache’d for future requests. However we would still need to make a /userinfo call every time the serverless function runs so we can obtain the sub which used as the key in Redis. If the serverless function can decode the sub from the access token, we could just ask the redis cache directly, avoiding calls to Auth0 for all requests outside of login.

System Arch:

The following are how we understand and implement both Login and Authenticated Calls to IdP flows, followed by how we would ideally like to make the Authenticated Calls to IdP.


  1. Trigger loginWithPopup() on the browser client and login with IdP
  2. Auth0 completes OAuth1 flow with IdP API
  3. IdP API returns IdP Access tokens to Auth0 *which is stored in an Auth0 DB
  4. (accidentally labeled this 5) Auth0 return ID and accessTokens to Browser? (this part is unclear to me, tho probably not important to the problem)
  5. ^^
  6. Browser Client sends Access Token retrieved by the auth0-spa-js function getTokenSilently() to Vercel
  7. Vercel hits /userinfo endpoint with Access Token as Bearer
  8. Auth0 replies with User Info
  9. Vercel hits /api/v2/users/{id} where ID is the extracted Sub Claim from the Auth0 response is the previous step
  10. Auth0 responds with an object that contains the IdP access token *which was retreived from an Auth0 DB
  11. Finally Vercel stores the info in a Redis DB where the key is the sub and the value is the IdP Access Token

Authenticated Call to IdP (this is made 60/min) :

  1. Logged In User makes request that requires data from the IdP with the Auth0 Access Token retrieved by the auth0-spa-js function getTokenSilently() passed in the request
  2. Vercel hits /userinfo endpoint with Access Token as Bearer
  3. Auth0 replies with User Info
  4. Vercel sends extracted Sub Claim from the Auth0 response is the previous step to Redis DB
  5. Redis DB finds the key (Sub Claim) and returns the value (IdP Access Token)
  6. Vercel makes authenticated request to IdP using the user’s IdP Access Token
  7. IdP API returns payload
  8. Vercel passes payload back to User

Steps 2 and 3 are the problematic ones because this call to Auth0 would be made 60/min, bursted in <2sec

Referencing some documentation from Auth0:

The token does not contain any information about the user except for the user ID (located in the sub claim).

Since we use the Sub Claim to store the IdP Access Token, we could decode the Access Token instead of making the call to Auth0 to get the Sub Claim.

However in our testing we found that the Access Token retrieved by the auth0-spa-js function getTokenSilently() is not a JWT, which is corroborated by Auth0 Docs:

Access tokens (which aren’t always JWTs) […]

In what ways could we decode the Access Token into a Sub Claim. And if this is not possible, how can we avoid calls to Auth0 API when we need make requests to the IdP. Our ideal flow would look like the following:

Hey there!

When it comes to consulting such complex scenarios, I highly encourage you to discuss it with our Professional Services:

Hi. I gave the scenario for reference, but my initial question still stands. How do I extract the Sub from the Access Token? Is this possible?

Using this library you would use claims.sub

1 Like

Thanks for sharing that with the rest of community Sandrino!

Ok, yes I managed to get this done! I’ll drop some more refs and docs for others, but my main problem was not declaring a aud, which meant I was getting an opaque string instead of JWT.

@sandrino-a0 My next question is. What part of this is secure, if we never hit the Auth0 API (which for my context is preferred)? What’s to stop an attacker from forging request or replaying request?

Posting my solution for ref:

In general to get the Sub claim you must verify and decode the JWT (there are a lot of libs, including the one above, that do this). The specifics of which are covered in many other places.

If you are getting an opaque string as your access token, you must include an audience/aud claim when getting the token. That aud will be the identifier from an API in your dashboard.

For example in my code, this is the options I pass when making a instance of the Auth0 client:

let auth0Client = await createAuth0Client({
 domain: config.domain,
 client_id: config.clientId,
 audience: "literallyAnyString",

Where “literallyAnyString” is the identifier that you assigned when making the API
(Auth0 implicitly suggests the url of the API, but it can be… literally any string)

This will ensure you are now sending access tokens as JWTs.

Example of where you will/would have set the identifier:

You can also find it at the API screen:

1 Like

Thanks for sharing that with the rest of community!