Secure Multi-Agent AI with Cedar: Least-Privilege Delegation on AWS
A multi-agent system can begin with a well-authorized user and still end with an unauthorized tool call. The orchestrator delegates to a specialist, the specialist chooses another agent, and the final agent holds credentials that can delete records or change production. If each component checks only its own role, authority silently expands across the chain.
This is the agentic version of the confused-deputy problem. The OWASP Top 10 for Agentic Applications calls out identity and privilege abuse as ASI03. Prompt instructions do not fix it. Telling an agent to “respect the user’s permissions” leaves a probabilistic model in charge of a deterministic security decision.
AWS published a reference design in July 2026 that uses OAuth 2.0, signed user context, Cedar policies, and Amazon Verified Permissions to check three independent boundaries: whether an agent may invoke a tool, whether one agent may delegate to another, and whether the originating human may authorize the final action. This guide turns that design into an implementation and operating model.
Why ordinary RBAC is not enough
Traditional role-based access control often evaluates one tuple: user, role, action. A multi-agent chain contains more dimensions:
- The human who initiated the request
- The session and authentication strength
- The orchestrating agent
- Every downstream agent in the delegation path
- The capabilities requested at each hop
- The final tool and resource
- Delegation depth
- Environment, namespace, risk level, and lifecycle stage
- Time and prior approvals
Suppose an administrator asks an orchestrator to summarize failed payments. The orchestrator delegates data collection to data-bot. A malicious record contains prompt injection telling data-bot to call delete_records. The human is an administrator, but did not delegate deletion. The bot might possess a broad IAM role, but its registered task is read-only. A role check on either identity alone can return the wrong answer.
The authorization decision must preserve the intersection:
effective authority
= originating user authority
∩ explicitly delegated scope
∩ invoking agent capability
∩ target agent capability
∩ tool and resource policy
∩ environmental constraints
An empty intersection is a denial. A language model cannot add a missing permission by explaining why the action would be useful.
Separate authentication, authorization, and execution
Cedar does not authenticate users. Amazon Verified Permissions does not validate a JWT merely because token claims appear in the request context. Establish identity first, make an authorization decision outside the model, and execute only after a permit.
| Stage | Responsibility | Example control |
|---|---|---|
| Authentication | Prove the human or workload identity | OIDC provider, signed JWT, MFA |
| Context integrity | Preserve trusted identity and delegation claims | OAuth token exchange and signed envelope |
| Authorization | Decide principal, action, resource, and context | Cedar in Amazon Verified Permissions |
| Execution | Invoke the approved tool with bounded credentials | Lambda, service role, session policy |
| Audit | Record the identity, chain, request, decisions, and effect | OCSF event, CloudWatch, Security Lake |
The policy decision point must sit in the request path. An agent should not call a production tool and ask Cedar afterward whether that was allowed. The executor should accept only a short-lived, signed authorization result bound to the exact action and resource, or it should call the evaluator synchronously before the effect.
The secure AI agent access patterns guide explains why authentication at an MCP gateway is necessary but insufficient. Tool-level authorization is the next boundary.
Use three independent policy layers
The AWS reference model evaluates three layers and stops at the first denial.
| Layer | Question | Principal → resource |
|---|---|---|
| L1: agent to tool | Is this production agent trusted and registered to invoke this tool? | Agent → Tool |
| L2: agent to agent | May this agent delegate this requested capability to that agent at this depth? | Agent → Agent |
| L3: originating user | Did the authenticated human authorize this risk and satisfy required conditions? | Agent → Tool, with user in context |
All three must permit. A trusted bot cannot override a user’s missing authority. An administrator cannot turn an unreviewed development agent into a production executor. A valid delegation cannot exceed a hard depth limit.
This layering is easier to reason about than one giant policy because a denial identifies the failed boundary. It also supports different owners: the platform team can own agent trust, application teams can own delegation and tool scope, and security or resource owners can own high-risk user requirements.

