Skip to content

Security & Identity · 17 min read · Published April 20, 2026

AI Agent Authentication & Identity Management

Most teams ship their first AI agent by borrowing a user's OAuth token. It works, it demos well, and it is a ticking time bomb. Here is the complete playbook for AI agent authentication and identity management — giving agents their own scoped identity, securing agent-to-agent calls with mTLS, and building audit trails you can defend in a compromise.

AI agent authentication and identity management — scoped tokens, mTLS handshake, and audit trail

Why agents need their own identity

The tempting pattern for a new agent integration is to take the invoking user's access token and let the agent use it directly. It solves the access problem immediately, it needs no new infrastructure, and it "just works." It is also wrong, and every enterprise audit will flag it. Here's why.

1. Blast radius. A user's OAuth token can see everything the user can see. A finance agent that only needs read-only access to the invoice folder now has access to the user's inbox, calendar, drive, and admin scopes. Compromise the agent, compromise the user.

2. Attribution. When the agent writes to a database or sends an email using the user's token, every log line says the user did it. You lose the ability to distinguish human actions from agent actions, which is fatal for audit and incident response. Regulators (SOC 2, HIPAA, EU AI Act) expect to see "who performed this action" resolve to a specific non-human principal, not "the user whose token the agent was riding."

3. Lifecycle coupling. The user leaves the company, their token is revoked, the agent silently breaks at 3am. An agent's identity should outlive any one user and have its own rotation/revocation lifecycle.

4. Consent scope. Users consent to their own token being used — they almost never consent to an autonomous agent acting on their behalf for hours after they close the tab. Delegated-but-separate identity (OAuth with explicit agent principal) is the only ethically and legally clean pattern.

Related reading: RapidClaw security & privacy overview and SOC 2 / HIPAA compliance for AI agents.

Service accounts vs. agent identities

A "service account" is the traditional IAM pattern — a non-human principal with a long-lived credential that one or more services share. It is the pattern most teams reach for first. It's better than borrowing user tokens, but it's still wrong for agents for three reasons.

  • Shared credentials. If five agents share one service account, a compromise of any one of them compromises all five. You lose per-agent blast-radius containment.
  • Over-broad scopes. Service accounts tend to accumulate scopes. After a year, the "shared agent SA" has write access to half your infra because one agent needed it once.
  • No trajectory tying. Service-account audit logs say "the SA did it." Agent audit logs need to say "agentfinance-bot, version v2.4.1, executing goal G-8271, on behalf of user u-904, did it." You need principals that carry that structure.

An agent identity is a per-agent, short-lived principal with its own credential lifecycle, its own scope definitions, and the ability to carry delegated-from-user context in claims. It's a superset of the service-account concept, and it is the baseline modern pattern. Both OpenClaw and Hermessupport this natively.

OAuth2 for agents

OAuth2 already has the primitives you need; you just have to use them correctly for agent use cases.

  • Client credentials grant for agent-to-service calls where no human is in the loop. The agent authenticates as itself with its own client ID and secret (or better — a private key for private_key_jwt).
  • Token exchange (RFC 8693) when the agent needs to act on behalf of a specific user. The user-facing frontend exchanges the user token for an agent-principal token that carries the user as a delegated claim.
  • Scoped, short-lived access tokens. 10–15 minute lifetime, refresh tokens rotated on every use, one scope per logical capability (e.g., invoices:read,invoices:mark_paid, not invoices:*).
  • DPoP or mTLS-bound tokens. Bind the token to a key the agent holds, so a stolen token is useless without the matching key.

OpenClaw: agent identity in Python

Here's a production-ready OpenClaw setup that gives each agent its own client credentials, requests a scoped token at startup, and refreshes it before expiry. Drop this in your agent bootstrap.

# openclaw_agent/identity.py
import os, time, threading, httpx
from pathlib import Path
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from jose import jwt
from openclaw import Agent, IdentityProvider

