As of 28 July 2022, Auth0 will allow private, non-namespaced custom claims to be added to access and . These same claims will also be added to the response of the /userinfo endpoint. To learn more about the types of claims, read JSON Web Token Claims.
Despite allowing the use of private, non-namespaced custom claims, Auth0 strongly recommends the use of public, namespaced custom claims whenever possible. Public, namespaced custom claims are the best way to avoid any collision with future claims added to the standard.
Previously, Auth0 allowed only namespaced claims on access and ID tokens. With the migration to custom claims, non-namespaced claims can be used on , ID tokens, and the /userinfo endpoint of Auth0’s Authentication API.
Copy
Ask AI
// 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');};
All 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:
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.
This restriction is applied to the total payload size of all your custom claims. This includes the custom claims names as well as the associated values, whether they are public namespaced or private non namespaced.
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.
// 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);};
Copy
Ask AI
// 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);};
Copy
Ask AI
// 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);};
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.
// 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');};
Auth0 will restrict the creation of private, non-namespaced custom claims on access tokens in which the 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.
ID tokens are not concerned by this restriction.
Public namespaced custom claims are not concerned by this restriction.
The following audience will restrict the creation of private, non-namespaced custom claims:
https://YOUR_TENANT.auth0.com/api or https://YOUR_TENANT.auth0app.com/api
https://YOUR_TENANT.auth0.com/api/v2 or https://YOUR_TENANT.auth0app.com/api/v2
https://YOUR_TENANT.auth0.com/mfa or https://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:
// 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');};
The example below demonstrates the returned response with custom claims if the audience is not an Auth0 API:
Copy
Ask AI
-- A resource owner password flow POST https://{yourTenant}.auth0.com/oauth/tokengrant_type:passwordusername:***password:***client_id:***client_secret:***audience:https://{yourApi}.com -- Note the audience, that is a custom APIscope:openid profile
Copy
Ask AI
// 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"}
The example below demonstrates the returned response with custom claims not added with an Auth0 API audience:
Copy
Ask AI
-- A resource owner password flow POST https://{yourTenant}.auth0.com/oauth/tokengrant_type:passwordusername:***password:***client_id:***client_secret:***audience:https://{yourTenant}.auth0.com/api/v2/ -- This is an Auth0 audience scope:openid profile
Copy
Ask AI
// 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"}
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.
Before this migration, setting a namespaced custom claim with an Auth0 domain identifier would result in the claim showing up in the /userinfo response. This behavior disappears after the migration and such custom claims are completely ignored.
Copy
Ask AI
// 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');};
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.
If you add OIDC user profile claims to access tokens, the same scope restrictions apply as are applicable to ID tokens. For example, to add email claim to access tokens, the flow must be triggered with a scope that contains email.
You can add the following OIDC user profile claims to access tokens:
// 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');};
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 add-on or Protocol (WS-Fed).Example 1: Auth0 ignores attribute mapping when context.idToken.app_metadata is set with an empty object.
Copy
Ask AI
// an Auth0 Rulefunction (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);}
SAML response prior to this migration:
Copy
Ask AI
<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>
Example 2: The version of app_metadata in context.id_token takes precedence.
Copy
Ask AI
// an Auth0 Rulefunction (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);}
SAML Response prior to this migration:
Copy
Ask AI
<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>
SAML response with the upgraded behavior:
Copy
Ask AI
<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>
// 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');};
// 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');};
Copy
Ask AI
-- a call to /userinfo GET https://{yourTenant}.auth0.com/userinfoAuthorization: Bearer {yourAccessToken}
Copy
Ask AI
// the response from /userinfo{ "sub": ***, (...) "myIdTclaim": "this is a claim"}
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:
Copy
Ask AI
// 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
If you are using SAML2 add-on or Web Service Federation Protocol (Ws-Fed) attribute mapping, Avoid setting app_metadata or user_metadata claims on the context.idToken object. Replace these claims with name-spaced claims when possible:
Use a condition on the current protocol or on the current client to exclude statements setting app_metadata or user_metadata when the protocol is samlp or wsfed.
Copy
Ask AI
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" };}