# JWT token is "invalid signature"?

**URL:** <https://community.auth0.com/t/jwt-token-is-invalid-signature/105796>\
**Category:** Get Help\
**Tags:** login-experience, new-universal-login-experience\
**Created:** [April 29, 2023, 10:40am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796 "2023-04-29T10:40:57Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [April 29, 2023, 10:40am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/1 "2023-04-29T10:40:57Z")

</div>

I’m totally new to JWT and most auth.

Right now I have a front-end which has Auth0’s universal login integrated. On callback, I obtain the auth token via `auth0Client.getTokenSilently()`.

This token is 505 characters long, and contains a mixture of alphanumeric characters and `.`'s.

I pass this to my back-end API so it can determine whether the user is logged in before completing the requested action.

Firstly, is this the right flow?

Secondly, why is [JWT.io](http://JWT.io)’s linter telling me the JWT I’m giving it is an “invalid signature”? This results in it showing an empty payload.

Despite this, when my back-end uses this 505-char token to retireve the user’s profile, it _does_ bring back the profile.

What am I missing here - why does everything basically work, but [JWT.io](http://JWT.io) says the token’s signature is invalid, resulting in no payload shown?

 ![image](https://global.discourse-cdn.com/auth0/original/3X/b/1/b14099d39a7e0b62e9e469226dce8776fd125938.png)

---

<div class="post-metadata">

**Author:** ![ty.frith](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/ty.frith/32/58484_2.png) [@ty.frith](https://community.auth0.com/u/ty.frith)\
**Post date:** [May 2, 2023, 2:14am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/3 "2023-05-02T02:14:41Z")

</div>

Hey again!

> [@kkrp1](#):
>
> Firstly, is this the right flow?

You’re on the right track!

> [@kkrp1](#):
>
> What am I missing here - why does everything basically work, but [JWT.io](http://JWT.io) says the token’s signature is invalid, resulting in no payload shown?

My guess is that this token is missing the `audience` - If you do not specify an `audience` (`aud` claim) then the access token you get back will be opaque (not a jwt). That is, it cannot be decoded but _can_ be used against the /userinfo endpoint. Some more on that here:

[https://community.auth0.com/t/why-is-my-access-token-not-a-jwt-opaque-token/31028](https://community.auth0.com/t/why-is-my-access-token-not-a-jwt-opaque-token/31028)

And a helpful FAQ on audience in general:

[https://community.auth0.com/t/what-is-the-audience/71414](https://community.auth0.com/t/what-is-the-audience/71414)

Hope this helps!

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 2, 2023, 10:13am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/4 "2023-05-02T10:13:46Z")

</div>

I’m mightily confused. I don’t have a hand in generating that token - it’s the result of resolving the query string params sent to my callback URL. I thought that what I resolved it to (via `getTokenSilently()`) generated a JWT. But you’re saying it’s lacking something, so how do I stop it lacking that thing?

I’m trying to follow docs but they go on forever and it just becomes circuitous. I did try to follow [this video](https://www.youtube.com/watch?v=o_MuvVF9pY4), but when I installed the debugger extension and stubbed the login request, I was met with an error page saying something went wrong, we track these errors automatically etc.

To clarify my issue: how do I validate, on the back-end, the token generated by `getTokenSilently()`, if it’s not a JWT after all because it’s missing this `aud` thing?

[This page](https://auth0.com/docs/secure/tokens/access-tokens) suggests that, in my situation, what I’m actually generating is not a JWT but an “opaque access token”. Accordingly, this doesn’t need validating, merely authenticating against the `/userinfo` endpoint. So if I understand that article correctly, authentication for my case _is_ as simple as just calling that endpoint. If I get a profile, the user is auth’d; if I don’t, they’re not. But you mentioned above that wasn’t ideal, so I’m confused.

Thank you.

---

<div class="post-metadata">

**Author:** ![ty.frith](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/ty.frith/32/58484_2.png) [@ty.frith](https://community.auth0.com/u/ty.frith)\
**Post date:** [May 2, 2023, 8:58pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/5 "2023-05-02T20:58:01Z")

</div>

Hey @kkrp1 !

> [@kkrp1](#):
>
> I’m mightily confused. I don’t have a hand in generating that token - it’s the result of resolving the query string params sent to my callback URL. I thought that what I resolved it to (via `getTokenSilently()`) generated a JWT. But you’re saying it’s lacking something, so how do I stop it lacking that thing?

Again, this all comes down to the `audience` - This would be passed to `getTokenSilently` via [authorizationParams](https://auth0.github.io/auth0-spa-js/interfaces/GetTokenSilentlyOptions.html#authorizationParams).

> [@kkrp1](#):
>
> [This page](https://auth0.com/docs/secure/tokens/access-tokens) suggests that, in my situation, what I’m actually generating is not a JWT but an “opaque access token”. Accordingly, this doesn’t need validating, merely authenticating against the `/userinfo` endpoint. So if I understand that article correctly, authentication for my case _is_ as simple as just calling that endpoint. If I get a profile, the user is auth’d; if I don’t, they’re not. But you mentioned above that wasn’t ideal, so I’m confused.

If the _only_ thing you are doing is using the access token to call `/userinfo` then you are correct in that you do not need an audience, nor do you need to validate the token per se. In most cases, the access token is used to access an API (your API) and in that case you’d want a JWT to inspect which does require an `audience` (your API identifier in Auth0) as you’ve seen.

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 3, 2023, 8:12am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/6 "2023-05-03T08:12:36Z")

</div>

> [@ty.frith](#):
>
> My guess is that this token is missing the `audience` - If you do not specify an `audience` (`aud` claim) then the access token you get back will be opaque (not a jwt). That is, it cannot be decoded but _can_ be used against the /userinfo endpoint. Some more on that here:

@ty.frith is spot on; that’s exactly what’s happening 🙂

Auth0 typically generates both an _ID Token_ and an _Access Token_: the _ID Token_ being designed for use by an application in order to build the application level session; the _Access Token_ being designed for use by your own API. Without the OAuth 2 `audience` parameter, the _(Access) Token_ returned is designed only for use with the Auth0 `/userinfo` endpoint ([here](https://auth0.com/docs/api/authentication#get-user-info)). Whilst it is a JWT, it uses an alternate encoding algorithm so is effectively opaque, and not designed to be decoded by [jwt.io](http://jwt.io).

Typically, any _Access Token_ you pass to your own API should have been generated by defining a (custom) API in Auth0 - see [here](https://auth0.com/docs/get-started/apis) for more details. You will typically specify a custom _audience_ (a.k.a. `Identifier`) for your API and also typically define `scopes`; both of which your API will typically validate. The _Access Tokens_ generated for the (custom) `audience` can then be used to call your API securely **and** your API can also use it to call the Auth0 `/userinfo` endpoint too: all (custom) API tokens generated in Auth0 typically support two intended audiences, the custom one you define _and_ also the Auth0 `/userinfo` endpoint.

Hope that helps 🕶

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 3, 2023, 9:05am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/7 "2023-05-03T09:05:57Z")

</div>

Thank you, both. I think the reality is I’m just rather out of my depth. I feel like I’m patching many and various concepts together from different docs articles and trying to make it all work. I appreciate all the advice that’s been given.

So here is what I have now done, based on the latest advice.

1. I have set up an API under Applications \> APIs. I have given it the identifier `https://my-api`

2. I am now passingthe audience param to `getTokenSilently()` via `getTokenSilently({audience: 'https://my-api'})`.

However this results in no change. The access token I get back from `getTokenSilently()`, even though I’m now specifying the audience, is still not a valid JWT according to [JWT.io](http://JWT.io) and thus not something I can validate back-end.

[This article](https://dev.to/kleeut/getting-a-jwt-access-token-from-auth0-3e81) suggests I must specify audience (my API identifier) in the Auth0 _constructor_. I’ve tried this too, but same result.

```auto
const auth0Client = await auth0.createAuth0Client({
	domain: ' ********',
	clientId: ' ********',
	audience: 'https://my-api',
	...

```

It’s also not clear which Auth0 application my browser should be connecting to. I have a SPA application, but when I created the API, this implicitly created a second (machine-to-machine) application. Only the former works with the browser flow. Yet presumably my API works only with the latter?

[[[---- EDIT ----]]]

Whoop! I finally got it working. It seems that I needed to specify audience within the `authorizationParams` sub-object in the constructor, i.e.

```auto
const auth0Client = await auth0.createAuth0Client({
	domain: ' ********',
	clientId: ' ********',
	authorizationParams: {
		redirect_uri: window.location.origin,
		audience: 'https://my-api'
	}
	...

```

I’d still be interested to know which application I should be using, though - the one _I_ set up (the SPA application) or the one that was implcitly created (machine-to-machine) when I created my API. And how come, when I go to my API \> Test, it lets me test it only in the context of the machine-to-machine application, yet my browser login works only with the SPA application client ID?

Continued thanks!

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 9, 2023, 9:34am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/8 "2023-05-09T09:34:48Z")

</div>

@peter.fernandez @ty.frith any thoughts here re: my last paragraph? Many thanks.

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 9, 2023, 10:25am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/9 "2023-05-09T10:25:33Z")

</div>

> [@kkrp1](#):
>
> I’d still be interested to know which application I should be using, though - the one _I_ set up (the SPA application) or the one that was implcitly created (machine-to-machine) when I created my API. And how come, when I go to my API \> Test, it lets me test it only in the context of the machine-to-machine application, yet my browser login works only with the SPA application client ID?

Thanks for the update @kkrp1 and I’m really glad to hear you got things working! 🕶🎉

OAuth2 defines a number of different grant types, which are also supported by Auth0 - see [here](https://auth0.com/docs/get-started/applications/application-grant-types) for more details. Grant types essentially define what workflows applications can perform - and the context in which they perform them too: _authorization code_, for example, lets an application perform API operations in the context of a user - with that user’s consent - whilst _client credentials_ allows an application to perform API operations in the context of the application itself (i.e. without a user ever being involved). The latter is typically used in a secure backend context - so rarely, if ever, in a SPA - and essentially uses machine level credentials rather than user level credentials. Auth0 automatically creates a machine-to-machine application for convenience, and this can be used to test an API from some secure automated backend (or the like).

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 9, 2023, 12:11pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/10 "2023-05-09T12:11:02Z")

</div>

> [@kkrp1](#):
>
> Thank you, both. I think the reality is I’m just rather out of my depth. I feel like I’m patching many and various concepts together from different docs articles and trying to make it all work. I appreciate all the advice that’s been given.

You’re welcome; glad to be of help 🙂 If you haven’t found it yet then may I suggest heading on over to [https://developer.auth0.com](https://developer.auth0.com) where you can find a whole host of guides et al that are supplemental to our Auth0 Docs. You may find some additional material that might help you get a better understanding 🕶

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 9, 2023, 2:37pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/11 "2023-05-09T14:37:12Z")

</div>

Thanks for this! I have found those guiys, and I’ve worked my way through several of them.

OK so based on what you said, does it sound like I’m on the right lines? To clarify, I’m not using the API key from my SPA; I’m using that from my back-end API to talk to Auth0 to get the profile of the user who logged in.

My precise flow is this:

1. In the front-end, user clicks login and goes through the universal login flow (bouncing out to Auth0, then back to my page via the callback)

2. This results in a code passed in the query string. This I convert to a JWT via the `getTokenSilently()` method of the front-end JS SDK. In doing this, I also specify the audience (which is the identifier of my Auth0-side API)

3. From this point, any calls to my back-end API (which lives separately from the SPA) involves passing this JWT along with it, as a header.

4. My API receives this, validates it via the `express-oauth2-jwt-bearer` NPM package, then, if that passes, uses it call `/userinfo` to resolve the user to a profile, get user meta etc.

This is my auth flow, based on everything I’ve learned and all the kind help I’ve received. Am I broadly on the right lines here?

Continued thanks.

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 9, 2023, 3:49pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/12 "2023-05-09T15:49:43Z")

</div>

> [@kkrp1](#):
>
> This is my auth flow, based on everything I’ve learned and all the kind help I’ve received. Am I broadly on the right lines here?

Essentially that’s correct 🕶 In terms of step 4, the call to `/userinfo` will typically return the user profile as defined by the OAuth 2 specification. However this will typically not include any user metadata - user metadata being an Auth0 artefact - unless an aspect has been included as a custom claim within the ID Token: custom claims are available via the `/userinfo` endpoint. For additional details see [JSON Web Token Claims](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims)

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 9, 2023, 4:03pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/13 "2023-05-09T16:03:02Z")

</div>

Thanks for this.

Re: meta data, actually I had a separate thread about this and your colleague suggested using a post-login action to bolt the meta data onto the returned profile. (Interesting, though, that this approach isn’t discussed on the user meta docs page, which does discuss some other approaches.)

Am I right in thinking the right thing is to retrieve the user meta from the profile, rather than somehow adding the user meta into the JWT itself? (My JWT noobness showing through, there…)

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 10, 2023, 8:54am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/14 "2023-05-10T08:54:21Z")

</div>

> [@kkrp1](#):
>
> Am I right in thinking the right thing is to retrieve the user meta from the profile, rather than somehow adding the user meta into the JWT itself? (My JWT noobness showing through, there…)

That really depends 🙂

It depends on the metadata you want to retrieve. For example, retrieving user `user_metadata` directly from Auth0 can be done from an insecure - i.e. SPA - context via use of the Auth0 Management API by using a token allocated in a user context; see [here](https://auth0.com/docs/secure/tokens/access-tokens/get-management-api-tokens-for-single-page-applications#available-scopes-and-endpoints) for more details. Retrieving user `app_metadata`, however, typically requires a secure backend context using a token allocated via machine level authentication (i.e. _client credentials_ grant).

It also depends on the contents of the metadata you intend to retrieve. If it’s of a PII or highly security sensitive nature then using the Management API can be the preference. However, this requires an additional API call to Auth0, (potentially) the use of a secure backend, and potentially may be impacted by rate limiting (see paragraph below). So using custom claims can be a more desirable option - particularly where (meta) data is of a non-PII or a lesser security sensitive nature (and when generated tokens are also managed securely, such as via a regular web service backend, or the like). Using custom claims doesn’t necessarily require any additional API calls to Auth0 - at least not outside of the `/userinfo` endpoint - and their use is not subject to rate limiting either.

And it also depends on the frequency that metadata will be retrieved. The rate limits associated with the Auth0 Management API are significantly different - and typically more restrictive - than the rate limits associated with the Auth0 Authentication API. See [here](https://auth0.com/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy) for more details. Depending on how often metadata needs to be read, this can preclude use of the Auth0 Management API and so using custom claims may be the only option.

Lastly, the database that backs the Auth0 User Profile - i.e. where metadata et al is stored for a user; see [User Profile Structure](https://auth0.com/docs/manage-users/user-accounts/user-profiles/user-profile-structure) for additional details - is **not** designed to be general purpose. It is primarily designed to hold information that would be valuable as part of user authorization in the context of OAuth 2/OIDC, SAML, etc. So having some additional and external datastore, where specific user profile information is stored - such as the llist of recently placed orders, for instance - is the preferred alternative.

FWIW, Auth0 Professional Services ([here](https://auth0.com/docs/get-started/professional-services)) is often the best route to take for any integration where there may be aspects of a tricky nature - and/or where there are concerns about security or the potential for opening up additional security attack surfaces.

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 11, 2023, 11:50am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/15 "2023-05-11T11:50:50Z")

</div>

Thanks for this extensive info.

In my case, using the flow I described previously, my idea is to store permissions info on the user metadata (not to be confused with Auth0 permissions - I’m storing the permissions in a proprietary, JSON-formatted way). To be clear, these are permissions for using my back-end app, not using Auth0.

Given what you said about the Auth0 user database not being intended for general purpose, is this a bad idea, and instead, as you allude, I should store these permissions outside of Auth0?

These permissions need to be retrieved on every request to my back-end API. (I could cache them, but I’m trying to build my API statelessly). Since they come from `/user_info`, i.e. the authentication API, not the management API, I figure I’ll be OK with rate limiting.

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 12, 2023, 9:47am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/16 "2023-05-12T09:47:31Z")

</div>

> [@kkrp1](#):
>
> Given what you said about the Auth0 user database not being intended for general purpose, is this a bad idea, and instead, as you allude, I should store these permissions outside of Auth0?

That really depends on the complexity of the permission list you’re planning to store 🙂 If you do end up going down the metadata route, then I would recommend using user `app_metadata`; user `user_metadata` is user self-modifiable, which is typically something you would definitely **not** want users to be able to do! Alternatively, perhaps take a look at something like the [Auth0 FGA](https://docs.fga.dev) project? It might be just what you’re looking for - or if not, at least give you some ideas 🕶

> These permissions need to be retrieved on every request to my back-end API. (I could cache them, but I’m trying to build my API statelessly). Since they come from `/user_info` , i.e. the authentication API, not the management API, I figure I’ll be OK with rate limiting.

This sort of fine/finer grained authorization use case is not uncommon. And for these scenarios the recommendation would typically be to add the authorization permissions as one or more custom claims to the _Access Token_ JWT. Assuming the permission complexity won’t end up bloating the JWT that is. You can also add the same custom claims - or some additional (custom) claims based on the permissions - to the _ID Token_ JWT too, if the UX needs to reflect what the user is allowed to do. Remember JWTs are signed, so it’s impossible to tamper with the data. And because JWT contents is easily (machine) decoded, you mitigate any additional API calls (e.g. the call to the `/userinfo` endpoint from step 4 in the flow above) 🕶

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 13, 2023, 6:36pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/17 "2023-05-13T18:36:54Z")

</div>

Thanks as ever, @peter.fernandez, really appreciate the continued help.

Re: adding my proprietary permissions format to the JWT itself (to the payload), this is where I’m stuck. I can’t figure out how to add anything to the JWT’s payload - only to the response returned by `/userinfo`.

One of your colleagues suggested this action, but as I say, that just adds the metadata to the response from `/userinfo`, not the JWT payload itself:

```auto
exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://my-app.example.com';
  if (event.authorization)
    api.idToken.setCustomClaim(`user_meta`, event.user.user_metadata);
};

```

I’d love to figure out how to add this (or any other info) to the JWT itself - that way I wouldn’t have to keep calling `/userinfo` on every request to my back-end to get the permissions, I’d have them right there in the JWT.

Again, apologies if I’m asking things you believe you’ve already answered - a lot of this is double-dutch to me and I’m trying to make sense of it as I go.

Thanks!

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 14, 2023, 7:06am UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/18 "2023-05-14T07:06:37Z")

</div>

Ah! I think I see where the confusion lies! Let me see if I can clarify 🕶

Firstly I’d recommend reframing your perspective. Previously in the thread (see [here](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/6) for details) I mentioned that Auth0 typically generates two types of token: an _ID Token_ and an _Access Token_; the former intended for use by an application, the latter intended for use by an API (a.k.a. resource server). They are **both** JWTs! Context then becomes an important clarifier when one simply refers to “a JWT” - because one has to understand/convey the use for which said JWT is intended. Thinking/communicating in terms of _ID Token_ and _Access Token_ on the other hand implicitly provides better clarity by virtue of nomenclature 🙂

With this in mind, if we revisit the aforementioned code then - together with the Auth0 Docs for [Actions Triggers: post-login - Event Object](https://auth0.com/docs/customize/actions/flows-and-triggers/login-flow/event-object) and [Actions Triggers: post-login - API Object](https://auth0.com/docs/customize/actions/flows-and-triggers/login-flow/api-object) - we could perhaps refactor in the following manner:

```auto
 exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://my-app.example.com';
  if (event.authorization)
    api.idToken.setCustomClaim(`user_meta`, event.user.user_metadata);
    api.accessToken.setCustomClaim(`user_meta`, event.user.user_metadata);
};

```

The original line (namely`api.idToken.setCustomClaim`) adds a custom claim to the _ID Token_ which the application can use - i.e. to perhaps correctly format the UX. And the new line (namely `api.accessToken.setCustomClaim`) adds a custom claim to the _Access Token_ which the API can then use - e.g. to perform any ‘fine grained’ authorization, or the like. Both are a, and in this case the same, custom claim added to a JWT!

Some best practice recommendations (and in no particular order):

1. Prefer to create name-spaced custom claims (see [here](https://auth0.com/docs/secure/tokens/json-web-tokens/create-custom-claims#namespaced-guidelines) for further details). This is what the `namespace` constant declared is intended for, and one would simply use it like so: `api.idToken.setCustomClaim(namespace+`/user\_meta`, event.user.user_metadata);`

2. As per my previous post [here](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/16): prefer to use `user.app_metadata` (rather than `user.user_metadata`) to store information that’s not intended for the user, themselves, to modify. You can read about this more in our [Actions Triggers: post-login - Event Object](https://auth0.com/docs/customize/actions/flows-and-triggers/login-flow/event-object) docs where `event.user` is discussed.

3. Avoid adding any custom claim based on `user.app_metadata` en masse. It’s highly unlikely to be the intent for any new attribute added to `app_metadata` that may be added in the future to automatically be exposed as a custom claim! Prefer instead to create one, or more, custom claim(s), each based on a specific `app_metadata` attribute.

We’ve covered a number of different things in this thread. Consequently, somewhat meandering from the original topic of discussion. Generally speaking, to facilitate more focused and clear conversation I’m going to recommend we now let this thread close; please feel free to reach out here in Community for additional help, and myself and the team will be happy to provide you with specific feedback and advice on a topic by topic basis 🤗

---

<div class="post-metadata">

**Author:** ![kkrp1](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/User_font_awesome.svg/768px-User_font_awesome.svg.png) [@kkrp1](https://community.auth0.com/u/kkrp1)\
**Post date:** [May 15, 2023, 12:35pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/19 "2023-05-15T12:35:52Z")

</div>

Thank you, @peter.fernandez. Nailed it. Give yourself a biscuit. Thanks again! (And yes, apologies for the meandering thread!)

---

<div class="post-metadata">

**Author:** ![peter.fernandez](https://sea1.discourse-cdn.com/auth0/user_avatar/community.auth0.com/peter.fernandez/32/45477_2.png) [@peter.fernandez](https://community.auth0.com/u/peter.fernandez)\
**Post date:** [May 15, 2023, 12:54pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/20 "2023-05-15T12:54:28Z")

</div>

😂 I’ll take a Chocolate Digestive! Thank you 🕶 So glad to hear you got it working; all the best with the rest of your integration 🤗

---

<div class="post-metadata">

**Author:** ![system](https://global.discourse-cdn.com/auth0/original/3X/b/5/b560f266932fcce5d57f09a6595bd50518cae3f3.jpeg) [@system](https://community.auth0.com/u/system)\
**Post date:** [May 29, 2023, 12:55pm UTC](https://community.auth0.com/t/jwt-token-is-invalid-signature/105796/21 "2023-05-29T12:55:12Z")

</div>

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.