class AgentIdentity(IdentityProvider):
    """Per-agent identity: private key + short-lived scoped tokens."""

    def __init__(
        self,
        client_id: str,
        key_path: str,
        token_url: str,
        scopes: list[str],
        audience: str,
    ):
        self.client_id  = client_id
        self.token_url  = token_url
        self.scopes     = scopes
        self.audience   = audience
        self._token     = None
        self._exp       = 0.0
        self._lock      = threading.Lock()
        self._key       = load_pem_private_key(
            Path(key_path).read_bytes(), password=None
        )

    def _sign_assertion(self) -> str:
        now = int(time.time())
        return jwt.encode(
            {
                "iss": self.client_id,
                "sub": self.client_id,
                "aud": self.token_url,
                "iat": now,
                "exp": now + 300,
                "jti": os.urandom(16).hex(),
            },
            self._key, algorithm="RS256",
        )

    def get_token(self) -> str:
        with self._lock:
            if self._token and time.time() < self._exp - 60:
                return self._token
            r = httpx.post(self.token_url, data={
                "grant_type":            "client_credentials",
                "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
                "client_assertion":      self._sign_assertion(),
                "scope":                 " ".join(self.scopes),
                "audience":              self.audience,
            }, timeout=10)
            r.raise_for_status()
            data = r.json()
            self._token = data["access_token"]
            self._exp   = time.time() + data["expires_in"]
            return self._token

    def on_behalf_of(self, user_token: str) -> str:
        """OAuth 2.0 Token Exchange (RFC 8693) — act-as user."""
        r = httpx.post(self.token_url, data={
            "grant_type":           "urn:ietf:params:oauth:grant-type:token-exchange",
            "subject_token":        user_token,
            "subject_token_type":   "urn:ietf:params:oauth:token-type:access_token",
            "actor_token":          self.get_token(),
            "actor_token_type":     "urn:ietf:params:oauth:token-type:access_token",
            "audience":             self.audience,
            "scope":                " ".join(self.scopes),
        }, timeout=10)
        r.raise_for_status()
        return r.json()["access_token"]


def build_agent() -> Agent:
    identity = AgentIdentity(
        client_id = os.environ["AGENT_CLIENT_ID"],
        key_path  = os.environ["AGENT_KEY_PATH"],
        token_url = os.environ["OAUTH_TOKEN_URL"],
        scopes    = ["invoices:read", "invoices:mark_paid"],
        audience  = "https://api.internal.example.com",
    )
    return Agent.from_config(
        "config/agent.yaml",
        identity=identity,
    )

The important properties of this pattern: the private key never leaves the agent host, tokens are short-lived and scoped, token-exchange preserves user delegation with proper actor/ subject claims, and token refresh is transparent to the agent's business logic. A compromise of a single agent exposes only that agent's scopes, not the whole service-account estate.

Hermes: agent identity in YAML

Hermes handles identity declaratively. You describe the identity source, the scopes, and the delegation pattern; Hermes runtime enforces them and rotates credentials automatically.

# agents/finance-bot.yaml
version: "1"
name: finance-bot
version_pin: claude-sonnet-4-6

identity:
  provider: oauth2
  client_id_from: env:AGENT_CLIENT_ID
  key_ref: vault://secret/agents/finance-bot/signing-key
  token_url: https://auth.internal.example.com/oauth2/token
  audience:  https://api.internal.example.com
  assertion_method: private_key_jwt
  scopes:
    - invoices:read
    - invoices:mark_paid
  token:
    lifetime_s: 600          # 10-min tokens
    refresh_margin_s: 60     # refresh 60s before expiry
    bind: dpop               # proof-of-possession via DPoP

  delegation:
    mode: token_exchange     # RFC 8693
    subject_source: request.user_token
    actor_claim: true        # include agent as actor
    max_scope_downgrade: strict  # agent scopes ⊆ user scopes

outbound:
  - name: invoices-api
    base_url: https://api.internal.example.com/invoices
    auth:
      type: bearer
      token_source: identity
    tls:
      mtls: true
      client_cert_ref: vault://secret/agents/finance-bot/mtls-cert

audit:
  sink: otlp://audit-collector.internal:4317
  include:
    - principal
    - delegated_subject
    - tool_calls
    - request_id
    - scope_usage

The delegation.max_scope_downgrade: strict is the critical line — it guarantees that even if the agent's own principal has invoices:mark_paid, it cannot use that scope when acting on behalf of a user who doesn't. This single rule prevents the most common privilege-escalation mistake in agent systems.

mTLS between agents

Bearer tokens over TLS are the baseline, but for agent-to-agent calls — especially in multi-agent systems where agents invoke other agents — you want both sides to authenticate. mTLS (mutual TLS) gives every agent a certificate and requires both sides to present one on every connection. Tokens can still flow on top for fine-grained scope, but the transport is strongly bound to the agent principal.

