Security

Testing Group Claim Size Limits Before an Agent Owns Too Many

Antonio Evans9 min read

A developer stands at a whiteboard in a staging environment, drawing a binary search tree with group icons at each node.
Key points

Key points

  1. 1Grow an agent's group claim in a staging identity provider that mirrors production, doubling group counts until the token or assertion is rejected, then binary search between the last pass and first fail.
  2. 2Compare the parsed claim count against the provisioned count, because an identity provider can silently omit the groups claim instead of returning an error.
  3. 3Identify which ceiling you hit: an identity provider group-count cap, a service provider size cap, a proxy header limit, or attribute truncation, since each fails differently.
  4. 4At the ceiling, shrink the claim with application-assigned groups or filters, or split the agent into several identities with narrower scopes.

Why the group claim is the fragile part of an agent's identity

An agent that calls tools across many systems usually gets authorization from a group or role list embedded in a SAML assertion or an OIDC token. That works for a human in a handful of teams. It fails quietly for an agent holding dozens or hundreds of group memberships, because the claim grows linearly while the limits protecting the identity provider and the service provider are fixed.

The limits are documented. Microsoft Entra ID emits at most 150 groups in a SAML assertion and 200 in a JWT, including nested groups, and when an identity exceeds that the provider omits group claims from the token entirely rather than trimming them Microsoft's group claims guidance. AWS rejects SAML responses larger than 128 KB, and Okta names "too many groups or roles" as the typical cause of that rejection Okta's support note on the AWS size limit. CA SiteMinder has truncated attribute values at a default of 1024 characters and logged a "Response attribute will be trimmed" warning that then breaks downstream authorization Broadcom's truncation article.

The asymmetry matters. A size limit produces a loud HTTP 400 with a clear error string. A count limit produces a valid token carrying no groups, which surfaces as "agent has no permissions" or fails with a misleading message. A harness that only watches for red errors will miss the case that hurts most.

Agents make this worse than humans because memberships accumulate without anyone consciously adding them. Identity governance research summarized by the Cloud Security Alliance found non-human identities outnumbering human ones by more than 90 to 1 in many organizations, with few organizations able to trace an agent's actions back to a human sponsor the CSA Agent Identity Governance Framework. The same paper notes that agents spawn child agents dynamically, each needing its own credentials and scope, and each another identity that can drift toward a claim limit.

What you are actually testing for

Decide which failure you are hunting before writing any code. Four distinct ceilings behave differently.

CeilingWhere it livesTypical symptomHow to confirm it
Group count capIdentity provider token builderGroups claim missing or replaced by an overage flagDecode the token and check for the claim, not just the status code
Payload size capService provider or receiving APIHTTP 400, "invalid assertion", "response too large"Compare raw request body size against the documented limit
Attribute truncationIdP attribute store or handlerValid token, fewer groups than provisioned, trailing groups lostDiff provisioned groups against arrived groups
HTTP header limitReverse proxy in front of the SP400 or 431 on POST, only with large cookiesReproduce with a smaller cookie set or direct backend call

The last row is the one most teams forget. Web servers enforce independent limits regardless of what the IdP emits; the Broadcom guidance lists roughly 8 KB for Apache, 4 to 8 KB for Nginx, 8 to 16 KB for IIS and 8 to 48 KB for Tomcat. Send a large response via HTTP-Redirect and the assertion travels as a URL parameter, where URL length limits produce failures that look nothing like a size problem.

Protocol choice changes the number too. SP-initiated flows correlate the response to an AuthnRequest while IdP-initiated responses carry less correlation state, and validation differs between them Scalekit's comparison of the two flows. If production uses a client-credentials OIDC path but staging exercises an interactive SAML POST, you are measuring the wrong ceiling.

Stage zero: build a production-shaped identity provider

A test against a toy IdP proves nothing, because the limits live in configuration you have not replicated. Match production on four dimensions:

  1. Same IdP product and tier, with identical group claim configuration. If production sends all groups, staging sends all groups.
  2. Same baseline membership, because a search starting from zero reveals the ceiling but not your headroom.
  3. Same token lifetimes, signing and encryption settings, since encryption inflates payload size.
  4. Same clock discipline. Assertions carry NotBefore and NotOnOrAfter values, and a few minutes of skew between IdP and SP produces intermittent rejections that mimic nothing else the SAML clock skew explainer. A flaky "randomly fails at 40 groups" result is usually skew.

Keep the harness deterministic. Generate groups from a template with fixed-length names so each addition costs a predictable number of bytes. s3-read and finance-accounts-payable-emea-readonly differ by 30 characters, and at 600 groups that is 18 KB, enough to move you across a threshold. Record the byte delta per group and you can predict the ceiling rather than only discovering it.

The NIST digital identity guidelines are worth reading for framing: they treat authenticator binding and lifecycle state as things requiring records of when binding happened and deliberate handling when it changes NIST SP 800-63B-4. An agent's group membership is exactly that lifecycle state. The 2017 edition of the same publication is withdrawn and superseded in its entirety, so consult the current version the superseded SP 800-63B.

