Rule doesn't add custom claims

In my Node CLI I I use the auth0 package to call Auth0.

If I use the signIn method on the database login like this:

await this.authClient.database!.signIn({
username: email,
password,
connection: ‘Username-Password-Authentication’,
})

When I use this tool to decode the tool: https://jwt.io/
I get back a token which doesn’t contain the custom data I added via a rule, just the basic fields

{
“iss”: “https://mydomain.auth0.com/”,
“sub”: “auth0|5b6b2342j54355613fd40421”,
“aud”: “kcpmPE21Lc6nSJf36oneC5pxJ69Vs”,
“iat”: 1546061189,
“exp”: 1546097189
}

But when I use a password grant:

const asd = await this.authClient.oauth!.passwordGrant({username: email, password, realm: ‘Username-Password-Authentication’})

I get a token that decodes like this:

{
https://www.mydomain.com/meta”: {
“roles”: [
“ordinary”
],
“importantValue”: “17”,
“claims”: [
“admin”
]
},
“nickname”: “piersm”,
“name”: “piers@piers.com”,
“picture”: “https://s.gravatar.com/avatar/74962f17942d8f13f13fde7af051eb90?s=480&r=pg&d=https%3A%2F%2Fcdn.auth0.com%2Favatars%2Fpi.png”,
“updated_at”: “2018-12-29T05:26:30.034Z”,
“email”: “piers@piers.com”,
“email_verified”: false,
“iss”: “https://mydomain.auth0.com/”,
“sub”: “auth0|5b6b57f3e5439292923fd40421”,
“aud”: “kcpmPE21Lc6nSz5yFasateC5pxJ69Vs”,
“iat”: 1546061190,
“exp”: 1546097190
}

But I can’t specify the audience of that request so my backend API can’t decode the token.

I’m not sure I understand the difference of what I’m doing here that is causing this. Why are there different tokens? What’s the audience?

Hi!
await this.authClient.database!.signIn uses the older (and deprecated) /oauth/ro to get the token result. /oauth/ro will not get the custom claims added, and also does not take an audience parameter (so the access token returned can’t be used to make requests to a custom API).

So, if you are using resource owner password grant (i.e. exchanging credentials for a token) you should be using the passwordGrant function, which uses the /oauth/token endpoint (the correct one to use in these cases).

But I can’t specify the audience of that request so my backend API can’t decode the token.

I’m not quite sure what you mean here, particularly because /oauth/ro does not take an audience parameter (/oauth/token does, on the other hand).
If you specify openid as the scope (which is the default value) you will get an ID Token (information about the user for the client application) and an Access Token (good to make requests to the /userinfo endpoint).
If you also specify an audience (an identifier of a custom API you created in the Auth0 dashboard), you will get an access token that is of JWT format, and can be used to make requests to your custom API. This is the token that you would usually use to authorize requests to the backend.

1 Like

I dug into the code a little. Bear with me here. There are 3 applications:

  1. React Web App
  2. Nodejs back end
  3. Nodejs CLI (new app I’m trying to get working)

Now the back end uses the following code (using the npm jsonwebtoken package)

decoded = jwt.verify(token, publicKey, {
  audience: config.authorizer.audience,
  issuer: config.authorizer.issuer,
  algorithms: ['RS256'],
});

To crack open the token and get the information stored in there (the stuff in the claims property from above).

Now there’s an Auth0 application for existing React web app. That web app uses the Client ID as it’s audience. Then the backend also uses that client ID to verify the token.

Since I created a separate application for my CLI (with a different Client ID) I now understand why calls made from the CLI to the backend are failing at that .verify() step.

So what’s the recommended way to handle this situation where a backend API has 2 different clients? Should they use the same Auth0 application? Should I use something else as the audience?

