-
-
Notifications
You must be signed in to change notification settings - Fork 204
Description
RFC: User-Delegated Agent Access
Phase: 4 — AI-Era Auth
Priority: P2 — Medium
Estimated Effort: Medium
Depends on: MCP Auth (#516), Fine-Grained Permissions (#508)
Problem Statement
Users need to authorize AI agents to act on their behalf with limited, revocable scope. When a user asks an AI agent to "manage my calendar" or "process my invoices," the agent needs a token that represents the user's identity but is constrained to only the permissions the user explicitly granted. WorkOS and Clerk both support this pattern.
Proposed Solution
1. Delegated Authorization Flow
Standard OAuth flow with agent-specific scope constraints:
1. Agent initiates OAuth flow on behalf of user:
GET /authorize?
client_id={agent_client_id}&
scope=calendar:read calendar:write&
response_type=code&
code_challenge=...
2. User authenticates (if not already logged in)
3. Consent screen shows agent-specific permissions:
"Calendar Agent wants to:"
☑ Read your calendar events
☑ Create and modify calendar events
[Allow for 24 hours] [Allow for 7 days] [Deny]
4. User approves → authorization code issued
5. Agent exchanges code for token:
POST /oauth/token
grant_type=authorization_code&code=...
6. Token includes user identity + constrained scope:
{
"sub": "user_123",
"client_id": "app_calendar_agent",
"scope": "calendar:read calendar:write",
"delegated": true,
"delegated_at": 1711800000,
"delegation_expires_at": 1711886400
}
2. Scope Downscoping
Agent token permissions = intersection of:
- User's actual permissions
- Agent's allowed scopes (from Application registration)
- User-approved scopes (from consent)
func computeDelegatedScopes(userPermissions []string, agentAllowedScopes []string, userApprovedScopes []string) []string {
// Token can only have permissions that exist in ALL three sets
return intersect(userPermissions, agentAllowedScopes, userApprovedScopes)
}If a user's permissions are later reduced (e.g., admin removes a role), the agent's delegated token automatically loses those permissions at validation time.
3. Time-Limited Delegation
Users choose how long the delegation lasts at the consent screen:
| Option | TTL | Use Case |
|---|---|---|
| One-time | Token valid for single use | Quick task |
| 24 hours | 86400s | Day-long agent task |
| 7 days | 604800s | Week-long project |
| 30 days | 2592000s | Ongoing automation |
| Until revoked | No expiry (refresh token based) | Persistent agent access |
Schema for tracking delegation grants:
type DelegationGrant struct {
ID string `json:"id" gorm:"primaryKey;type:char(36)"`
UserID string `json:"user_id" gorm:"type:char(36);index:idx_delegation_user"`
ClientID string `json:"client_id" gorm:"type:char(36);index:idx_delegation_client"`
ApplicationID string `json:"application_id" gorm:"type:char(36)"`
Scopes string `json:"scopes" gorm:"type:text"`
ExpiresAt int64 `json:"expires_at"` // 0 = until revoked
IsActive bool `json:"is_active" gorm:"type:bool;default:true"`
LastUsedAt int64 `json:"last_used_at"`
CreatedAt int64 `json:"created_at" gorm:"autoCreateTime"`
}4. Revocation
Users can revoke agent access at any time:
User-facing GraphQL API:
type DelegatedAgent {
id: ID!
application: Application!
scopes: [String!]!
expires_at: Int64
last_used_at: Int64
is_active: Boolean!
created_at: Int64!
}
type Query {
delegated_agents: [DelegatedAgent!]! # List agents with active access
}
type Mutation {
revoke_agent_access(id: ID!): Response! # Revoke specific delegation
revoke_all_agent_access: Response! # Revoke all agent delegations
}On revocation:
- Mark
DelegationGrant.IsActive = false - Invalidate all tokens issued under this grant (delete from memory store)
- Audit log:
user.agent_access_revoked
5. Active Agent Sessions Dashboard
Users see which agents have active access in their account settings:
Your Connected Agents:
┌─────────────────────────────────────────────────────┐
│ Calendar Agent │
│ Scopes: calendar:read, calendar:write │
│ Granted: Mar 15, 2026 · Expires: Apr 14, 2026 │
│ Last used: 2 hours ago │
│ [Revoke] │
├─────────────────────────────────────────────────────┤
│ Billing Agent │
│ Scopes: invoices:read │
│ Granted: Mar 20, 2026 · No expiry │
│ Last used: 5 minutes ago │
│ [Revoke] │
└─────────────────────────────────────────────────────┘
This is implemented in the web/app/ React frontend and uses the delegated_agents query.
6. Notification on New Agent Access
When a new agent delegation is created, send email notification:
Subject: New agent access granted to your account
"Calendar Agent" was granted access to your account:
- Read your calendar events
- Create and modify calendar events
Expires: April 14, 2026
If you didn't authorize this, revoke access immediately:
[Revoke Access]
CLI Configuration Flags
--enable-delegated-agent-access=true # Enable user-delegated agent flow
--max-delegation-duration=2592000 # Max delegation TTL in seconds (30 days)
--notify-on-agent-delegation=true # Email notification on new delegation
Testing Plan
- Integration test: full delegation flow (authorize → consent → token → use → revoke)
- Test scope downscoping (intersection of user + agent + approved scopes)
- Test time-limited delegation expiry
- Test revocation invalidates all tokens for the grant
- Test user permission reduction affects delegated token
- Test delegation listing and active session display
- Test email notification on new delegation