The doubling phase: bracket the ceiling fast

Phase one doubles the count each iteration and stops at the first failure.

counts = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 768, 1024]

for n in counts:
    provision the agent identity with exactly n groups
    request the token or start the SSO flow
    record:
      http_status
      raw_payload_bytes        # body size, or extracted assertion/token size
      claim_present (bool)     # did the groups claim survive?
      claim_count              # how many groups arrived
      first_error_string
    if http_status >= 400 or claim_count != n:
        first_failure = n
        last_success  = previous n
        break

Run each count three times. Identity providers cache, service providers cache, and issuance sometimes depends on which node answers. A count that fails twice and passes once is a signal worth recording.

Use two stop conditions, not one. Stop on request failure, and stop when the arrived claim count differs from what you provisioned. The second catches silent omission. If you provision 151 and receive 150, you found a count cap, not a size cap.

Send traffic through the real path. Post the assertion to your actual SP endpoint through the actual proxy chain with realistic cookie and header sizes. A curl call straight to the Assertion Consumer Service bypasses the layer most likely to truncate.

The binary search: pin the exact ceiling

With a bracket in hand, bisection needs at most eight more probes across a range of 256, each one identity change plus one token request.

def find_ceiling(passes, fails, probe, max_rounds=12):
    lo, hi = passes, fails          # lo passes, hi fails, invariant
    history = []
    for _ in range(max_rounds):
        if hi - lo <= 1:
            break
        mid = (lo + hi) // 2
        ok, observed = probe(mid)   # ok = no error AND claim_count == mid
        history.append((mid, ok, observed))
        if ok:
            lo = mid
        else:
            hi = mid
    return lo, hi, history

The invariant is that lo always passes and hi always fails. When the gap reaches one, lo is the ceiling: the largest count yielding a complete, accepted claim. Log the history, because the failure shape identifies which ceiling you found.

  • Errors above N, complete claims below N. A size limit. The group ceiling holds only while name lengths hold, so record the byte size at the ceiling as the real budget.
  • Success above N with no groups claim, or with 150 groups when you sent 151. A count cap with silent omission. The number is the limit, and your authorization logic must handle an overage indicator rather than read an empty list as "no access."
  • Success above N with a stable prefix of groups. Truncation. Worse than omission, because the app may grant access on surviving groups while the rest vanished.
  • Failures alternate rather than track count. Suspect skew, caching, or IdP nodes with different configuration. Fix the harness before trusting a number.
  • Failure only through the proxy, never direct. You found the web server header limit. Test with the proxy in the loop and record the byte size where it breaks.

Bisection assumes monotonicity, which normally holds with one exception. Some IdPs switch strategy when a claim is oversized, truncating and adding a link to fetch groups via an API, so the same oversized claim can yield a valid token under one configuration and a hard error under another. Confirm which behavior yours exhibits, and re-run the top of the failing range once with a clean cache.

Interpreting the failures you will actually see

ObservedMost likely causeImmediate action
HTTP 400 on the ACS endpoint, body over the documented SP limitPayload size capReduce claim width before adding groups
Token valid, groups absent, overage flag presentIdP group count capMove authorization to app roles or resolve groups via API
Token valid, group array shorter than provisionedAttribute truncationRaise the attribute limit or narrow the attribute
400 or 431 with large cookies onlyProxy header limitPost the assertion rather than redirect, or raise the buffer
Intermittent "assertion expired" at moderate countsClock skewCheck NTP and allowed skew settings
Errors only for group names with special charactersEncoding expansionMeasure encoded size, not character count

The last row deserves emphasis. Every character needing escaping in XML or a URL expands the payload. Groups named with ampersands, spaces, or non-ASCII characters can cross a threshold with far fewer memberships than a test using clean names like grp-0001 suggested. Generate the worst case your directory allows, not the tidiest.

What to do at the ceiling

You have four levers. They differ in cost, availability, and how much they change your agent's design.

Shrink each group's representation. Use object identifiers rather than display names where the protocol allows, and drop redundant prefixes. A GUID is 36 characters; a fully qualified path can exceed 60. At 200 groups that is nearly 5 KB, a fixed amount of headroom you can state as a number.

Change what you send, not how many groups exist. Microsoft recommends restricting claims to groups assigned to the application, or using a group filter, and recommends application roles over groups for new applications the platform supports Microsoft's guidance on group claims and app roles. The principle generalizes: an agent should receive the groups that matter to its task, not every group it belongs to. This is usually the correct structural fix.

Use nesting or indirection. If the platform supports nested groups or a directory role standing in for a set of resource groups, the claim can stay flat while real membership grows. Microsoft counts nested groups toward its limits, so nesting only helps if it collapses many leaf groups into few emitted ones. Verify against the provider rather than assuming.