Define a small, strict Cedar schema
Begin with explicit entities and actions. The AWS example uses Agent and Tool, plus invoke_tool and delegate_task actions. User attributes travel in a trusted context record rather than as a Cedar User entity.
{
"AgentAuthz": {
"entityTypes": {
"Agent": {
"shape": {
"type": "Record",
"attributes": {
"trust_level": { "type": "Long", "required": true },
"namespace": { "type": "String", "required": true },
"registered_capabilities": {
"type": "Set",
"element": { "type": "String" },
"required": true
},
"lifecycle_stage": { "type": "String", "required": true }
}
}
},
"Tool": {
"shape": {
"type": "Record",
"attributes": {
"namespace": { "type": "String", "required": true },
"risk_level": { "type": "String", "required": true }
}
}
}
},
"actions": {
"invoke_tool": {
"appliesTo": {
"principalTypes": ["Agent"],
"resourceTypes": ["Tool"]
}
},
"delegate_task": {
"appliesTo": {
"principalTypes": ["Agent"],
"resourceTypes": ["Agent"]
}
}
}
}
}
Enable strict validation on the Verified Permissions policy store. Schema validation catches misspelled attributes, invalid action-resource combinations, and type mismatches before they become runtime ambiguity. Treat the schema as a versioned API: review changes, test old policies, and deploy schema and evaluator updates in a compatible order.
Do not put unbounded prompt text, model reasoning, or raw tool arguments into policy attributes. Normalize them into stable facts such as capability query_records, resource tenant acme, data classification confidential, or amount band high. Authorization inputs should be small enough to enumerate and test.
Implement Layer 1: agent-to-tool
Layer 1 permits a specific agent to call a specific tool only when authoritative agent attributes meet the production standard.
permit(
principal == AgentAuthz::Agent::"finance-agent",
action == AgentAuthz::Action::"invoke_tool",
resource == AgentAuthz::Tool::"process_payment"
) when {
principal.trust_level >= 3 &&
principal.namespace == "payments" &&
principal.lifecycle_stage == "production" &&
principal.registered_capabilities.contains("process_payment") &&
resource.namespace == principal.namespace
};
The agent must not supply its own trust level, namespace, lifecycle stage, or capability list. Retrieve those attributes from a registry controlled by the agent promotion process, then pass them as Cedar entities. AWS’s sample accepts some request values for simplicity and explicitly warns production implementations to replace them with authoritative lookup.
Define what a trust level means. A score of three might require pinned tools, security review, integration tests, prompt-injection evaluation, a named owner, and a bounded execution identity. A number assigned by the agent developer is decoration, not assurance.
Keep identity and permission separate. A workload identity from SPIFFE, IAM, or another system proves which agent code is calling. Cedar decides what that identity may ask to do. The SPIFFE and SPIRE on EKS guide covers the identity half of that equation.
Implement Layer 2: bounded delegation
Layer 2 checks each hop. The AWS reference permits an orchestrator to delegate to data-bot only when the chain is shallow enough and requested capabilities are a subset of the target’s registered capabilities.
permit(
principal == AgentAuthz::Agent::"orchestrator",
action == AgentAuthz::Action::"delegate_task",
resource == AgentAuthz::Agent::"data-bot"
) when {
context.delegation_depth <= 3 &&
context.target_capabilities.containsAll(
context.requested_capabilities
)
};
Add a system-wide forbid for the hard ceiling:
forbid(
principal,
action == AgentAuthz::Action::"delegate_task",
resource
) when {
context.delegation_depth > 5
};
In Cedar, an applicable forbid overrides permits. Use that property for invariants such as maximum delegation depth, disabled agents, suspended tenants, emergency tool shutdown, or prohibited production operations.
The requested_capabilities set must come from the orchestrator’s structured delegation request, not from the downstream agent’s interpretation of prose. Bind the authorization result to that normalized set. If data-bot later decides deletion is useful, it needs a fresh decision for delete_records.
Track the full delegation path and detect loops. Depth prevents unlimited recursion, but explicit agent IDs in the chain help detect A → B → A, attribute denials, and investigate unexpected routing.
The Bedrock Agents and MCP DevOps guide shows how tool discovery increases the need for explicit capability boundaries. Discovery should reveal what exists; it should not grant permission to use it.
Implement Layer 3: originating-user authority
Layer 3 keeps the invoking agent as the Cedar principal but checks verified human attributes from context. A destructive data tool might require an administrator role, MFA, and a delegation depth of no more than two:
permit(
principal == AgentAuthz::Agent::"data-bot",
action == AgentAuthz::Action::"invoke_tool",
resource == AgentAuthz::Tool::"delete_records"
) when {
context.originating_user.role == "admin" &&
context.originating_user.mfa_verified == true &&
context.delegation_depth <= 2 &&
context.delegated_capabilities.contains("delete_records")
};
The last check is essential. A user may possess a role that permits deletion in general without delegating deletion in this session. Capture explicit user intent in a scope or approval artifact, not merely a broad directory role.
For high-risk effects, include resource boundaries and transaction constraints: tenant, account, environment, maximum amount, data classification, or change window. Cedar authorization is fine-grained; do not collapse every production operation into one admin Boolean.
MFA claims must come from a validated token. An agent cannot set mfa_verified: true because the request sounds urgent. Validate issuer, audience, signature, expiration, not-before time, token use, and session claims at the gateway.
Preserve context integrity across hops
The originating user context becomes valuable security data. If agents pass it as editable JSON, a compromised hop can change role from support to admin, set MFA to true, or reset delegation depth.
The AWS design uses two complementary mechanisms:
- The adapter creates a canonical record from verified token claims and signs it with HMAC-SHA256 using a key from Secrets Manager.
- OAuth 2.0 Token Exchange, RFC 8693, creates a scoped on-behalf-of token for downstream delegation.
HMAC proves that the context came from the trusted adapter and was not changed. Token exchange expresses who acts on behalf of whom and narrows the downstream scope. One is not a substitute for the other.
Canonicalization must be deterministic. Define field order, encoding, missing-value behavior, Unicode handling, and version. Include at least user ID, session ID, role or groups, authentication method, MFA status, issued time, expiry, authorized scope, delegation path, and request correlation ID. Bind tool arguments or a stable hash of them when the permit applies to a specific transaction.
Rotate the signing key and include a key identifier so evaluators can accept the old and new key during a bounded transition. Do not let agents read the key; only the trusted adapter signs and the evaluator verifies.
Replay defense matters too. Include expiration and a unique authorization or request ID. A previously valid delete_records envelope should not authorize a second deletion after the human session ends.
Place the evaluator outside the model loop
The model can propose a principal, action, resource, and normalized request, but a separate component must build the final authorization call from trusted state.
In the AWS reference path:
- AWS WAF filters common web attacks, request rate, and body size.
- API Gateway validates the JWT through an authorizer.
- An MCP adapter maps verified claims, applies content controls, signs the envelope, and normalizes the request.
- A Cedar evaluator verifies the signature and loads authoritative agent and tool attributes.
- The evaluator checks the relevant L1, L2, and L3 policies, stopping on denial.
- Only a permit reaches an executor that holds tool credentials.
- The evaluator emits an audit event even when execution later fails.
The application calls Amazon Verified Permissions with a policy-store ID, principal entity, action, resource entity, context, and supporting entities. The result contains a decision of ALLOW or DENY, plus determining policies and errors. Fail closed on API errors, schema mismatches, missing entities, signature failure, timeout, or unknown actions.
A simplified evaluator boundary looks like this:
def authorize_and_execute(request, signed_context):
context = verify_and_decode(signed_context) # raises on any failure
normalized = normalize_request(request)
entities = authoritative_entities(
agent_id=normalized.agent_id,
target_id=normalized.target_id,
)
decisions = [
evaluate_layer_1(normalized, context, entities),
evaluate_layer_2(normalized, context, entities),
evaluate_layer_3(normalized, context, entities),
]
if any(decision != "ALLOW" for decision in decisions):
audit_denial(normalized, context, decisions)
raise PermissionError("authorization denied")
capability = issue_single_use_capability(normalized, context)
return invoke_bounded_tool(normalized, capability)
This deliberately hides SDK serialization inside the layer functions. Unit-test those adapters against the current IsAuthorized request model instead of hand-building polymorphic Cedar values throughout application code.
Never return raw policy internals to an untrusted caller. A stable denial code such as ORIGINATOR_MFA_REQUIRED can guide a user, while detailed policy IDs, attributes, and evaluation errors remain in protected logs.
Bound the executor credentials too
Cedar returns a decision; it does not constrain an IAM role that already has broad access. The executor must use credentials matching the permit.
For AWS tools, assume a short-lived role with a session policy narrowed to the approved account, region, resource, and action. For databases, use stored procedures or a broker that accepts the single authorized operation. For SaaS APIs, place a gateway in front of broad tokens and validate the signed capability there.
Do not give every agent the executor credential. The model-facing runtime should call the policy enforcement point, which alone can reach the effectful backend. Otherwise a prompt injection can bypass Cedar by invoking the tool directly.
Server-side tool execution can help centralize this boundary. The AgentCore Gateway server-side execution guide explains the operational pattern, but the gateway still needs identity, authorization, and credential scoping.
Separate read and write tools even when they share an API. query_records and delete_records should be distinct actions with distinct policies, metrics, and credentials. A generic database.execute tool makes least privilege much harder.
Test policies as code
Authorization tests should outnumber happy-path demos. Build a matrix that changes one dimension at a time.
| Scenario | Expected result | Denying layer |
|---|---|---|
| Production finance agent processes a payment in its namespace | Permit if user scope also allows it | None |
| Development agent calls a production payment tool | Deny | L1 |
| Orchestrator requests a capability not registered to target | Deny | L2 |
| Six-hop delegation by an administrator | Deny | L2 hard forbid |
| Support user requests deletion without MFA | Deny | L3 |
| Administrator with MFA explicitly delegates deletion within two hops | Permit | None |
| Signed context is modified after the adapter | Deny before Cedar | Integrity check |
| Verified Permissions times out | Deny | Fail-closed evaluator |
| Valid permit is replayed | Deny | Capability/executor |
Use Cedar schema validation, policy validation, unit tests, and property-based tests. Generate combinations of trust level, lifecycle stage, depth, capability sets, role, MFA, tenant, and risk. Assert that adding a delegation hop never increases authority and that removing a requested capability never requires more privilege.
Run adversarial end-to-end scenarios containing prompt injection in user text, retrieved documents, tool results, and agent messages. The model may choose the wrong action; the deterministic layer must still deny it. The AWS agent evaluation guide provides a useful approach to disposable test accounts and measurable outcomes.
Audit every decision and effect
An agent authorization event should answer:
- Which human initiated the request and through which authenticated session?
- Which agents formed the delegation path?
- Which normalized capabilities were delegated?
- Which action and resource were evaluated?
- What did each layer decide, and which policy determined it?
- What was the final decision and latency?
- Was a capability issued, consumed, expired, or replayed?
- What effect occurred and which backend request ID proves it?
AWS’s reference emits OCSF event class 99001 to CloudWatch Logs and sends failed emissions to an SQS dead-letter queue. Monitor evaluator latency, deny rate, signature failures, unknown agents, depth denials, Verified Permissions errors, and DLQ depth.
Do not log raw prompts, tokens, HMAC keys, database rows, or sensitive tool responses merely to gain traceability. Store hashes or bounded summaries where possible and apply retention and access policies to the audit data. The Bedrock trust and safety checklist covers complementary content, privacy, and monitoring controls.
Use two correlated records: the authorization decision and the actual tool effect. A permit without a tool event may indicate a downstream failure. A tool event without a permit is a critical bypass.
Operate policy changes safely
Centralizing authorization makes policy changes powerful. Protect the policy store with separation of duties, infrastructure as code, review, strict schema validation, and deployment tests.
Use separate policy stores for development, staging, and production. Promote the same versioned policy artifact through environments. Record policy IDs or hashes in audit events so investigators can reproduce the decision made at that time.
Emergency denies need a fast path. A forbid policy can disable a tool, agent, tenant, or action while application teams investigate. Pre-authorize who can activate that control and test propagation latency.
In a multi-account organization, AWS recommends considering a central security account for the policy store and cross-account roles for verifiedpermissions:IsAuthorized. Service control policies can prevent workload accounts from creating unsanctioned stores. Centralization improves consistency but adds a network and availability dependency, so define timeouts, fail-closed behavior, quotas, and regional recovery.
Cache static agent metadata carefully; do not cache final high-risk permits beyond their transaction and expiry. A disabled agent or revoked user must stop quickly.
A practical production sequence
Adopt the pattern one effectful tool at a time:
- Inventory agents, delegation paths, tools, resources, credentials, and direct bypass routes.
- Normalize each tool into explicit actions and risk levels.
- Define a strict Cedar schema and separate environment policy stores.
- Establish an OIDC identity path with MFA claims for high-risk operations.
- Create an authoritative agent registry and promotion criteria.
- Sign the originating context and implement scoped token exchange.
- Add L1 agent-to-tool policies and remove direct agent credentials.
- Add L2 delegation paths, capability subsets, loop detection, and a hard depth forbid.
- Add L3 user scope, MFA, tenant, resource, and transaction constraints.
- Bind each permit to short-lived, single-use executor authority.
- Test denials, tampering, replay, policy outages, and prompt injection.
- Correlate authorization events with backend effects and alarm on gaps.
- Rehearse emergency tool shutdown and signing-key rotation.
- Expand autonomy only when evaluation and audit evidence justify it.
The managed coding agents comparison helps identify different runtime surfaces, but the authorization rule stays the same: model choice must not determine production authority.
Multi-agent AI does not require a new definition of least privilege. It requires applying the old definition to every principal and every delegation. Preserve the human’s verified intent, constrain each agent to registered capability, deny excessive chains, and place the final decision outside the model. Cedar makes those rules readable; Amazon Verified Permissions makes them centrally evaluable; bounded executor credentials make the decision real.
Sources
- Enforce least-privilege authorization in multi-agent AI chains using Cedar
- Amazon Verified Permissions User Guide
- Amazon Verified Permissions IsAuthorized API
- Cedar policy language reference
- AWS sample: Cedar authorization for agentic AI
- OAuth 2.0 Token Exchange RFC 8693
- OWASP Top 10 for Agentic Applications
Comments