Agent Sessions¶
This guide covers the complete developer journey for building an agent that uses Nexus for authentication — from registering providers and agents to making scoped requests and handling the full OBO flow.
What you are building¶
An agent that calls Salesforce with read-only access, calls Google Calendar with read-only access, and executes internal business operations only when a human user with the right permission triggers them. No OAuth code in the agent. No credentials in environment variables. No refresh token logic. Nexus handles all of it.
Step 1 — Register your providers (one-time admin)¶
# Salesforce
curl -X POST https://your-gateway.example.com/v1/providers \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "salesforce",
"auth_type": "oauth2",
"client_id": "SF_CLIENT_ID",
"client_secret": "SF_CLIENT_SECRET",
"auth_url": "https://login.salesforce.com/services/oauth2/authorize",
"token_url": "https://login.salesforce.com/services/oauth2/token",
"scopes": ["crm:contacts:read", "crm:contacts:write"],
"params": { "skip_scope_on_exchange": true }
}'
# Google Calendar
curl -X POST https://your-gateway.example.com/v1/providers \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"name": "google-calendar",
"auth_type": "oauth2",
"client_id": "GOOGLE_CLIENT_ID",
"client_secret": "GOOGLE_CLIENT_SECRET",
"issuer": "https://accounts.google.com",
"enable_discovery": true,
"scopes": ["https://www.googleapis.com/auth/calendar.events.readonly"]
}'
Step 2 — Register your agents (one-time admin)¶
Each agent is registered with its maximum allowed scope set. An agent can never request more than what is declared here.
# CRM agent — read-only access to Salesforce contacts
curl -X POST https://your-gateway.example.com/admin/v1/agents \
-H "X-API-Key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "crm-agent",
"description": "Reads customer records from Salesforce",
"allowed_scopes": ["crm:contacts:read"]
}'
# Calendar agent
curl -X POST https://your-gateway.example.com/admin/v1/agents \
-H "X-API-Key: your-api-key" \
-d '{
"agent_id": "calendar-agent",
"description": "Reads calendar events",
"allowed_scopes": ["https://www.googleapis.com/auth/calendar.events.readonly"]
}'
# Ops agent — custom scopes for internal operations
curl -X POST https://your-gateway.example.com/admin/v1/agents \
-H "X-API-Key: your-api-key" \
-d '{
"agent_id": "ops-agent",
"description": "Executes authorized internal financial operations",
"allowed_scopes": ["acme:gliding", "acme:flaring"]
}'
Step 3 — Connect users to providers¶
Each user who will authorize an agent must complete the OAuth flow for their account. Your backend initiates this:
curl -X POST https://your-gateway.example.com/v1/request-connection \
-H "X-API-Key: your-api-key" \
-d '{
"workspace_id": "user_sarah",
"provider_id": "SALESFORCE_PROVIDER_UUID",
"scopes": ["crm:contacts:read", "crm:contacts:write"],
"return_url": "https://your-app.com/connections/callback"
}'
Redirect the user to the returned auth_url. After they authorize, poll check-connection until status is active. Store the connection_id against the user.
Step 4 — Agent requests a scoped session¶
When the agent needs to call Salesforce for a user, it requests a session. In Go:
session, err := nexusClient.RequestAgentSession(ctx, oauthsdk.AgentSessionInput{
AgentID: "crm-agent",
ProviderName: "salesforce",
Scopes: []string{"crm:contacts:read"},
TTL: 15 * time.Minute,
})
if err != nil {
return err
}
defer nexusClient.CloseAgentSession(ctx, session.SessionID)
resp, err := http.Get("https://api.salesforce.com/v1/contacts?q=" + filter,
// session.AccessToken is scoped to crm:contacts:read only
withBearer(session.AccessToken),
)
In Python:
session = nexus.request_agent_session(
agent_id="crm-agent",
provider="salesforce",
scopes=["crm:contacts:read"],
)
try:
resp = httpx.get(
"https://api.salesforce.com/v1/contacts",
params={"q": filter},
headers={"Authorization": f"Bearer {session.access_token}"},
)
return resp.json()["records"]
finally:
nexus.close_agent_session(session.session_id)
In TypeScript:
const session = await nexus.requestAgentSession({
agentId: 'crm-agent',
provider: 'salesforce',
scopes: ['crm:contacts:read'],
ttl: 900,
})
try {
const resp = await fetch('https://api.salesforce.com/v1/contacts', {
headers: { Authorization: `Bearer ${session.accessToken}` },
})
return resp.json()
} finally {
await nexus.closeAgentSession(session.sessionId)
}
Step 5 — OBO session for user-triggered operations¶
When a human user triggers an operation that requires their specific authorization, use an OBO session. The Broker validates the user's permissions before issuing the session.
def run_gliding(user_token: str, customer_ids: list) -> dict:
# Raises NexusAuthError if the user does not have acme:gliding permission
obo = nexus.request_obo_session(
agent_id="ops-agent",
provider="internal-ops",
scopes=["acme:gliding"],
user_context_token=user_token,
)
try:
return internal_ops.glide(
customer_ids=customer_ids,
tenant_id=obo.tenant_id, # enforced downstream
clearance_level=obo.clearance_level,
)
finally:
nexus.close_agent_session(obo.session_id)
The Broker validates both gates before issuing the OBO session:
- The user must have acme:gliding in their permissions (verified via your BACKEND_AUTH_URL)
- The ops-agent must have acme:gliding in its allowed_scopes
If either check fails, the request returns 403.
What the agent never wrote¶
| Responsibility | Who handles it |
|---|---|
| OAuth implementation | Broker |
| Refresh token logic | Broker |
| Token storage | Broker |
| Scope enforcement | Broker — at session request time |
| JWT validation for OBO | Broker — calls your backend, extracts claims |
| User context stamping | Broker — stamps acting_for, tenant_id, clearance_level |
| Credential rotation | Broker |
| Token expiry | Broker — session has explicit expires_at |
Session vs connection — when to use which¶
Use connection tokens (GetToken) |
Use agent sessions (RequestAgentSession) |
|---|---|
| Agent has no registered identity | Agent is registered with allowed_scopes |
| You want maximum flexibility | You want enforced least-privilege |
| Bridge-based persistent connections | Discrete per-operation credential grants |
| Existing integrations | New agent builds |