Same agent, different user
One agent, invoked by two people in different directory groups, reaches different data, because policy reads group membership from the identity provider you already run.
You run one agent and many people use it. What each person’s request may reach should follow the groups you already maintain in your directory, without a second permission system to keep in step.
The problem
Section titled “The problem”An agent usually gets one identity and one set of permissions. Whoever invokes it, the downstream system sees the same principal with the same access.
That flattens your directory. The careful work of putting people in groups, and the entitlements attached to those groups, stops at the agent’s front door. Someone in a read-only group and someone with write access invoke the same agent and get the same reach.
It also turns a convenience into an escalation path. If the agent’s static identity holds the union of what any of its users might need, then the least-privileged person invoking it has borrowed the most-privileged person’s access for the duration of a request. Nobody granted that, and nothing in the log distinguishes it from an ordinary call.
Maintaining a second permission table inside the agent does not fix it. Now group membership lives in two places, and the copy drifts the day someone changes teams.
flowchart LR
A[Analyst] --> C[Your Agent]
B[Administrator] --> C
C -->|"One static identity, union of permissions"| D[Resource]
D -->|"Same access for both"| E["Log: the agent did it"]
When this applies
Section titled “When this applies”- More than one person invokes the same agent, and they should not all reach the same data
- Your identity provider already has the group structure that expresses who may see what
- You want an entitlement change made in the directory to govern agent access too
- You need to show a reviewer that the agent is not a way around existing group boundaries
Suppose a support agent can read customer records. Your tier-one group should reach contact details and open tickets. Your billing group should reach payment history. The same agent, asked the same question by two people, should answer differently, and it should be your directory that decides.
When it does not
Section titled “When it does not”Where the difference you need is per record or per row rather than per Resource, the Resource enforces that itself. Keycard decides which Resource and which operation a request may reach.
Where nobody is present to authenticate, no ID token arrives, so there is no groups claim to read. Keycard Groups still apply. See Act on Behalf of Absent Users.
Where the distinction you want is about what the agent may do rather than who it acts for, decide it per tool at the gateway. See Allow one tool, deny the next.
The solution
Section titled “The solution”Keycard evaluates policy on every credential request, and that evaluation can read the invoking user’s group membership from your identity Provider.
The agent stays one registered Application. What changes per request is the User it is acting for, and the groups that user carries. A policy that permits a Resource only when the subject’s groups contain a particular value produces one outcome for the analyst and another for the administrator, from the same agent and the same code.
Once a policy set of your own is active, a request that matches no permit is refused, and a forbid always beats a permit. Until then the managed defaults permit every User every Resource, so activate a set of your own without default-user-grants before you rely on the refusal. A set that keeps default-user-grants still permits everyone, so a group permit next to it narrows nothing; write the rule as a forbid ... unless on the group instead, since a forbid narrows access even alongside that permit.
How it works
Section titled “How it works”flowchart LR
A[User signs in] -->|"ID token with groups"| B[Keycard session]
B --> C[Your Agent requests a credential]
C --> D{Policy evaluation}
D -->|"subject_claims.groups matches"| E[Credential for that Resource]
D -->|"no match"| F[Denied and logged]
- The User signs in through your identity provider. Keycard federates over OpenID Connect, and Okta, Microsoft Entra ID, Ping Identity, Auth0, and Google are all documented paths. Auth0 and Google can’t send a
groupsclaim, so use Keycard Groups with them. - Keycard reads a
groupsclaim from the provider’s ID token and carries it into policy evaluation ascontext.subject_claims.groups. Configure your provider to include groups in the ID token; if it only releases them for a requested scope, add that scope under Additional Scopes on the Provider. Match the exact values it emits, which may be IDs rather than names. - Your agent requests a credential for one named Resource, acting for that User.
- Policy evaluates against the User, the Application, the Resource, and that group context. The full set of attributes policy can see is listed under entity properties.
- A permitted request yields a credential scoped to that User and Resource. A refused one is recorded as a Failure carrying
access_denied. When aforbiddecided it, the reason names that policy. When nothing permitted the request, the reason names the principal and the Resource.
What you get
Section titled “What you get”The same agent gives different people different reach, and the difference is expressed once, in policy, against groups you already maintain.
Access follows your directory rather than a copy of it. When someone moves teams, you change one thing in your identity provider.
Every decision is attributable. The audit record names the person, the Application acting for them, and the Resource, so “why could they see that” is answerable from the log rather than by reading the agent’s code.
What you take on
Section titled “What you take on”Group membership arrives at sign-in, so a change lands only in sessions that start after the person signs in again. Keycard reads the groups claim from the ID token when the session is created, and that snapshot serves the session rather than being re-fetched per request. Removing someone from a group in your directory therefore governs their next session rather than one already running. An agent that already holds a refresh token keeps the earlier groups until that token expires, up to 90 days, even if the person signs in again; see What Keycard receives from your identity provider. Disabling the person stops that sooner; revoking a grant doesn’t. A credential already issued lasts until it expires.
You write the policies. Expressing the distinction once in policy is the point, and it means someone owns those rules and keeps them current as your group structure changes.
Group matching is exact. Cedar cannot pattern-match across the members of a set, so a prefix naming scheme needs the membership modeled as its own groups.
Common questions
Section titled “Common questions”Does this replace our identity provider?
Section titled “Does this replace our identity provider?”No, and it reads from yours on purpose. Your directory stays the system of record for who someone is and which groups they belong to. Keycard decides what their agent may reach, using that membership as an input. Where a team has no identity provider, that is the piece to solve first, because everything here derives from a verified person and their groups.
How does a reviewer confirm the boundary actually holds?
Section titled “How does a reviewer confirm the boundary actually holds?”Run the identical task as two users in different groups and compare. Both requests appear in the Audit Log, the permitted one as a credential issuance naming the person and the Resource, the refused one as a Failure. Its reason names the forbid that decided it or, when no policy permitted the request, the principal and the Resource. That pair of records is the evidence, and it is reproducible on demand rather than asserted.
What happens if the group claim stops arriving?
Section titled “What happens if the group claim stops arriving?”The policy stops matching. Under a policy set of your own, that means access stops rather than opening up: it fails shut. Under the managed defaults the unmatched request is allowed instead, which is why this page asks you to activate your own set first. The cost is that a provider misconfiguration looks like a policy denial, which is why the Aside above suggests confirming the claim in a decoded ID token before a first session.
Can we do this with roles instead of directory groups?
Section titled “Can we do this with roles instead of directory groups?”Yes, and the two behave differently in one way worth knowing. Keycard Roles and Keycard Groups are read when policy evaluates, so a change applies on the next credential request. The IdP groups claim is a sign-in snapshot. Where directory sync is on for your Zone, directory groups arrive as Keycard Groups. A credential already issued stays valid until it expires.
Where else this applies
Section titled “Where else this applies”- A data agent where the finance group reaches revenue tables and everyone else reaches the product ones
- An operations agent where on-call members may act on production and the rest may only read
- A customer-facing assistant in a multi-tenant product, where the invoking user’s tenant group decides which tenant’s data is in reach
- An internal search agent that indexes broadly and returns only what the asker’s groups already permit
The shape is the same each time. One agent, many invokers, and a distinction that already exists in your directory rather than one invented for the agent.
Related
Section titled “Related”- Access Snowflake is the build walkthrough for this pattern: Okta group membership decides who reaches a Snowflake MCP server
- Act as the user who asked is the pattern this builds on, where the agent carries one person’s identity
- Access Policies covers policy syntax, the attributes policy can read, and role-based rules
- Policies explains where policy sits and what it decides
- Identity Providers covers connecting Okta, Auth0, and Google to a Zone
- Roles & Permissions is the alternative to directory groups, resolved at evaluation time
- Audit Log & Sessions shows how to read both sides of a denial