---
title: Workload Authorization Grant, chained through the enterprise IdP (draft-carleton-workload-authz-grant-01)
---
sequenceDiagram
    autonumber

    actor IdPAdmin as Enterprise IdP admin
    participant AP as Agent Platform<br/>(creates agents, signs their grants)
    participant Agent
    participant IdP as Enterprise Workload IdP<br/>(ZipIdP, WAG issuer /wag/{slug})
    actor AppAdmin as Application admin
    participant AS as Application AS<br/>(workload-api /token)
    participant API as Application API<br/>(/api/*)

    rect rgba(128, 128, 128, 0.12)
        Note over IdPAdmin, API: One-time setup (nothing about individual agents is exchanged)

        AP-->>IdPAdmin: Agent Platform's issuer identifier and public JWKS
        IdPAdmin->>IdP: Create a WAG issuer for the Agent Platform in /admin/wag,<br/>registering its iss, JWKS or jwks_uri,<br/>and claims to pass through (e.g. team)
        IdP-->>IdPAdmin: The WAG issuer's identifier https://host/wag/{slug}<br/>plus metadata and JWKS URLs

        AppAdmin->>AS: Register the WAG issuer in config.json:<br/>issuer, keys from metadata, scopes, claim_scopes
        AS->>IdP: GET /.well-known/oauth-authorization-server/wag/{slug}
        IdP-->>AS: issuer (must match exactly), jwks_uri
        AS->>IdP: GET /wag/{slug}/jwks
        IdP-->>AS: The WAG issuer's public keys (cached 1h)
    end

    rect rgba(128, 128, 128, 0.06)
        Note over AP, Agent: Per agent: the Agent Platform creates an agent and names it.<br/>Nothing is sent to the IdP, the application's AS or its API.
    end

    rect rgba(80, 140, 255, 0.10)
        Note over AP, API: Per access

        Agent->>AP: Ask for a grant
        AP-->>Agent: Agent Platform grant, signed with the Agent Platform's key<br/>iss = Agent Platform, sub = agent id at the Platform<br/>aud = the WAG issuer, exp, iat, jti, claims

        Agent->>IdP: POST /wag/{slug}/token (token exchange)<br/>subject_token = Agent Platform grant<br/>subject_token_type and requested_token_type = token-type:wag<br/>audience = application AS issuer<br/>resource and scope (optional)
        activate IdP
        Note right of IdP: The WAG issuer checks iss is its Agent Platform<br/>Verify with that Platform's keys only<br/>Check aud, exp, iat, jti (single use)
        Note right of IdP: First sight of this sub?<br/>Record agent with new sub agent_{random},<br/>never reassigned
        Note right of IdP: The WAG issuer signs its grant with its current key<br/>iss = the WAG issuer, sub = agent_..., aud = application AS, resource if requested<br/>exp capped near the Agent Platform grant's exp<br/>plus passthrough and static claims
        IdP-->>Agent: access_token = the WAG issuer's grant<br/>issued_token_type token-type:wag, token_type N_A
        deactivate IdP

        Agent->>AS: POST /token<br/>grant_type=jwt-bearer, assertion=the WAG issuer's grant<br/>resource=AS/api (optional)
        activate AS
        Note right of AS: Match iss (and tenant claim) to one registered WAG issuer<br/>Verify with that issuer's keys only<br/>(unknown kid: refetch its JWKS once)
        Note right of AS: Check aud = this issuer, exp, iat, sub<br/>jti single use per issuer<br/>Record agent under (issuer, sub)
        Note right of AS: Scopes = requested within policy<br/>(base scopes plus claim_scopes, e.g. team=core)
        AS-->>Agent: Access token (at+jwt), Bearer<br/>aud = API, client_id = the WAG issuer<br/>no refresh token
        deactivate AS

        Agent->>API: GET /api/issues<br/>Authorization: Bearer access token
        activate API
        Note right of API: Verify at+jwt signature, iss, aud, exp<br/>Check scope issues:read
        API-->>Agent: 200 issues
        deactivate API

        Agent->>API: POST /api/issues
        alt Token has issues:write
            API-->>Agent: 200 issue created
        else Scope missing
            API-->>Agent: 403 insufficient_scope<br/>WWW-Authenticate names issues:write
        end
    end

    rect rgba(255, 90, 90, 0.08)
        Note over Agent, AS: Refusals: error_description says who must act (section 7)
        alt Agent Platform not the one this issuer trusts, or agent disabled
            IdP-->>Agent: 400 invalid_grant (enterprise IdP admin must act)
        else WAG issuer not registered at the application, or new-agent cap reached
            AS-->>Agent: 400 invalid_grant (application admin must act)
        else Bad signature, wrong aud, expired, or replayed grant
            AS-->>Agent: 400 invalid_grant (issuer of the grant must act)
        else Scope outside policy
            AS-->>Agent: 400 invalid_scope (application admin must act)
        end
    end
Mermaid source
---
title: Workload Authorization Grant, chained through the enterprise IdP (draft-carleton-workload-authz-grant-01)
---
sequenceDiagram
    autonumber

    actor IdPAdmin as Enterprise IdP admin
    participant AP as Agent Platform<br/>(creates agents, signs their grants)
    participant Agent
    participant IdP as Enterprise Workload IdP<br/>(ZipIdP, WAG issuer /wag/{slug})
    actor AppAdmin as Application admin
    participant AS as Application AS<br/>(workload-api /token)
    participant API as Application API<br/>(/api/*)

    rect rgba(128, 128, 128, 0.12)
        Note over IdPAdmin, API: One-time setup (nothing about individual agents is exchanged)

        AP-->>IdPAdmin: Agent Platform's issuer identifier and public JWKS
        IdPAdmin->>IdP: Create a WAG issuer for the Agent Platform in /admin/wag,<br/>registering its iss, JWKS or jwks_uri,<br/>and claims to pass through (e.g. team)
        IdP-->>IdPAdmin: The WAG issuer's identifier https://host/wag/{slug}<br/>plus metadata and JWKS URLs

        AppAdmin->>AS: Register the WAG issuer in config.json:<br/>issuer, keys from metadata, scopes, claim_scopes
        AS->>IdP: GET /.well-known/oauth-authorization-server/wag/{slug}
        IdP-->>AS: issuer (must match exactly), jwks_uri
        AS->>IdP: GET /wag/{slug}/jwks
        IdP-->>AS: The WAG issuer's public keys (cached 1h)
    end

    rect rgba(128, 128, 128, 0.06)
        Note over AP, Agent: Per agent: the Agent Platform creates an agent and names it.<br/>Nothing is sent to the IdP, the application's AS or its API.
    end

    rect rgba(80, 140, 255, 0.10)
        Note over AP, API: Per access

        Agent->>AP: Ask for a grant
        AP-->>Agent: Agent Platform grant, signed with the Agent Platform's key<br/>iss = Agent Platform, sub = agent id at the Platform<br/>aud = the WAG issuer, exp, iat, jti, claims

        Agent->>IdP: POST /wag/{slug}/token (token exchange)<br/>subject_token = Agent Platform grant<br/>subject_token_type and requested_token_type = token-type:wag<br/>audience = application AS issuer<br/>resource and scope (optional)
        activate IdP
        Note right of IdP: The WAG issuer checks iss is its Agent Platform<br/>Verify with that Platform's keys only<br/>Check aud, exp, iat, jti (single use)
        Note right of IdP: First sight of this sub?<br/>Record agent with new sub agent_{random},<br/>never reassigned
        Note right of IdP: The WAG issuer signs its grant with its current key<br/>iss = the WAG issuer, sub = agent_..., aud = application AS, resource if requested<br/>exp capped near the Agent Platform grant's exp<br/>plus passthrough and static claims
        IdP-->>Agent: access_token = the WAG issuer's grant<br/>issued_token_type token-type:wag, token_type N_A
        deactivate IdP

        Agent->>AS: POST /token<br/>grant_type=jwt-bearer, assertion=the WAG issuer's grant<br/>resource=AS/api (optional)
        activate AS
        Note right of AS: Match iss (and tenant claim) to one registered WAG issuer<br/>Verify with that issuer's keys only<br/>(unknown kid: refetch its JWKS once)
        Note right of AS: Check aud = this issuer, exp, iat, sub<br/>jti single use per issuer<br/>Record agent under (issuer, sub)
        Note right of AS: Scopes = requested within policy<br/>(base scopes plus claim_scopes, e.g. team=core)
        AS-->>Agent: Access token (at+jwt), Bearer<br/>aud = API, client_id = the WAG issuer<br/>no refresh token
        deactivate AS

        Agent->>API: GET /api/issues<br/>Authorization: Bearer access token
        activate API
        Note right of API: Verify at+jwt signature, iss, aud, exp<br/>Check scope issues:read
        API-->>Agent: 200 issues
        deactivate API

        Agent->>API: POST /api/issues
        alt Token has issues:write
            API-->>Agent: 200 issue created
        else Scope missing
            API-->>Agent: 403 insufficient_scope<br/>WWW-Authenticate names issues:write
        end
    end

    rect rgba(255, 90, 90, 0.08)
        Note over Agent, AS: Refusals: error_description says who must act (section 7)
        alt Agent Platform not the one this issuer trusts, or agent disabled
            IdP-->>Agent: 400 invalid_grant (enterprise IdP admin must act)
        else WAG issuer not registered at the application, or new-agent cap reached
            AS-->>Agent: 400 invalid_grant (application admin must act)
        else Bad signature, wrong aud, expired, or replayed grant
            AS-->>Agent: 400 invalid_grant (issuer of the grant must act)
        else Scope outside policy
            AS-->>Agent: 400 invalid_scope (application admin must act)
        end
    end