A cloud-based identity and access management service for securing user authentication and resource access
Custom claims not appearing in Entra External ID (CIAM) tokens
Hi,
I’m using Microsoft Entra External ID (CIAM) and cannot get custom user attributes to appear as claims in the ID/access tokens for my SPA, even though they are configured in both Token configuration and Attributes & Claims.
I’d like to confirm whether this is a limitation/bug in CIAM, or if I’m missing a required step.
Environment
Tenant (External / CIAM)
Tenant ID: ac64f26c-0e2c-4c00-bea8-498e75af71e4
External login domain: digitalfleetsbx.ciamlogin.com
Application (SPA)
Name: digital-fleet-app-sbx
Application (client) ID: ef49379b-aab5-46e1-8455-9df6e5eebf9d
App registration object ID: dc9452f7-0e09-4aa0-b65a-3c2fa735d3d6
Service principal (Enterprise app) ID: e602245f-f435-49fc-aab7-d28b1f3acc8d
User flow
Name: 10-07-signup
Scopes requested by SPA
openid profile email api://ef49379b-aab5-46e1-8455-9df6e5eebf9d/access_as_user
Test user
UPN: ******@penske.com
Object ID: 016beece-9310-43e7-8f58-9297bc47e907
Tokens are issued via the CIAM endpoints, e.g.:
https://digitalfleetsbx.ciamlogin.com/ac64f26c-.../oauth2/v2.0/authorize?p=10-07-signup
https://digitalfleetsbx.ciamlogin.com/ac64f26c-.../oauth2/v2.0/token?p=10-07-signup
Custom attributes
In External Identities → Custom user attributes, I created:
Account ID – String, Custom
Org ID – String, Custom
These are backed by directory extensions:
extension_d44f0b8b98564d03a2100b0efad0ab96_accountid
extension_d44f0b8b98564d03a2100b0efad0ab96_orgid
Via Graph:
PATCH https://graph.microsoft.com/v1.0/users/{user-id}
{
"extension_d44f0b8b98564d03a2100b0efad0ab96_accountid": "d11411d2-e343-4b8a-bd45-d12e5659b15d",
"extension_d44f0b8b98564d03a2100b0efad0ab96_orgid": "ORG-999"
}
A GET for that user with $select confirms both extension attributes are present and have those values.
In the user flow 10-07-signup, both Account ID and Org ID are added under User attributes and the app digital-fleet-app-sbx is listed under Applications.
App registration configuration
In the app registration manifest:
"signInAudience": "AzureADMyOrg",
"acceptMappedClaims": true,
"isFallbackPublicClient": true
So the app is:
Single-tenant (AzureADMyOrg).
Explicitly configured to accept mapped claims.
In App registrations → Token configuration → Optional claims (for this app) I have:
account_id – Token type: ID
extension_d44f0b8b98564d03a2100b0efad0ab96_accountid – Token type: ID
extension_d44f0b8b98564d03a2100b0efad0ab96_orgid – Token type: ID
So optionalClaims is also asking for these attributes in the ID token.
Enterprise app (service principal) – Attributes & Claims
From the app registration, I click Managed application in local directory to open the service principal:
Service principal ID: e602245f-f435-49fc-aab7-d28b1f3acc8d
In Enterprise applications → digital-fleet-app-sbx → Single sign-on → Attributes & Claims, under Additional claims, I see:
AccountId
Token type: SAML, JWT
Source: `user.accountid` (directory extension `extension_d44f0b8b98564d03a2100b0efad0ab96_accountid`)
**OrgId**
Token type: **SAML, JWT**
Source: `user.orgid` (directory extension `extension_d44f0b8b98564d03a2100b0efad0ab96_orgid`)
**test-claim-1**
Token type: **JWT**
Source: `user.companyname` (built-in attribute, no extension)
There are no claim conditions configured on these claims.
The service principal shows no claimsMappingPolicies configured (so the Attributes & Claims portal config should be effective).
Actual tokens (problem)
I authenticate using the 10-07-signup user flow and scopes above (both via my SPA and via a raw authorize + token flow / jwt.ms). Example decoded tokens:
ID token payload:
{ "aud": "ef49379b-aab5-46e1-8455-9df6e5eebf9d", "iss": "https://ac64f26c-0e2c-4c00-bea8-498e75af71e4.ciamlogin.com/ac64f26c-0e2c-4c00-bea8-498e75af71e4/v2.0", "email": "******@myemail.com", "family_name": "Name", "given_name": "Name", "name": "My User Name", "oid": "016beece-9310-43e7-8f58-9297bc47e907", "preferred_username": "******@penske.com", "sid": "00aa9fc9-4d7e-9bd1-1221-6644b7774240", "sub": "SHKsG8uLhNgHovrrw3j_bO1SnSVXA9yIHBk9rcFp3OY", "tid": "ac64f26c-0e2c-4c00-bea8-498e75af71e4", "ver": "2.0" }
Access token payload:
{ "aud": "ef49379b-aab5-46e1-8455-9df6e5eebf9d", "iss": "https://ac64f26c-0e2c-4c00-bea8-498e75af71e4.ciamlogin.com/ac64f26c-0e2c-4c00-bea8-498e75af71e4/v2.0", "scp": "access_as_user", "name": "My User Name", "oid": "016beece-9310-43e7-8f58-9297bc47e907", "preferred_username": "******@myemail.com", "sub": "SHKsG8uLhNgHovrrw3j_bO1SnSVXA9yIHBk9rcFp3OY", "tid": "ac64f26c-0e2c-4c00-bea8-498e75af71e4", "ver": "2.0" }
What is missing from both tokens (ID and access):
AccountId
OrgId
test-claim-1
account_id
extension_d44f0b8b98564d03a2100b0efad0ab96_accountid
extension_d44f0b8b98564d03a2100b0efad0ab96_orgid
I’ve confirmed this:
From my SPA,
Using Run user flow → jwt.ms, and
With a raw curl auth code → token exchange.
In all cases, tokens contain standard OIDC claims (email, given_name, etc.), but none of the configured custom/optional claims.
What I’ve ruled out
App is single-tenant and has "acceptMappedClaims": true.
No claimsMappingPolicies attached to the service principal.
Custom attributes exist and have values on the user in Graph.
User flow includes the app and the custom user attributes.
Optional claims for the extension attributes are configured for the ID token.
I’m using fresh tokens (new sign-in flows), and see the same behavior via jwt.ms and pure HTTP calls.
Questions
In an Entra External ID (CIAM) external tenant (*.ciamlogin.com):
Should Enterprise app → Single sign-on → Attributes & Claims + "acceptMappedClaims": true work for adding custom directory extension attributes as claims in ID/access tokens?
Should **App registration → Token configuration → Optional claims** for those extension attributes work for ID tokens in this model?
Given the configuration above, is there any additional step required for CIAM tenants, or is this likely a platform issue in my external tenant?
- As a workaround, I’m considering a custom authentication extension (tokenIssuanceStart) that calls Graph, reads the extension attributes, and uses
tokenIssuanceStart.setClaimsto injectaccountId/orgIdinto the token.- Is that currently the recommended way to guarantee custom attributes appear in tokens for CIAM tenants, or should the “standard” Attribute & Claims / optionalClaims path already work here?