Split the agent into narrower identities. This is the fix that scales once the others are exhausted. Replace one overprivileged agent with several, each scoped to bounded resources, each with its own membership and owner. This aligns with least privilege and with just-in-time access, which the CSA framework positions as replacing standing agent privileges with intent-declared, time-bound grants.

The decision rule: if the measured ceiling sits comfortably above current membership plus a growth margin, do nothing and monitor. If you are within 20 percent of it, change what you send. If you are over it, or will be within one planning cycle, split the identity. A single identity with 400 memberships is a blast radius, not just a token problem.

Provisioning discipline keeps you off the ceiling. Treating agents as managed identities with automated create, update and deprovision steps, rather than hand-built service accounts, is the difference between a group list that grows deliberately and one nobody pruned LoginRadius on SCIM provisioning for agents. An inventory recording each identity's credential type, scope, owner, and deprovisioning criteria is the minimum for noticing drift Sweet Security's non-human identity guidance. Whether an agent should carry a human-like identity or a workload identity also changes how many groups it plausibly needs Aembit's discussion of agent identity types.

Teams running many agents against different model providers usually face identity work and model-access work together. Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible API key with prepaid USD pricing, keeping the agent-to-model path consistent while you rework authentication: https://quantaintelligence.ai/inference.

Making this a standing check, not a one-off

A test that runs once is already stale. Membership grows, group names change, and someone edits a claim configuration.

  • Run the full search when the IdP claim configuration, the SP's documented limit, the group naming convention, or the provisioning source changes.
  • Run a nightly headroom check. Provision the agent at 80 percent of the recorded ceiling and assert the full claim arrives. One token request catches drift.
  • Store the ceiling with its context. "412 groups" means nothing without the IdP, the claim configuration, average group name length, and which SP's limit applied.
  • Alert when claim count falls below membership count. This is the production detector for silent omission.
  • Test deprovisioning. Confirm that removing groups shrinks the claim, because a claim that only grows is a ceiling you will reach regardless.

Two adjacent controls belong beside this work. Short-lived credentials bound how long a stale large claim stays useful, and Microsoft notes that access tokens default to a one-hour lifetime, which limits how quickly a bad claim stops mattering Microsoft's access revocation guide. Break-glass identities deserve the same scrutiny as agents, including exclusions from policies that could block the recovery they exist for Microsoft's emergency access guidance. When sessions or factors change, invalidation should be a deliberate state transition, with sessions bound to a security epoch that increments on change the security epoch walkthrough.

If you want the harness design reviewed before you build it, a free 30-minute call covering your goals and a tailored recommendation is the fastest route: https://quantaintelligence.ai/calendar.

FAQ

FAQ

Why not test one large number instead of searching?

One large number tells you it fails, not where. The ceiling is what you need, because it is the budget for growth planning, alerting, and the split decision. Doubling plus bisection finds an exact boundary in roughly a dozen probes across a range of a thousand.

What if my identity provider silently drops the groups claim instead of erroring?

That is a count cap, and it is the most dangerous outcome because nothing looks broken. Entra ID documents this behavior: exceeding 150 groups in SAML or 200 in JWT causes group claims to be omitted entirely Microsoft's group claims guidance. The harness must assert that arrived count equals provisioned count, not merely that the status was 200.

Should the test use SAML or OIDC?

Whichever your agent uses in production, then the other if a second flow exists. Limits differ by protocol: Entra ID allows 150 groups in SAML and 200 in JWT, dropping to six for implicit flow Microsoft's token configuration guidance. Testing one protocol yields a ceiling that does not apply to the other path.

How many groups should the agent be allowed in production?

Set the operating limit well below the measured ceiling, derived from the same variables that move it: claim configuration, naming, and the SP size cap. A practical pattern caps at 80 percent of the ceiling and alerts at 90 percent. If projections cross the ceiling within one planning cycle, split the agent instead of raising the cap.

Does group filtering break authorization for some users?

It can, if the application assumed the claim described full membership. Filtering to application-assigned groups means the token describes what the app needs, which is the intent. Audit authorization logic for directory-wide assumptions before enabling it, and add a test that a group outside the filter disappears from the claim.

Sources

Sources

  1. Configure group claims for applications by using Microsoft ...
  2. AWS Account Federation Fails Due to SAML Response ...
  3. "Response attribute will be trimmed" warning for SAML ...
  4. Agent Identity Governance Framework, Lab Space
  5. IdP vs SP iniated SSO workflows: How they differ
  6. SAML Clock Skew in Keycloak
  7. NIST.SP.800-63B-4.pdf
  8. Digital Identity Guidelines: Authentication and Lifecycle ...
  9. Configure group claims and app roles in tokens
  10. Using SCIM to Provision and Govern AI Agents
  11. Understanding Non-Human Identity for AI Agents
  12. What Kind of Identity Should Your AI Agent Have?
  13. Revoke user access in an emergency in Microsoft Entra ID
  14. Manage emergency access accounts in Microsoft Entra ID
  15. Revoking Sessions and Trusted Devices After MFA Recovery
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles