workos/auth.md

repository·main·Indexed 19 days ago

https://github.com/workos/auth.md

A reference implementation of 'agentic registration', a protocol enabling AI agents to authenticate to services on behalf of users. It utilizes Identity Assertion JWT Authorization Grants (ID-JAGs), claim ceremonies, and a two-step exchange process to acquire access tokens via discovery and identity assertion.

Tokens
29.7K
Snippets
68
Records
87
Agent score
67%

What's inside auth.md

  1. Overview of Agent Auth Consumer Flows

    main

    Services that want agents to authenticate on behalf of users can implement one of three primary flows. All flows require publishing discovery metadata and implementing the /agent/identity registration endpoint, along with standard OAuth /oauth2/token and /oauth2/revoke endpoints.

    Supported Flows

    1. ID-JAG identity assertion: Trusted agent providers (e.g., OpenAI, Anthropic, Cursor) assert a user's identity using an Identity Assertion JWT Authorization Grant (ID-JAG). The service verifies the assertion and returns a service-signed identity_assertion which the agent exchanges for an access_token at the token endpoint.
    2. Verified-email identity assertion: The agent provides a user email; the service generates a 6-digit user_code and a verification_uri. The user completes the authorization by signing in to the service and entering the code.
    3. Anonymous registration: Used when an agent has no user identity. The agent self-registers to receive a pre-claim identity_assertion. A human can later take ownership of this identity via a claim ceremony (code-handoff).

    Key Technical Details

    • Credential Issuance: All flows terminate at /oauth2/token using the RFC 7523 JWT-bearer grant type.
    • Claim Ceremony: The Verified-email and Anonymous flows utilize an RFC 8628 device-authorization-shaped claim ceremony.
    • Benefits: Provides a real revocation surface for agent delegations and supports MCP-server agents that start without a user identity.
  2. Agent Auth Provider Guide Overview

    main
    The Agent Auth Provider protocol enables trusted providers to authenticate agents on behalf of users by asserting their identities. Instead of users manually creating API keys for agents, providers sign Identity Assertion JWT Authorization Grants (ID-JAGs). This allows the provider to act as an identity broker, maintaining control over consent prompts, revocation UX, and delegation audit trails for every service the user's agent interacts with.
  3. Sequence Diagram: ID-JAG Identity Assertion

    main

    This flow describes how a service verifies an identity assertion from a trusted provider (like OpenAI) to issue an access token to an agent.

    1. Discovery: The Agent discovers the service's authorization capabilities via GET /.well-known/oauth-protected-resource and GET /.well-known/oauth-authorization-server.
    2. Assertion Request: The Agent requests an audience-specific ID-JAG from the Provider.
    3. Registration: The Agent sends the ID-JAG to the Service via POST /agent/identity with { type: identity_assertion, assertion: ID-JAG }.
    4. Verification: The Service fetches the Provider's JWKS via GET /.well-known/jwks.json, verifies the signature and claims, and returns a service-signed identity_assertion.
    5. Token Exchange: The Agent exchanges the identity_assertion for an access_token via POST /oauth2/token using grant_type=jwt-bearer.
    sequenceDiagram
        actor User
        participant Agent
        participant Provider as Agent Provider
        participant Service
    
        Agent->>Service: GET /api/resource
        Service-->>Agent: 401 Unauthorized<br/>WWW-Authenticate: Bearer resource_metadata="..."
    
        Agent->>Service: GET /.well-known/oauth-protected-resource
        Service-->>Agent: 200 OK (PRM with authorization_servers)
        Agent->>Service: GET /.well-known/oauth-authorization-server
        Service-->>Agent: 200 OK (AS metadata with agent_auth block)
    
        Agent->>User: Consent to assert identity to audience?
        User-->>Agent: Consent granted
    
        Agent->>Provider: Request audience-specific ID-JAG
        Provider-->>Agent: 200 OK (ID-JAG)
    
        Agent->>Service: POST /agent/identity<br/>{ type: identity_assertion, assertion: ID-JAG }
        Service->>Provider: GET /.well-known/jwks.json
        Provider-->>Service: 200 OK (JSON Web Key Set)
        Service->>Service: Verify signature + claims, match user
        Service-->>Agent: 200 OK (identity_assertion)
    
        Agent->>Service: POST /oauth2/token<br/>grant_type=jwt-bearer&assertion=...
        Service-->>Agent: 200 OK (access_token)
  4. Understand the agentic registration protocol roles

    main

    The agentic registration protocol enables agents to authenticate to services on behalf of users using three distinct roles:

    1. Agent: Acts on behalf of a user.
    2. Agent Provider: Mints identity assertions known as ID-JAGs (RFC draft).
    3. Service: Accepts identity assertions and issues credentials. If an agent is not associated with a user identity or the provider doesn't support ID-JAGs, the service falls back to an RFC 8628-style claim ceremony to authenticate the agent itself.

    Agents discover how to interact with a service by reading the AUTH.md file hosted by that service.

  5. Understand the Agentic Registration Workflow

    main

    The service follows a specific sequence for agentic registration. Agents must follow these steps in order and should not skip ahead:

    1. Discover: Locate the resource and authorization server metadata.
    2. Pick a method: Select a registration method based on available identity information.
    3. Register: Submit the registration request to the identity_endpoint.
    4. Claim ceremony (if required): Complete the identity verification process.
    5. Exchange: Swap the registration proof for an access_token.
    6. Call API: Use the token to access the protected resource.
    7. Handle revocation: Monitor for identity revocation events.
  6. How downstream services verify identity assertions

    main

    When a service receives an Identity Assertion (ID-JAG) from an agent provider, it performs verification based on the (iss, sub) pair (issuer and subject). The service follows one of three resolution paths:

    1. Existing delegation: The (iss, sub) pair is already bound to a user. The service returns a service-signed identity_assertion immediately.
    2. JIT-provisioned: No existing user matches the ID-JAG's verified email or phone number. The service creates a new user and binds the delegation.
    3. Step-up required: The (iss, sub) pair is new, but the verified email or phone matches an existing user. To prevent unauthorized account takeover, the service returns a 401 interaction_required along with a claim ceremony, requiring the user to manually confirm the link.

    Rejection Criteria:

    • Missing Verification: Services reject ID-JAGs that contain neither a verified email nor a verified phone number.
    • Expired Session: Services reject ID-JAGs where the auth_time is older than the service's max_age (typically 1 hour). In this case, the service returns 401 login_required. Agents must refresh the user's authentication at the provider before retrying.
  7. How agents discover Agent Auth

    main

    Agents discover the registration pathway via a two-hop discovery process using standard metadata endpoints:

    1. Protected Resource Metadata (PRM): The resource server (API) advertises its authorization servers at .well-known/oauth-protected-resource (per RFC 9728). On a 401 error, the server includes a WWW-Authenticate: Bearer resource_metadata="..." header pointing to this URL.
    2. Authorization Server Metadata: The agent fetches the metadata from the URL found in the PRM at <authorization_servers[0]>/ .well-known/oauth-authorization-server. This metadata contains the agent_auth block, which provides the necessary endpoints for identity, claims, and events.
    // Example Protected Resource Metadata (PRM)
    {
      "resource": "https://api.service.example.com/",
      "resource_name": "Service",
      "resource_logo_uri": "https://service.example.com/logo.png",
      "authorization_servers": ["https://auth.service.example.com/"],
      "scopes_supported": ["api.read", "api.write"],
      "bearer_methods_supported": ["header"]
    }
  8. Sequence Diagram: Verified-Email Identity Assertion

    main

    This flow allows an agent to assert a specific user's identity via email verification.

    1. Registration: Agent calls POST /agent/identity with { type: service_auth, login_hint: email }.
    2. Code Handoff: Service returns a claim_token, user_code, and verification_uri. The agent surfaces the code and URI to the user.
    3. Verification: The user visits the verification_uri, signs in as the asserted email, and completes the claim via POST /agent/identity/claim/complete with { claim_attempt_token, user_code }.
    4. Token Exchange: The Agent polls for the token using grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=... until it receives the final access_token and identity_assertion.
    sequenceDiagram
        actor User
        participant Agent
        participant Service
    
        Agent->>Service: POST /agent/identity<br/>{ type: service_auth, login_hint: email }
        Service-->>Agent: 200 OK (claim_token, claim: user_code, verification_uri)
        Agent-->>User: Surface user_code + verification_uri
        User->>Service: GET verification_uri (signs in as asserted email, lands on /claim)
        User->>Service: POST /agent/identity/claim/complete<br/>{ claim_attempt_token, user_code }
        Service-->>User: 200 OK (claim page confirms)
    
        loop until claimed
          Agent->>Service: POST /oauth2/token<br/>grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=...
          Service-->>Agent: 200 OK (access_token + identity_assertion) | authorization_pending
        end
  9. How the Identity Assertion Sequence works

    main

    The authentication flow follows a specific sequence of discovery and assertion to allow an agent to access a protected resource:

    1. Discovery: The Agent attempts to access a resource and receives a 401 Unauthorized with a WWW-Authenticate header containing resource_metadata. The Agent then performs discovery via /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server to find the authorization server and its agent_auth block.
    2. Consent: The Agent requests consent from the User to assert identity to the specific audience.
    3. Assertion Request: The Agent requests an audience-specific ID-JAG from the Agent Provider.
    4. Identity Assertion: The Agent sends the ID-JAG to the Service via POST /agent/identity with { type: identity_assertion, assertion: ID-JAG }. The Service verifies the assertion by fetching the Provider's JWKS from /.well-known/jwks.json.
    5. Token Acquisition: Once the identity is asserted, the Agent exchanges the assertion for an access token via POST /oauth2/token using grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer.
    sequenceDiagram
        actor User
        participant Agent
        participant Provider as Agent Provider
        participant Service
    
        Agent->>Service: GET /api/resource
        Service-->>Agent: 401 Unauthorized<br/>WWW-Authenticate: Bearer resource_metadata="..."
    
        Agent->>Service: GET /.well-known/oauth-protected-resource
        Service-->>Agent: 200 OK (PRM with authorization_servers)
        Agent->>Service: GET /.well-known/oauth-authorization-server
        Service-->>Agent: 200 OK (AS metadata with agent_auth block)
    
        Agent->>User: Can assert identity to audience?
        User-->>Agent: Consent granted [once, always]
    
        Agent->>Provider: Request audience-specific ID-JAG
        Provider-->>Agent: 200 OK (ID-JAG)
    
        Agent->>Service: POST /agent/identity<br/>{ type: identity_assertion, assertion: ID-JAG }
        Service->>Provider: GET /.well-known/jwks.json
        Provider-->>Service: 200 OK (JSON Web Key Set)
        Service-->>Agent: 200 OK (identity_assertion)
    
        Agent->>Service: POST /oauth2/token<br/>grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=...
        Service-->>Agent: 200 OK (access_token)
  10. Understand the two layers of revocation

    main

    The system employs two independent layers of revocation that affect your ability to access resources:

    1. Credential Layer (RFC 7009): This is controlled by the agent. You can call the revocation_endpoint to kill a specific access_token. If this happens, your identity_assertion is still valid, and you can simply exchange it for a new token.

    2. Registration Layer (RFC 8935): This is provider-driven. The identity provider can push a Security Event Token (SET) to the service's events_endpoint. This invalidates the entire identity assertion and all derived access tokens. You cannot call this; you discover it when the /oauth2/token endpoint returns invalid_grant, at which point you must restart the flow from Step 3.

  11. Sequence Diagram: Anonymous Registration + Claim Ceremony

    main

    This flow allows an agent with no user identity to self-register and later be 'claimed' by a human user.

    1. Registration: Agent calls POST /agent/identity with { type: anonymous }. Service returns an identity_assertion and a claim_token.
    2. Initial Token: Agent exchanges the assertion for an access_token with pre-claim scopes.
    3. Claim Initiation: When a user wants ownership, the Agent calls POST /agent/identity/claim with { claim_token, email }.
    4. Code Handoff: Service returns a user_code and verification_uri. The user visits the URI, signs in, and completes the claim via POST /agent/identity/claim/complete with { claim_attempt_token, user_code }.
    5. Token Refresh: The Agent continuously polls/requests tokens using grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=... until the claim is successful, at which point it receives a post-claim access_token and a v2 identity_assertion.
    sequenceDiagram
        actor User
        participant Agent
        participant Service
    
        Agent->>Service: POST /agent/identity<br/>{ type: anonymous }
        Service-->>Agent: 200 OK (identity_assertion, claim_token)
        Agent->>Service: POST /oauth2/token<br/>grant_type=jwt-bearer&assertion=...
        Service-->>Agent: 200 OK (access_token, pre-claim scope)
    
        Note over Agent: Agent operates with pre-claim scopes
    
        User-->>Agent: Wants to take ownership
        Agent->>Service: POST /agent/identity/claim<br/>{ claim_token, email }
        Service-->>Agent: 200 OK (claim_attempt: user_code, verification_uri)
        Agent-->>User: Surface user_code + verification_uri
        User->>Service: GET verification_uri (signs in, lands on /claim)
        User->>Service: POST /agent/identity/claim/complete<br/>{ claim_attempt_token, user_code }
        Service-->>User: 200 OK (claim page confirms)
    
        loop until claimed
          Agent->>Service: POST /oauth2/token<br/>grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=...
          Service-->>Agent: 200 OK (post-claim access_token + v2 identity_assertion) | authorization_pending
        end
  12. Quickstart agentic registration reference implementation

    main

    To run the local development environment for the agentic registration protocol, use pnpm. This starts both the service (at http://localhost:8000) and the provider (at http://localhost:4000).

    • Use pnpm dev to run both.
    • Use pnpm dev:service to run only the service.
    • Use pnpm dev:provider to run only the provider.

    The service home page provides an interactive walkthrough of the three registration flows.

    pnpm install
    pnpm dev