When renewing id_token via refresh_token in Auth0, jti (JWT ID) not in new id_token

1.2k views Asked by At

When logging into Auth0:

POST https://my.auth0.com/oauth/ro
{
  "client_id":   "<client-id>",
  "username":    "[email protected]",
  "password":    "••••••••",
  "connection":  "<auth0-connection>",
  "grant_type":  "password",
  "scope":       "openid offline_access jti email",
  "device":      "<device-id>"
}
// Response
{
  "refresh_token": "<refresh-token>",
  "id_token": "<id-token>",
  "access_token": "<access-token>",
  "token_type": "bearer"
}
// id_token JWT payload
{
  "jti": "3d4c5e97-3543-4c53-b46e-3aa965cd5a34",
  "email": "[email protected]",
  "email_verified": false,
  "iss": "https://my.auth0.com/",
  "sub": "auth0|<id>",
  "aud": "<aud>",
  "exp": 1481766419,
  "iat": 1481730461
}

if I specify jti in my scope, the returned id_token, which is a JWT, will contain a jti. Having jti in a JWT is recommended by Auth0. jtis uniquely identify JWTs and can be used for things like blacklisting JWTs.

For some reason though, if I try getting a new id_tokenusing a refresh token:

POST https://my.auth0.com/delegation
{
  "client_id":       "<client-id>",
  "grant_type":      "urn:ietf:params:oauth:grant-type:jwt-bearer",
  "refresh_token":   "<refresh-token>",
  "api_type":        "app",
  "scope":           "openid offline_access jti email",
  "device":          "<device-id>"
}
// Response
{
  "token_type": "Bearer",
  "expires_in": 35958,
  "id_token": "<id-token>"
}
// id_token JWT payload
{
  "email": "[email protected]",
  "email_verified": false,
  "iss": "https://my.auth0.com/",
  "sub": "auth0|<id>",
  "aud": "<aud>",
  "exp": 1481766678,
  "iat": 1481730720,
  "azp": "<azp>"
}

even though I specify jti in my scope, the id_token returned does not contain a jti.

Is this a bug? Please help.

1

There are 1 answers

3
João Angelo On BEST ANSWER

The jti claim is not generated or included by default. If you're seeing that behavior the most likely explanation is that you have a custom rule that is doing the following:

function (user, context, callback) {
  user.jti = require('uuid').v4();
  callback(null, user, context);
}

This means that when you include the jti as a scope that value gets included in the resulting ID Token because it's obtained from that property. When going through delegation the jti claims seems to receive special treatment and it's ignored when you do the refresh. The use of delegation for refresh is discouraged, however, if you want to continue with that approach and just need a unique identifier within the JWT, if you use a non-reserved claim name you may be able to workaround this. For example, in a rule:

function (user, context, callback) {
  user.myjti = require('uuid').v4();
  callback(null, user, context);
}

Then on both requests include the myjti scope; a quick test showed that this works also when using delegation.