Agent DocsDeveloper documentation
Builder Guide

OBO Token Exchange

RFC 8693 delegated authentication for agent-to-service calls

OBO (On Behalf Of) Token Exchange

Comprehensive guide to the platform's RFC 8693-compliant token exchange mechanism, which enables agents to act on behalf of users when calling downstream services.


Overview

What Is OBO Token Exchange?

On Behalf Of (OBO) token exchange allows the platform to obtain scoped, short-lived tokens from the identity provider (Keycloak) so that agents can call downstream services (SAP, Salesforce, ServiceNow, etc.) as the user rather than as a generic service account. This preserves the user's identity and authorization context through the entire call chain.

Why Is It Needed?

Enterprise systems enforce their own authorization. When an agent calls SAP S/4HANA to retrieve purchase orders, SAP needs to know which user is requesting the data so it can enforce SAP-level entitlements. A generic service account would either have too much access (security risk) or too little (broken functionality). OBO solves this by exchanging the user's JWT for a delegated token scoped to the target system.

RFC 8693 Compliance

The implementation follows RFC 8693 (OAuth 2.0 Token Exchange). The key parameters:

RFC 8693 ParameterPlatform Value
grant_typeurn:ietf:params:oauth:grant-type:token-exchange
subject_tokenUser's JWT from auth context
subject_token_typeurn:ietf:params:oauth:token-type:access_token
actor_tokenAgent's JWT-SVID (when available)
actor_token_typeurn:ietf:params:oauth:token-type:jwt
audienceTarget service identifier (e.g., sap-s4hana)
scopeConstrained scopes for the delegated token

Supported Exchange Flows

FlowUse CaseIdP Requirement
RFC 8693 (generic)Keycloak token exchange for any OIDC-capable downstream serviceKeycloak with Token Exchange SPI enabled
SAP Destination ServiceOAuth2UserTokenExchange for SAP BTP systemsSAP BTP Destination Service + Keycloak

Use Cases

  • SAP S/4HANA -- Agent reads purchase orders, creates invoices as the user
  • Salesforce -- Agent queries CRM records respecting field-level security
  • ServiceNow -- Agent creates/updates tickets under the user's identity
  • Microsoft Graph -- Agent accesses SharePoint/Teams resources with user consent
  • Custom APIs -- Any OAuth2-protected internal service

Architecture

End-to-End Sequence

Scroll to zoom · Drag to pan · 100%

Component Responsibilities

Scroll to zoom · Drag to pan · 100%

Delegation Chains

When Agent A delegates to Agent B, the OBO token must be forwarded through the chain. The platform tracks this via the delegationChain array and the X-Agent-Actor header.

User to Agent A to Agent B

Scroll to zoom · Drag to pan · 100%

Delegation Depth Control

Scroll to zoom · Drag to pan · 100%

The platform enforces configurable delegation depth limits via the maxDelegationDepth setting on interaction policies. The delegationChain array grows with each hop. When delegationChain.length >= maxDelegationDepth, further delegation is blocked.

Default depth limits by workflow type:

Workflow TypeDefault Max Depth
Simple agent3
Multi-agent orchestration5
SRE incident response4

Security Model

Encryption at Rest

All cached OBO tokens are encrypted using AES-256-GCM before being stored in the tenantCredentials table. The encryption is handled by encryptCredential() and decryptCredential() from the credential resolver module.

Scroll to zoom · Drag to pan · 100%

SPIFFE Identity

Each agent has a SPIFFE ID: spiffe://{trustDomain}/tenant/{tenantId}/agent/{agentId}. This identity is used as the actor_token in the RFC 8693 exchange when available as a JWT-SVID, establishing a cryptographic proof of which agent is acting on behalf of the user.

Scoped Delegation

OBO tokens are scoped in three ways:

  1. Audience -- The token is only valid for the specified target service
  2. Scopes -- Only the requested scopes are granted (e.g., read:orders but not write:orders)
  3. Time -- Tokens have a short TTL set by Keycloak

Cache Key Derivation

Cache keys are derived using SHA-256 to prevent collisions and information leakage:

cacheKey = SHA-256(userId + ":" + agentId + ":" + audience + ":" + sortedScopes).slice(0, 32)

This ensures that different user/agent/audience/scope combinations never share cached tokens.

60-Second Expiry Buffer

When checking cached tokens, the platform subtracts 60 seconds from the expiry time. This prevents race conditions where a token that is technically valid could expire between retrieval and use.

Audit Logging

Every OBO token exchange is audit-logged with:

  • Agent and user identifiers
  • Target audience and scopes
  • Delegation chain depth
  • Risk level (low for direct, medium for chain depth 1-2, high for depth 3+)

Admin Dashboard

OBO Token Monitoring

The Admin Console provides real-time visibility into OBO token activity.

Endpoints:

EndpointMethodDescription
/api/admin/obo/tokensGETList all active OBO tokens with optional agentId and audience filters
/api/admin/obo/tokens/:agentIdGETList OBO tokens for a specific agent
/api/admin/obo/statsGETToken exchange statistics (active/expired/revoked counts, per-agent breakdown)
/api/admin/obo/revokePOSTRevoke OBO tokens for a specific agent (and optionally a specific user)

Stats Response Structure

{
  "data": {
    "totalActive": 12,
    "totalExpired": 45,
    "totalRevoked": 3,
    "byAgent": [
      { "agentId": "agent-finance-1", "count": 8 },
      { "agentId": "agent-sap-reader", "count": 4 }
    ]
  }
}

Bulk Revocation

Admins can revoke all OBO tokens for an agent (e.g., after a security incident):

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "agent-finance-1"}' \
  http://localhost:4000/api/admin/obo/revoke

To revoke tokens for a specific user-agent pair:

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"agentId": "agent-finance-1", "userId": "user-jane-1"}' \
  http://localhost:4000/api/admin/obo/revoke

Configuration

Agent oboTargets

Agents declare their OBO requirements in the config.oboTargets array. This is configured in the Studio Agent Designer (Safety tab) or via the API.

{
  "name": "Finance Agent",
  "config": {
    "oboTargets": [
      {
        "audience": "sap-s4hana",
        "scopes": ["read:orders", "write:invoices"],
        "description": "Access SAP purchase orders and invoices",
        "type": "rfc8693"
      },
      {
        "audience": "salesforce",
        "scopes": ["api", "refresh_token"],
        "description": "Query Salesforce CRM records",
        "type": "rfc8693"
      }
    ]
  }
}

OBOTarget Schema

FieldTypeRequiredDescription
audiencestringYesTarget service identifier (e.g., sap-s4hana)
scopesstring[]YesOAuth scopes to request
descriptionstringNoHuman-readable purpose
typerfc8693 or sap-destinationNoExchange flow type (default: rfc8693)
sapDestinationNamestringNoSAP BTP Destination Service name (SAP flow only)

Keycloak Setup

Keycloak must be configured to support token exchange:

  1. Enable Token Exchange -- The realm must have the Token Exchange SPI enabled
  2. Create Confidential Client -- A client for the API Gateway with client_credentials and token-exchange grant types
  3. Configure Audience -- Register each downstream service as a client/resource in Keycloak
  4. Map Scopes -- Create scope mappings between the platform client and target service scopes
  5. Set Policies -- Define Token Exchange policies that allow the platform client to exchange tokens for the target audiences

Environment Variables

VariableRequiredDescription
OBO_TOKEN_ENDPOINTYesKeycloak token endpoint (e.g., https://keycloak.example.com/realms/platform/protocol/openid-connect/token)
OBO_CLIENT_IDYesPlatform's confidential client ID
OBO_CLIENT_SECRETYesClient secret (store in Vault for production)
SPIFFE_TRUST_DOMAINNoSPIFFE trust domain (default: agentic-platform)

How Tokens Are Delivered to Agents

When the External Agent Executor calls an agent that has oboTargets, it sets HTTP headers:

X-OBO-Token-sap-s4hana: Bearer <delegated-token>
X-OBO-Token-salesforce: Bearer <delegated-token>
X-Agent-Actor: {"sub":"spiffe://agentic-platform/tenant/t1/agent/a1","client_id":"api-gateway","delegation_chain":[]}

The agent reads the X-OBO-Token-{audience} header and uses it when calling the corresponding downstream service.


Key Source Files

FileDescription
services/api-gateway/src/lib/obo-token-exchange.tsCore OBO exchange, caching, revocation, and stats
services/api-gateway/src/middleware/obo-context-middleware.tsExtracts user token for downstream OBO exchange
services/api-gateway/src/lib/external-agent-executor.tsSets X-OBO-Token-* headers when calling agents
services/api-gateway/src/routes/admin.tsAdmin monitoring and revocation endpoints
packages/types/src/agent.tsOBOTarget and OBOTargetType type definitions
apps/studio/app/(main)/agents/components/safety-tab.tsxStudio UI for configuring oboTargets
services/api-gateway/src/lib/schemas/agent-config.schema.tsZod validation for oboTargets