Practical setup:

  1. Run a private CA (Vault PKI, cert-manager + CSI, or SPIFFE/SPIRE for cloud-native). Don't use your public web TLS CA.
  2. Issue certs to each agent with a SPIFFE ID or custom SAN (e.g.,spiffe://example.com/agent/finance-bot). The cert identity is the agent identity.
  3. Certificates are short-lived (1–24 hours) and auto-rotated by the identity provider. A compromised key loses value fast.
  4. Agent-to-agent calls verify both the peer cert's SPIFFE ID and the bearer token's aud + sub claims. Both must agree.
  5. Wire an authorization policy layer (OPA, Cedar, or your gateway's native policy engine) that maps (caller identity, callee identity, scope) → allow/deny. Centralize it so agents don't each implement their own authZ.

See also our OpenClaw security hardening guide for the full network-layer checklist and the agent firewall setup for egress controls.

JWT scoping: one scope per capability

The biggest anti-pattern we see in agent deployments is scope bloat — a single JWT with admin, rw, orinvoices:*. These tokens are impossible to review, impossible to revoke surgically, and impossible to audit.

The discipline:

  • One scope per verb-noun. Not invoices:* but invoices:read, invoices:create, invoices:mark_paid, each requested separately.
  • Request only what you need this call. Use the OAuth2 scope-down pattern: the agent token includes a broad set of scopes; at each tool invocation the agent requests a narrower child token containing only what this call needs.
  • Attenuate, don't amplify. A delegated token should always carry a scope subset of the user's. Reject any token where scope ⊄ parent_scope.
  • Audit the scope usage distribution. If an agent has invoices:mark_paid but has never used it in 30 days, revoke it. Scope over-grant is how blast radius creeps.
  • Use JWT claims beyond scope. Addagent_id, agent_version, goal_id, trace_id, and delegated_subject claims. Those claims are what make audit forensics tractable.

Least-privilege access for agents

Least privilege is the only principle that survives contact with a real compromise. The practical rules we enforce:

  1. Default deny. A new agent starts with zero scopes. Each capability is added explicitly, with a reviewer signing off.
  2. Read-before-write. An agent that doesn't need to write shouldn't have a write scope. Separate identities per read-only vs. read-write agent where practical.
  3. Time-boxed elevation. For scopes the agent needs occasionally (e.g., "nightly reconciliation"), don't leave them on permanently. Issue time-boxed tokens on a schedule and expire them.
  4. Environment separation. Prod scopes and prod credentials are inaccessible to dev and staging agents. No shared secrets.
  5. Kill switch. Every agent identity has a single flag that revokes all tokens and disables issuance. Wire it to your on-call paging tool alongside the deploy-rollback switch; see our AI agent CI/CD pipeline guide for the rollback-identity pairing pattern.

Identity federation across agent platforms

Most teams end up running more than one agent platform — OpenClaw for some workloads, Hermes for others, a third-party SaaS agent for one integration. Identity federation is how you keep a single source of truth for agent principals across them.

The pattern:

  • One identity provider for all agents — your existing IdP (Okta, Auth0, Azure AD, Keycloak) with a dedicated realm or project for agent principals.
  • OIDC federation between platforms. The agent runtime presents its platform-native identity (OpenClaw runtime token, Hermes runtime token) and federates to the IdP, which issues the scoped access token for the target API.
  • SPIFFE for infra-layer identity. Kubernetes workload identity (via SPIRE) gives every agent pod a cryptographic identity independent of its OAuth client. The OAuth exchange rides on top of that.
  • Unified audit pipeline. Every platform ships identity events (issue, exchange, revoke, deny) to the same audit sink with a consistent schema. Cross-platform forensics requires cross-platform logs.

Audit trails that survive a compromise

Audit logs are only useful if they survive the thing you're auditing. Three properties matter.

Completeness. Log every token issuance, every exchange, every tool call with the scope used, every authorization decision. Missing records are what regulators notice.

Immutability. Write audit events to a WORM (write-once-read-many) sink the agent itself can't delete. A compromised agent with delete permission on its own log is no audit trail at all. S3 Object Lock, append-only Kafka with long retention, or a dedicated audit appliance are acceptable; the agent's own database is not.

Attribution. Every event carries principal (the agent), delegated_subject(the user, if any), agent_version, goal_id, trace_id, and scope_used. A well-attributed log answers "who did what, on whose behalf, with what authority, as part of which task" without a join across five systems.

For more on observability that pairs with this audit trail, see our agent observability guide.

Threats to model for

  • Token exfiltration. Short lifetimes + proof-of-possession (DPoP / mTLS-bound) + revocation-on-suspicion + per-agent scopes.
  • Prompt injection that exfils credentials.Agents should never be able to "see" their own raw credentials; inject them via the HTTP layer only, with allow-lists on outbound hosts.
  • Confused-deputy attack. An agent acting on behalf of user A accepts a tool result that subtly redirects it to act on behalf of user B. Defense: pin delegated_subject at goal creation, reject any tool result that would force a subject swap.
  • Supply-chain compromise. A malicious model update issues agents that exfil scopes. Defense: model version pinning (see AI agent CI/CD pipeline), egress allow-lists, and anomaly detection on outbound token-issue-to-use ratios.
  • Ex-employee tokens. Agent identities are organizationally-owned, not user-owned. When a user leaves, the user's delegation links are revoked, but the agent continues with its own principal and a new owner.

Identity patterns for multi-agent systems

Multi-agent systems — where one agent orchestrates others, or agents call each other peer-to-peer — multiply every identity problem. Two patterns work in practice.

Supervisor pattern (strict hierarchy). One orchestrator agent holds the user delegation. Sub-agents receive further-attenuated tokens from the orchestrator, never from the user directly. Each sub-agent's scopes are a strict subset of the orchestrator's scopes, which are a strict subset of the user's scopes. Authorization decisions check the full chain. This is the right pattern when you can model the workflow as a tree and sub-agents have well-defined bounded tasks.

Peer pattern (mesh). Agents call each other horizontally. Each agent maintains its own identity; when Agent A calls Agent B on behalf of user U, Agent A presents both its own token and the user's delegation. Agent B validates both, applies its own authorization policy, and takes the narrower result. This is the right pattern for agent marketplaces, agent-to-agent integrations across teams, or any topology where the call graph isn't known ahead of time.

In either pattern, pin the delegated subject at goal creation time. The user on whose behalf a multi-step goal runs must be captured when the goal starts, not re-derived at each sub-agent call. Otherwise a prompt-injection in one sub-agent's tool response can coerce the next sub-agent into acting on behalf of a different user — the confused-deputy attack in multi-agent clothes.

For the broader architecture, see A2A protocol and agent-to-agent hosting.

Incident response and credential rotation

Prepare for the day an agent credential leaks. The value of the preparation is measured in how fast you can rotate and how narrow the blast radius is.

  1. Inventory. You should be able to list every active agent identity, its key material fingerprint, its current scopes, and the sink it reports to — in under 60 seconds. If that query takes longer, fix it now; it'll be worse at 3am.
  2. Rotation drills. Pick an agent at random once a month and rotate its signing key as if it were compromised. Measure the time from "decide to rotate" to "old key no longer accepted by any downstream." It should be under 15 minutes; at many companies it is measured in hours or days.
  3. Revocation lists. Every IdP and downstream service must honor a revocation list (or equivalent short-TTL plus re-issuance) that propagates within the token lifetime. Revocation that takes 10 minutes to propagate plus 10-minute tokens means 20 minutes of exposure — often acceptable. Revocation that takes an hour to propagate is functionally useless.
  4. Quarantine playbook. "Agent X may be compromised" should trigger: disable issuance for agent X's client ID, revoke all outstanding tokens for that ID, dump the last 24h of tool calls from the audit sink, snapshot the agent's state, then investigate. The first three steps should be a one-command script.
  5. Post-incident scope review. Every incident that involves a credential ends with a scope review — did the agent really need the scope it used to cause damage? Usually the answer is "it had that scope from six months ago when a different use case wanted it." Ratchet scopes down.

What to build this week

  1. Inventory every agent in your org and write down the scopes each one currently uses. The exercise usually reveals 2–3 agents running on user tokens that should be moved.
  2. Pick one agent. Create an OAuth client (private-key-jwt, not client-secret), store the key in a vault, and wire the AgentIdentity class above (or the equivalent Hermes YAML).
  3. Enumerate its capabilities. Create one scope per verb-noun. Request minimum scopes at each call site.
  4. Wire mTLS between the agent and its callees with short-lived certs and a SPIFFE ID.
  5. Stand up the immutable audit sink and ship identity events to it. Test a recovery drill: "show me everything agent X did on behalf of user Y between 2pm and 3pm."
  6. Add token-exchange for user delegation. Enable strict scope downgrade.
  7. Add the kill switch and test it. Revocation-drills are the cheapest confidence-building exercise you'll run.

Using RapidClaw's managed identity

Identity is the security primitive you want boring, audited, and running on someone else's pager. RapidClaw's managed hosting bundles per-agent OAuth clients, auto-rotated signing keys, SPIFFE-based mTLS between agents, token exchange with strict scope downgrade, and a WORM audit sink — on top of OpenClaw or Hermes. If you're building this yourself, the patterns above are the right ones; if you don't want to own the undifferentiated parts, we can run them for you.

Related deeper reads: Security & privacy overview, OpenClaw security hardening guide, SOC 2 / HIPAA compliance for AI agents, EU AI Act compliance for agents, and AI agent CI/CD pipelines.

Frequently asked questions