Hitting the Claims Mapping Policy 50 Limit in Entra ID

Share

Whoah…. okay, maybe this title isn’t as long as my SAML certificate notification post, but the topic sure likes to sprawl.

Let’s say you’re mid–SSO integration. The app isn’t a simple gallery walkthrough. It’s the kind that used to live on AD FS with a claim rule set that looked like a novel. You’re past NameID. Past the happy-path attributes. You’ve decided the portal Attributes & Claims blade isn’t going to cut it — too many transforms, too much repeatable config — so you reach for a Claims Mapping Policy.

You wire up the JSON. You assign it to the service principal. You test.

And then… half the claims you carefully defined just don’t show up in the assertion.

No hard error. No “you exceeded the limit” banner. Just missing attributes and a Monday morning ticket that says SSO is “randomly” broken.

Welcome to 50.

Claims Mapping Policy vs the portal (don’t mix them up)

Before we talk about the ceiling, we need to name the tool correctly.

Portal: Attributes & Claims

Enterprise app → Single sign-on → Attributes & Claims. Clicky UI. Claim conditions. Group claims. Great for most SaaS apps.

There’s also Custom Claims Policy (Preview) in Microsoft Graph, which shares the same underlying config as that portal blade — so you can manage those claims via Graph or the admin center.

Claims Mapping Policy (CMP)

A first-class policy object (claimsMappingPolicy) you create in Graph/PowerShell and assign to a service principal. One policy can be linked to multiple apps. You define ClaimsSchema, optional ClaimsTransformation / ClaimsTransformations, optional GroupFilter, IncludeBasicClaimSet, and friends in JSON.

Microsoft is blunt about precedence:

Claims Mapping Policy supersedes both Custom Claims Policy and the claims customization offered through the Microsoft Entra admin center.

Once a CMP is assigned, tokens for that app ignore the portal / Custom Claims Policy config. The Attributes & Claims blade often shows something like “This configuration was overwritten by a claim mapping policy created via Graph/PowerShell.” That’s not a bug — that’s the policy winning.

Notes:

  • CMP is great when you need automation, reusable policy, or claim shapes the UI doesn’t express cleanly.
  • CMP is not great if you still want colleagues editing the same map in the portal later. Pick a source of truth and stick to it.

Docs: Claims customization · Claims customization using PowerShell · claimsMappingPolicy resource

The 50 that bites: ClaimsSchema

Here’s the limit that stopped me cold on a recent SSO integration.

From the Graph claimsMappingPolicy docs:

Policy property Documented max What happens over the limit
ClaimsSchema 50 claim schema entries Entries after 50 are ignored — not emitted
ClaimsTransformation 50 transformations Transformations after 50 are ignored

Read that again: ignored. Not rejected on create. Not called out in the token with a friendly overage claim. Just… absent.

So if you migrate a chunky AD FS rule set into one giant ClaimsSchema array — employee ID, department, cost center, site code, three flavors of NameID logic, a dozen app-specific URIs, plus intermediate schema entries that only exist to feed transformations — you can sail past 50 without the create API yelling at you. The policy saves. The sign-in “works.” The 51st claim never leaves the building.

That’s the failure mode. Silent truncation of your carefully authored map.

Why 50 arrives faster than you think

A few ways the schema budget evaporates:

  1. One AD FS issuance rule ≠ one Entra schema entry. Transforms often need an input schema entry and an output schema entry that points at Source: transformation.
  2. Duplicates. Emitting the same user attribute under two SAML URIs still costs two schema rows.
  3. “Just in case” claims. Porting every historical AD FS claim “because the vendor might need it” is how you invent a limit incident.
  4. Basic claim set confusion. IncludeBasicClaimSet: true still emits the basic set in addition to your schema — it does not give you free schema slots. Setting it false means you must put anything you still need into ClaimsSchema yourself (and those count).

The other ceilings (still real, still adjacent)

CMP’s 50 isn’t the only quota in this neighborhood. Keep them straight so you don’t debug the wrong wall:

Feature Limit / rule Why you care
CMP ClaimsSchema 50 entries; extras ignored This post’s villain
CMP ClaimsTransformation 50 transforms; extras ignored Same silent failure mode
Portal / SAML regex transforms Max 20 total regex replacements Documented under SAML claims customization
Portal claim conditions – groups 50 unique groups across all claims on the app Different “50” — conditional claim sources
SAML groups claim 150 memberships (incl. nested in the general case) Missing groups / overage
JWT groups claim 200 Same problem, higher ceiling
Group filtering Only if user in ≤ 1,000 groups Filter skipped → overage
Groups assigned to the app Direct membership only Nesting surprises
Portal transforms per claim Max 2 chained UI constraint

Group claims and claim conditions still matter on SSO projects — especially if the app wants a groups dump and a CMP. Just don’t conflate “50 unique groups for claim conditions” with “50 ClaimsSchema entries on a CMP.” Same number. Different knobs.

Docs: Entra service limits · Configure group claims · Customize SAML token claims

How CMP talks to groups