The backend should be modeled as an API (https://manage.auth0.com/#/apis). When you create the API, you assign an identifier to it, which will be the audience to check for.

Then each client you define will need to request an Access Token to that API, by adding the audience={the_api_identifier} parameter in the token request. When you add the audience parameter, the client application will get an access token where the audience is that of the target API. Note that if you also specified scope=openid you will get an ID Token, which carries information about the user and is meant to be consumed by the client application directly.

So, in essence, you end up with one API and any number of clients (each with their own client ID) that can potentially request a token for the API. In the access tokens, the aud claims (audience) will be the API identifier. In the ID tokens, the aud will be the client ID.

Also, note that you will need passwordGrant method (not signIn), as /oauth/ro is not compatible with the audience and API authorization features. Or, even better, use an interactive authorization flow (/authorize) to avoid collecting the credentials directly in the react application.

2 Likes

So I managed to get something working but it doesn’t align with what you laid out. I just want to clarify.

So here’s what I have. A node.js API and two clients a node CLI and a React web page. So everything is in javascript.

Here’s the web page client code. It was set up long ago and seems to work. It uses the auth0-js package

auth0 = new auth0.WebAuth({
domain: ‘mycompany.auth0.com’,
clientID: ‘SDFWR@#$GR@#$RSFD#@$F3214’,
redirectUri: ‘mycompany.com/callback’,
audience: https://mycompany.auth0.com/userinfo,
responseType: ‘token id_token’,
});

Then the CLI uses this (unfortunately it’s not easy to use the redirect logic). It uses the auth0 package

const authClient = new AuthenticationClient({
clientId: ‘nvbnfghf5t34567rhnfg54r674hgdf546s’,
domain: ‘mycompany.auth0.com
})

Now when I use both I get a token which I used the npm jsonwebtoken package to crack open as such

const decodedToken = jwt.verify(
  token!,
  functionToGetPublicKey(),
  {
    audience: CLIENT_ID for either above apps
    issuer: 'https://mycompany.auth0.com/',
    algorithms: ['RS256'],
  });

This works and I get the claims in the token I’m looking for provided I supply the right client id as the audience. However this is very different than what you’ve laid out.

  1. There’s no access tokens ( I have no idea where they come into play)
  2. Audience which is listed in my Auth0 dashboard as https://mycompany.auth0.com/api/v2/ isn’t used anywhere. Audience is only specified on the web client SDK and the API side decoding, there’s no mention of it in the CLI. The .verify() uses the client ID as the audience.

Do I have this wired up wrong?

The issue here is that the web client is asking just for an ID Token (the token used to get information about the authentication process). When you include audience: https://mycompany.auth0.com/userinfo, that audience is the OpenID Connect user info endpoint (i.e. the endpoint that provides information about the user), and that audience is kind of implied when you put scope:openid.

So, what you want is an API properly defined in the API sections on the dashboard. You will provide an ID for that API (e.g. https://yourdomain.com/api). This does not need to be a real URL, it’s just an identifier.
Then, your authorize request will mention that audience:

auth0 = new auth0.WebAuth({
  domain: ‘mycompany.auth0.com’,
  clientID: ‘SDFWR@#$GR@#$RSFD#@$F3214’,
  redirectUri: ‘mycompany.com/callback’,
  audience: "https://yourdomain.com/api",
  responseType: ‘token id_token’,
});

That will get you an ID Token (for the client application to consume) and an Access Token (to make requests to the API).

The API will have to be configured so that the audience is the API identifier, not the client ID:

const decodedToken = jwt.verify(
  token!,
  functionToGetPublicKey(),
  {
    audience: "https://yourdomain.com/api",
    issuer: 'https://mycompany.auth0.com/',
    algorithms: ['RS256'],
  });

The CLI will have to use the client credentials grant to get an Access Token too, where you exchange the client credentials (client id and client secret) for an Access Token.
For that, you will create a specific Application to represent the CLI (if you haven’t done so) and, in the API configuration, grant access to that application.
Once you do that, then you can do this:

const authClient = new AuthenticationClient({
  clientId: ‘the-cli-client-id’,
  clientSecret: 'the-cli-client-secret',
  domain: ‘mycompany.auth0.com’
});
authClient.clientCredentialsGrant({
   audience: 'https://mycompany.auth0.com/',
 }, function (err, response) {
   if (err) {
     // Handle error.
   }

   console.log(response); // here you will have the access token to call the API
});
1 Like

Thanks for the quick reply. Okay I see where things aren’t tying together, it’s in the API part. Okay. I’ll give that a shot

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