Custom Claims Migration
As of 28 July 2022, Auth0 will allow private, non-namespaced custom claims to be added to access and ID tokens. These same claims will also be added to the response of the /userinfo
endpoint. To learn more about the types of JWT claims, read JSON Web Token Claims.
Example
Previously, Auth0 allowed only namespaced claims on access and ID tokens. With the migration to custom claims, non-namespaced claims can be used on access tokens, ID tokens, and the /userinfo
endpoint of Auth0's Authentication API.
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// public namespaced custom claims
api.accessToken.setCustomClaim('https://myDomain.com/myClaim', 'this is a public, namespaced claim');
api.idToken.setCustomClaim('https://myDomain.com/myClaim', 'this is a public, namespaced claim');
// non-namespaced custom claims
api.accessToken.setCustomClaim('myClaim', 'this is a private, non namespaced claim');
api.idToken.setCustomClaim('myClaim', 'this is a private, non namespaced claim');
};
Was this helpful?
Affected flows
All OpenID Connect (OIDC) flows that Auth0 supports are affected by this migration. To review the list of flows, read Authentication and Authorization Flows.
The following features are also affected:
The following features are affected only when used along with Auth0 Rules and attribute mapping:
Restrictions
Maximum token size
Auth0 will restrict the custom claims payload to a maximum of 100KB. It is important to make sure the payload does not exceed this limit, otherwise the authentication transaction will fail with an error. We recommend you review your use of extensibility code (i.e. Rules, Hooks, or Actions). In particular, review large payloads from external APIs.
To avoid errors, Auth0 recommends using the smallest token payload necessary for your application to operate. You may need to strip any properties that are not crucial before you set the custom claim value.
The limit of 100KB is applied to access tokens and ID tokens separately. For example, an access token of 100KB and an ID token of 100KB can be returned in the same transaction.
Examples
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// fetching a payload that is superior to 100KB
const aHeavyPayload = getHeavyPayload();
// this will fail the authentication
api.idToken.setCustomClaim('myclaim', aHeavyPayload);
};
Was this helpful?
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// fetching a payload that is 50KB
const a50KBPayload = getHeavyPayload();
// fetching another payload that is 50KB
const another50KBPayload = getHeavyPayload();
// this will fail the authentication
api.idToken.setCustomClaim('myclaim', a50KBPayload);
api.idToken.setCustomClaim('https://myDomain.com/myClaim', another50KBPayload);
};
Was this helpful?
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// fetching a payload that is 50KB
const a50KBPayload = getHeavyPayload();
// fetching another payload that is 50KB
const another50KBPayload = getHeavyPayload();
// this will succeed
api.accessToken.setCustomClaim('myclaim', a50KBPayload);
api.idToken.setCustomClaim('https://myDomain.com/myClaim', another50KBPayload);
};
Was this helpful?
Restricted claims
Auth0 will restrict the customization of claims used in the OIDC or OAuth2 standards or claims for internal use. Any attempt to modify one of these claims will be ignored. The transaction won't fail, but the claim will not be added to tokens. Auth0 recommends using a public, namespaced claim.
acr
act
active
amr
at_hash
ath
attest
aud
auth_time
authorization_details
azp
c_hash
client_id
cnf
cty
dest
entitlements
events
exp
groups
gty
htm
htu
iat
internalService
iss
jcard
jku
jti
jwe
jwk
kid
may_act
mky
nbf
nonce
object_id
org_id
org_name
orig
origid
permissions
roles
rph
s_hash
sid
sip_callid
sip_cseq_num
sip_date
sip_from_tag
sip_via_branch
sub
sub_jwk
toe
txn
typ
uuid
vot
vtm
x5t#S256
Example
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// this will be ignored
api.accessToken.setCustomClaim('roles', 'this is a role, but Auth0 will ignore it');
// this will succeed, and appear in the token
api.idToken.setCustomClaim('https://myDomain.com/roles', 'this is a role');
};
Was this helpful?
Restricted token audience
Auth0 will restrict the creation of private, non-namespaced custom claims on access tokens in which the audience is an Auth0 API. Any attempt to set a private, non-namespaced custom claim on an access token where the audience is an Auth0 API will be ignored. The transaction will not fail, but the claim will not be added to your token. Auth0 recommends not setting custom claims on tokens that are to be consumed by Auth0’s APIs.
The following audience will restrict the creation of private, non-namespaced custom claims:
https://YOUR_TENANT.auth0.com/api
orhttps://YOUR_TENANT.auth0app.com/api
https://YOUR_TENANT.auth0.com/api/v2
orhttps://YOUR_TENANT.auth0app.com/api/v2
https://YOUR_TENANT.auth0.com/mfa
orhttps://YOUR_TENANT.auth0app.com/mfa
The exception to this restriction is the Auth0 /userinfo
audience. Private, non-namespaced custom claims are allowed on the following audience:
https://YOUR_TENANT.auth0.com/userinfo
https://YOUR_TENANT.auth0app.com/userinfo
Examples
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// these will be ignored if the audience is an Auth0 audience
api.accessToken.setCustomClaim('myATclaim', 'this is a claim');
api.accessToken.setCustomClaim('https://myDomain.com/myATclaim', 'this is a claim');
// these will succeed, they are not concerned by the audience restriction
api.idToken.setCustomClaim('myIdTclaim', 'this is a claim');
api.idToken.setCustomClaim('https://myDomain.com/myIdTclaim', 'this is a claim');
};
Was this helpful?
The example below demonstrates the returned response with custom claims if the audience is not an Auth0 API:
-- A resource owner password flow
POST https://{yourTenant}.auth0.com/oauth/token
grant_type:password
username:***
password:***
client_id:***
client_secret:***
audience:https://{yourApi}.com -- Note the audience, that is a custom API
scope:openid profile
Was this helpful?
// The Access token returned by Auth0
{
"iss": "https://{yourTenant}.auth0.com/",
"sub": ***,
"aud": [
"https://{yourApi}.com",
"https://{yourTenant}.auth0.com/userinfo"
],
"iat": 1655283444,
"exp": 1655369844,
"azp": ***,
"scope": "openid profile",
"gty": "password",
// The custom claims were added, because the Audience is not an Auth0 audience
"myATclaim": "this is a claim",
"https://{yourDomain}.com/{myATclaim}": "this is a claim"
}
Was this helpful?
The example below demonstrates the returned response with custom claims not added with an Auth0 API audience:
-- A resource owner password flow
POST https://{yourTenant}.auth0.com/oauth/token
grant_type:password
username:***
password:***
client_id:***
client_secret:***
audience:https://{yourTenant}.auth0.com/api/v2/ -- This is an Auth0 audience
scope:openid profile
Was this helpful?
// The Access token returned by Auth0
{
"iss": "https://{yourTenant}.auth0.com/",
"sub": ***,
"aud": [
"https://{yourTenant}.auth0.com/api/v2/",
"https://{yourTenant}.auth0.com/userinfo"
],
"iat": 1655283444,
"exp": 1655369844,
"azp": ***,
"scope": "openid profile",
"gty": "password",
// The public namespaced custom claims was added, because it is not concerned by this restriction
// However, the private non-namespaced custom claim {myATclaim} was ignored
"https://mydomain.com/{myATclaim}": "this is a claim"
}
Was this helpful?
Restriction on Auth0 and Webtask namespaces
Auth0 will restrict the creation of namespaced custom claims with an Auth0 domain as namespace identifier. Auth0 domains are:
auth0.com
webtask.io
webtask.run
Any attempt to set a namespaced custom claim on a token with one of the domains above as an identifier will be ignored. The transaction will not fail, but the claim will not be added to your token.
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// none of these will be added to tokens nor to /userinfo response
api.idToken.setCustomClaim('https://example.auth0.com', 'this is a claim');
api.idToken.setCustomClaim('https://example.webtask.io', 'this is a claim');
api.idToken.setCustomClaim('https://example.webtask.run', 'this is a claim');
};
Was this helpful?
OIDC user profile claims
Auth0 will now allow OIDC user profile claims to be added to access tokens.
Attempts to add OIDC user profile claims to the access token were silently ignored prior to this migration. With the updated behavior, access tokens will contain these OIDC user profile claims.
You can add the following OIDC user profile claims to access tokens:
address
birthdate
email
email_verified
family_name
gender
given_name
locale
middle_name
name
nickname
phone_number
phone_number_verified
picture
preferred_username
profile
updated_at
website
zoneinfo
Example
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// this was ignored so far. From this migration on, the claim will be added to access tokens
// if the scope contains 'email'
api.accessToken.setCustomClaim('email', 'myemail@domin.com');
// this was ignored so far. From this migration on, the claim will be added to access tokens
// if the scope contains 'profile'
api.accessToken.setCustomClaim('family_name', 'A family name');
};
Was this helpful?
SAML2 add-on and Web Service Federation Protocol (WS-Fed) attribute mapping with Auth0 Rules
Similar to using Auth0 Rules to make changes to the user object, app_metadata
or user_metadata
pre-migration claims also merge contents when the claim is set on the context.idToken
object and the names conflict. To learn more about the object properties, read User Object Properties In Rules.
Using custom claims, however, Auth0 gives precedence to the claim that was set on the context.idToken object
.
This change impacts Auth0 Rules that set app_metadata
and user_metadata
via context.id_token
(assigning objects to them) and, at the same time, uses these fields in attribute mapping for SAML add-on or Web Service Federation Protocol (WS-Fed).
Example 1: Auth0 ignores attribute mapping when context.idToken.app_metadata
is set with an empty object.
// an Auth0 Rule
function (user, context, callback) {
user.app_metadata.a_claim = 'This is a claim';
user.app_metadata.another_claim = 'This is a another claim';
context.samlConfiguration = context.samlConfiguration || {};
context.samlConfiguration.mappings = {
"a_claim": "app_metadata.a_claim",
"another_claim": "app_metadata.another_claim"
};
context.idToken.app_metadata = {};
return callback(null, user, context);
}
Was this helpful?
SAML response prior to this migration:
<samlp:Response>
(...)
<saml:Assertion>
(...)
<saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<saml:Attribute Name="a_claim" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">
This is a claim
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="another_claim" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">
This is a another claim
</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
Was this helpful?
SAML response with the upgraded behavior:
<samlp:Response>
(...)
<saml:Assertion>
(...)
<saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</saml:Assertion>
</samlp:Response>
Was this helpful?
Example 2: The version of app_metadata
in context.id_token
takes precedence.
// an Auth0 Rule
function (user, context, callback) {
user.app_metadata.a_claim = 'This is a claim';
user.app_metadata.another_claim = 'This is a another claim';
context.samlConfiguration = context.samlConfiguration || {};
context.samlConfiguration.mappings = {
"a_claim": "app_metadata.a_claim",
"another_claim": "app_metadata.another_claim",
"claim_set_via_id_token": "app_metadata.claim_set_via_id_token"
};
context.idToken.app_metadata = {
claim_set_via_id_token: "This is a claim which was set via context.idToken"
};
return callback(null, user, context);
}
Was this helpful?
SAML Response prior to this migration:
<samlp:Response>
(...)
<saml:Assertion>
(...)
<saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<saml:Attribute Name="a_claim">
<saml:AttributeValue xsi:type="xs:anyType">
This is a claim
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="another_claim">
<saml:AttributeValue xsi:type="xs:anyType">
This is a another claim
</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="claim_set_via_id_token">
<saml:AttributeValue xsi:type="xs:anyType">
This is a claim which was set via context.idToken
</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
Was this helpful?
SAML response with the upgraded behavior:
<samlp:Response>
(...)
<saml:Assertion>
(...)
<saml:AttributeStatement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<saml:Attribute Name="claim_set_via_id_token" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<saml:AttributeValue xsi:type="xs:string">
This is a claim which was set via context.idToken
</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
Was this helpful?
Add private, non-namespace claims to tokens
You can now add private, non-namespaced custom claims to the payload of access and ID tokens.
Example
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// previously ignored
// From this migration on, the claim will be added to access tokens
api.accessToken.setCustomClaim('myATclaim', 'this is a claim');
// previously ignored
// From this migration on, the claim will be added to ID tokens
api.idToken.setCustomClaim('myIdTclaim', 'this is a claim');
};
Was this helpful?
Private, non-namespace claims to /userinfo
Auth0 now returns private, non-namespaced custom claims in the /userinfo response when set on ID tokens.
Example
// an Auth0 action
exports.onExecutePostLogin = async (event, api) => {
// this was ignored so far.
// From this migration on, this claim will be returned in /userinfo
api.idToken.setCustomClaim('myIdTclaim', 'this is a claim');
};
Was this helpful?
-- a call to /userinfo
GET https://{yourTenant}.auth0.com/userinfo
Authorization: Bearer {yourAccessToken}
Was this helpful?
// the response from /userinfo
{
"sub": ***,
(...)
"myIdTclaim": "this is a claim"
}
Was this helpful?
Actions
Review tenant logs
First, check your tenant logs for deprecation notices to determine whether your tenant is affected by the migration.
Navigate to Auth0 Dashboard > Monitoring > Logs.
Search the logs for
type: depnote AND description: *Custom*claims*
.
Example
Provided below is an example deprecation log that is generated whenever extensibility code triggers.
{
"date": "2022-06-28T08:12:52.084Z",
"type": "depnote",
"description": "Custom claims must be namespaced: This feature is being deprecated. Please see details.feature of this log for more information.",
"connection_id": "",
"client_id": ****,
"client_name": ****,
"details": {
"feature": {
"grant": "password",
"access_token_claims_to_be_allowed": [
"myclaim"
],
"access_token_claims_to_be_disallowed": [
"gty"
],
"id_token_claims_to_be_allowed": [
"myclaim"
],
"id_token_claims_to_be_disallowed": [
"gty"
],
"id": "legacy_custom_claims",
"name": "Custom claims must be namespaced when they are added through rules / actions / hooks."
}
},
"log_id": ****,
"_id": ****,
"isMobile": false,
"user_agent": "Other 0.0.0 / Other 0.0.0",
"id": ****
}
Was this helpful?
Fix Auth0 rules for SAML2 add-on and Web Service Federation Protocol (Ws-Fed)
If you set app_metadata
or user_metadata
claims on the context.idToken
object using SAML2 add-on or Web Service Federation Protocol (Ws-Fed) with Auth0 Rules along with attribute mapping, you will need to update your configuration to adjust for how Auth0 evaluates conflicting claim names between these objects. There are several possible fixes:
Make sure that the code of your Auth0 Rule always gives precedence to the content of objects set on
context.id_token
:// my_claim will be ignored, this line of code is not relevant anymore, // prefer setting my_claim on `context.idToken` user.app_metadata.my_claim = 'a value'; // this version of app_metadata will take precedence over any other change context.idToken.app_metadata = { another_claim: 'another value' }; // Only `another_claim` will appear in SAML/WsFed responses
Was this helpful?
/If you are using SAML2 add-on or Web Service Federation Protocol (Ws-Fed) attribute mapping, Avoid setting
app_metadata
oruser_metadata
claims on thecontext.idToken
object. Replace these claims with name-spaced claims when possible:context.idToken['https://mydomain.com/app_metadata'] = { my_claim: 'my claim' };
Was this helpful?
/Use a condition on the current protocol or on the current client to exclude statements setting
app_metadata
oruser_metadata
when the protocol issamlp
orwsfed
.if (!['samlp', 'wsfed'].includes(context.protocol)) { context.idToken.app_metadata = { claim_set_via_id_token: "This is a claim which was set via context.idToken" }; }
Was this helpful?
/
Disable legacy behavior
Navigate to Auth0 Dashboard > Tenant Settings > Advanced and search for Migrations.
Use the toggle to disable Custom claims must be namespaced.