CMP can include a GroupFilter in the policy definition: match on displayname or samaccountname, with prefix / suffix / contains. Useful for shrinking what goes into the groups claim.

That filter is still subject to the broader group-claim rules (150 SAML / 200 JWT, filter eligibility ≤ 1,000 memberships, assigned-groups = direct only). CMP doesn’t give you a free pass around those.

If you’re doing multi-pattern group filtering, the native filter is effectively one pattern string — regex-style transforms on a customized group claim name are the usual escape hatch (and then you’re back in regex-replacement budget land).

Assigning the policy (the part people forget to verify)

High-level flow:

  1. Create the policy: New-MgPolicyClaimMappingPolicy (or POST /policies/claimsMappingPolicies).
  2. Assign it to the enterprise app’s service principal: New-MgServicePrincipalClaimMappingPolicyByRef.
  3. Confirm assignment: GET .../servicePrincipals/{id}/claimsMappingPolicies.
  4. For APIs/OIDC resource apps, also mind acceptMappedClaims / token version on the application object when you’re emitting mapped claims into access tokens.
  5. Test the actual token (SAML assertion or JWT at jwt.ms). Do not trust the Attributes & Claims blade as a mirror of the CMP — it isn’t one.

Scopes you’ll need typically include Policy.ReadWrite.ApplicationConfiguration (and related read scopes). Claims-mapping policies can only be assigned to service principals — not set as an org-wide default.

Docs: Claims customization using PowerShell · Customize claims with CMP in Microsoft Graph

What breaks in the wild

Patterns that look a lot like “SSO is flaky” but are really quota-shaped:

  • Claims 51+ silently missing → ClaimsSchema over 50; policy created fine; assertion incomplete.
  • Transforms that “don’t run” → past the 50 transformation entries, or the output schema row that should emit them got truncated.
  • Portal edits do nothing → CMP is assigned and superseding; you’re editing a ghost UI.
  • AD FS lift-and-shift blowouts → every historical issuance/transform rule dumped into one JSON → 50 arrives on day one.
  • Groups still empty / overage → you fixed CMP, but the user crossed 150 (SAML) or the app ignored overage / can’t call Graph.
  • “Groups assigned to the app” + nesting → user nested under an assigned group; direct-only rule bites.

Practical ways out when you hit 50

If the CMP ClaimsSchema wall is the problem, these are the moves that actually help:

  1. Count before you commit. Script a check: length of ClaimsSchema and ClaimsTransformation arrays. Treat 50 as a hard design budget, not a surprise.
  2. Consolidate. One claim with a clear URI beats three legacy aliases “for compatibility” unless the SP truly needs all three.
  3. Drop dead weight. If the vendor’s SAML tracer never consumed a claim in UAT, it doesn’t need a schema row in prod.
  4. Prefer app roles for authorization when the app can take them — smaller tokens, clearer semantics, less pressure to invent claim sprawl (nested groups still aren’t in play for app-role assignment via groups).
  5. Emit Object IDs for groups, not display names, when groups are still required — and prefer groups assigned to the application so you stay intentional under 150.
  6. Simplify transforms. Every Join/Extract/RegexReplace chain tends to cost schema + transformation slots. Sometimes a synced extension attribute computed upstream is cheaper than three transforms in the token pipeline.
  7. Don’t duplicate schema entries for the same source just to rename things twice — pick the claim type the app documents.
  8. When the app can call Microsoft Graph, let it fetch full membership / profile data instead of stuffing the universe into SAML.
  9. Split responsibility thoughtfully. One reusable CMP across similar apps is fine; one monster policy that tries to satisfy five unrelated vendors is how you meet 50 with style.
  10. Stay on CMP or stay on the portal — not both. If you need GUI + Graph on the same config, look at Custom Claims Policy (Preview) instead of CMP; know that CMP will keep winning (and locking the blade) if it’s assigned.

Quick reference

Question Short answer
What’s the CMP schema limit? 50 ClaimsSchema entries
What’s the CMP transform limit? 50 ClaimsTransformation entries
What happens at 51? Silent ignore — not in the token
Does CMP override the portal? Yes — supersedes Attributes & Claims and Custom Claims Policy
Where do you assign it? To the app’s service principal
Is portal claim-conditions “50 groups” the same limit? No — related neighborhood, different feature
SAML groups ceiling? 150 (JWT 200)

Final thoughts

Claims Mapping Policy is powerful. It’s also quota-shaped — and unlike a certificate expiry, it won’t email you 60 / 30 / 7 days before you have a bad time.

If you treat Entra like “AD FS but in the cloud, paste every issuance rule into JSON,” you’ll meet 50 the hard way: a policy that saves, a sign-in that “works,” and an assertion that’s missing the one attribute the app actually authorizes on.

If you treat ClaimsSchema like a budget — count entries, consolidate early, prefer app roles or Graph where you can, and keep transforms intentional — your SSO integrations stay about coffee instead of hunting ghosts in a SAML tracer.

Stay curious. Count your schema entries. And maybe don’t port the 2008 claim rule named EmitEverythingIncludingTheKitchenSink without a second look.

References