-
-
Notifications
You must be signed in to change notification settings - Fork 204
Description
RFC: Agent-to-Agent (A2A) Protocol Support
Phase: 4 — AI-Era Auth
Priority: P2 — Medium
Estimated Effort: Medium
Depends on: M2M Auth (#509), Token Exchange (#521 — to be created)
Problem Statement
Google's Agent-to-Agent (A2A) protocol (backed by 50+ partners including Salesforce, SAP, Atlassian) defines how AI agents discover, authenticate, and communicate with each other. Authorizer can serve as the identity backbone for agent ecosystems — issuing agent identities, managing agent-to-agent trust, and auditing agent actions. No self-hosted auth platform currently offers this.
Proposed Solution
1. Agent Identity via Service Accounts
Agents are registered as specialized M2M applications (extends #509) with agent-specific metadata:
Enhanced Application schema (add fields to existing Application from #509):
// Additional fields on Application schema:
AgentType string `json:"agent_type" gorm:"type:varchar(50)"` // "ai_agent", "service", "automation"
AgentCardURL string `json:"agent_card_url" gorm:"type:text"` // URL where agent card is published
Capabilities string `json:"capabilities" gorm:"type:text"` // JSON array of capability descriptors
DelegationRules string `json:"delegation_rules" gorm:"type:text"` // JSON: which agents/scopes this agent can delegate to
MaxDelegationDepth int `json:"max_delegation_depth" gorm:"default:3"` // prevent infinite delegation chains2. Agent Card Generation
A2A protocol requires agents to publish an Agent Card at /.well-known/agent.json.
Authorizer generates Agent Cards for registered agents:
New endpoint: GET /.well-known/agents/:client_id/agent.json
{
"name": "billing-agent",
"description": "Handles billing and invoice operations",
"url": "https://billing-agent.example.com",
"provider": {
"organization": "Acme Corp",
"url": "https://acme.com"
},
"version": "1.0.0",
"capabilities": {
"streaming": false,
"pushNotifications": false,
"stateTransitionHistory": true
},
"authentication": {
"schemes": ["OAuth2"],
"credentials": null
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json"],
"skills": [
{
"id": "create-invoice",
"name": "Create Invoice",
"description": "Creates a new invoice for a customer",
"inputModes": ["application/json"],
"outputModes": ["application/json"]
}
]
}GraphQL admin API for agent card configuration:
type Mutation {
_update_agent_card(params: UpdateAgentCardInput!): Application!
}
input UpdateAgentCardInput {
application_id: ID!
agent_type: String
capabilities: Map
skills: [AgentSkillInput!]
}3. Agent-Specific OAuth Scopes
Scopes for agent operations:
| Scope | Purpose |
|---|---|
agent:invoke |
Invoke another agent's skills |
agent:delegate |
Delegate a task to another agent on behalf of a user |
agent:read_context |
Read shared context/state from another agent |
agent:write_context |
Write to shared context |
agent:discover |
Discover available agents and their capabilities |
These scopes are enforced when agents present tokens to each other.
4. Short-Lived Agent Tokens
Agent-to-agent tokens have intentionally short lifetimes to minimize blast radius:
- Default TTL: 5 minutes (configurable per agent)
- Max TTL: 30 minutes
- Automatic refresh via client_credentials grant (agents re-authenticate frequently)
- No refresh tokens for agent-to-agent flows
// In token generation for agent clients:
if app.AgentType != "" {
// Enforce short TTL
ttl := min(app.TokenExpiresIn, maxAgentTokenTTL)
claims["exp"] = time.Now().Add(time.Duration(ttl) * time.Second).Unix()
claims["agent_type"] = app.AgentType
claims["grant_type"] = "client_credentials"
}5. Token Exchange for Agent Delegation (RFC 8693)
When Agent A needs to act on behalf of a user through Agent B:
POST /oauth/token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token={user's access token}
subject_token_type=urn:ietf:params:oauth:token-type:access_token
requested_token_type=urn:ietf:params:oauth:token-type:access_token
audience={Agent B's client_id}
scope=agent:invoke
Response: A new token scoped for Agent B, with delegation chain:
{
"sub": "user_123",
"act": {
"sub": "app_agent_a",
"act": {
"sub": "app_agent_b"
}
},
"scope": "agent:invoke",
"aud": "app_agent_b",
"delegation_depth": 2,
"original_actor": "user_123"
}Delegation chain tracking: The act (actor) claim creates a verifiable chain showing who delegated to whom. max_delegation_depth prevents infinite chains.
6. Agent Audit Trail
All agent actions logged with enhanced metadata:
auditProvider.Log(ctx, audit.AuditEvent{
ActorID: agentClientID,
ActorType: "agent",
Action: "agent.invoked",
ResourceType: "agent",
ResourceID: targetAgentClientID,
Metadata: map[string]interface{}{
"delegating_user_id": originalUserID,
"skill_invoked": "create-invoice",
"scope_used": "agent:invoke",
"delegation_depth": 2,
"delegation_chain": []string{"user_123", "app_agent_a", "app_agent_b"},
},
})7. Agent Discovery Endpoint
New endpoint: GET /agents — lists registered agents (filtered by capabilities)
{
"agents": [
{
"client_id": "app_billing_agent",
"name": "Billing Agent",
"description": "Handles billing operations",
"agent_card_url": "https://auth.example.com/.well-known/agents/app_billing_agent/agent.json",
"capabilities": ["create-invoice", "get-balance"],
"authentication": {"type": "oauth2", "token_endpoint": "https://auth.example.com/oauth/token"}
}
]
}Requires agent:discover scope.
CLI Configuration Flags
--enable-agent-auth=false # Enable A2A features
--agent-token-default-ttl=300 # Default agent token TTL (5 min)
--agent-token-max-ttl=1800 # Max agent token TTL (30 min)
--agent-max-delegation-depth=3 # Maximum delegation chain depth
Testing Plan
- Integration test: agent registration with agent card generation
- Test agent-to-agent token exchange with delegation chain
- Test delegation depth enforcement (reject at max depth)
- Test short-lived token TTL enforcement
- Test agent audit trail captures full delegation chain
- Test agent discovery endpoint with scope filtering