Secure Multi-Agent AI with Cedar: Least-Privilege Delegation on AWS

Cleber Rodrigues
Written by Cleber Rodrigues
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.

Three-layer Cedar authorization path for a signed multi-agent delegation chain

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:

  1. The adapter creates a canonical record from verified token claims and signs it with HMAC-SHA256 using a key from Secrets Manager.
  2. 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:

  1. AWS WAF filters common web attacks, request rate, and body size.
  2. API Gateway validates the JWT through an authorizer.
  3. An MCP adapter maps verified claims, applies content controls, signs the envelope, and normalizes the request.
  4. A Cedar evaluator verifies the signature and loads authoritative agent and tool attributes.
  5. The evaluator checks the relevant L1, L2, and L3 policies, stopping on denial.
  6. Only a permit reaches an executor that holds tool credentials.
  7. 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:

  1. Inventory agents, delegation paths, tools, resources, credentials, and direct bypass routes.
  2. Normalize each tool into explicit actions and risk levels.
  3. Define a strict Cedar schema and separate environment policy stores.
  4. Establish an OIDC identity path with MFA claims for high-risk operations.
  5. Create an authoritative agent registry and promotion criteria.
  6. Sign the originating context and implement scoped token exchange.
  7. Add L1 agent-to-tool policies and remove direct agent credentials.
  8. Add L2 delegation paths, capability subsets, loop detection, and a hard depth forbid.
  9. Add L3 user scope, MFA, tenant, resource, and transaction constraints.
  10. Bind each permit to short-lived, single-use executor authority.
  11. Test denials, tampering, replay, policy outages, and prompt injection.
  12. Correlate authorization events with backend effects and alarm on gaps.
  13. Rehearse emergency tool shutdown and signing-key rotation.
  14. 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

Cleber Rodrigues

Cleber Rodrigues

AWS Enthusiast | Cloud Architect | AWS Certified Solutions Architect – Professional

Comments

comments powered by Disqus