About this service
Enterprise identity broker for delegated app access to corporate services through Okta SSO. Grant a subset of your permissions to an app, agent, or MCP server.
How it works
Apps authenticate using the OAuth 2.0 Device Authorization Grant (RFC 8628). The app requests a device code, a human approves it via SSO, and the app receives a session token to access services.
Manage your connections
How to interact with it
Here are the steps you need to follow to integrate this service into your app.
All authenticated endpoints require a Bearer session token obtained through the device authorization flow below.
Discovery & configuration
Use this to determine the service names to plug into the requests below.
# Machine-readable service metadata
curl -s https://agent-auth.dev.tiis.io/config | jq '.services[].name'
curl -s https://agent-auth.tiis.io/config | jq '.services[].name'
"github_enterprise_server"
"github_enterprise_cloud"
"office365"
"zoom"
"google"
Step 1. Start a device authorization flow:
# Request a device code for a service
curl -s -X POST https://agent-auth.dev.tiis.io/device/authorize \
-H "Content-Type: application/json" \
-d '{"service": "google"}'
A successful response (HTTP 200) looks like:
{
"device_code": "<DEVICE_CODE>",
"user_code": "ABCD-EFGH",
"verification_uri": "https://agent-auth.dev.tiis.io/activate",
"verification_uri_complete": "https://agent-auth.dev.tiis.io/activate?code=ABCD-EFGH",
"expires_in": 600,
"interval": 5
}
Hold on to device_code — you'll need it in Step 3. interval is the minimum number of seconds to wait between polls in Step 3.
What's happening: this opens a pending authorization on DIS and hands back a device_code (your handle for polling) plus a user_code and verification URL for the human. Nothing has been authenticated or consented yet — you've only started the flow.
Step 2. Direct the human user to open verification_uri_complete in their browser. They'll authenticate via Okta SSO and then approve the requested service.
What's happening: this is where trust is actually established, and it is strictly between the human and DIS — not your app. The human proves their identity through Okta SSO and consents to DIS brokering tokens for the requested service on their behalf. Your app is granted nothing directly; DIS becomes the authorized party.
Step 3. Poll for completion using the device_code from Step 1:
# Poll every {interval} seconds until the human approves
curl -s -X POST https://agent-auth.dev.tiis.io/device/token \
-H "Content-Type: application/json" \
-d '{"service": "google", "device_code": "<DEVICE_CODE>"}'
While the human has not yet approved, the response (HTTP 400, per RFC 8628 §3.5) is:
{"error": "authorization_pending"}
If the flow expires before approval, HTTP 410 with {"error":"expired_token"}. If the human denies, HTTP 403 with {"error":"access_denied"}. On success (HTTP 200) the response is:
{
"session_token": "<SESSION_TOKEN>",
"user_id": "00u...",
"expires_in": 3600,
"email": "user@example.com",
"name": "User Name",
"service_status": "authorized",
"service": "google"
}
The presence of session_token indicates completion — stop polling and continue to Step 4. service_status and service appear only for non-Okta services.
What's happening: you're waiting for the human to finish Step 2. authorization_pending means keep polling at the interval from Step 1. Once session_token appears, the human↔DIS session exists and you hold a handle that identifies the human to DIS — it is not itself a credential for any service.
Step 4. Use the session token to fetch an access token for the service:
curl -s https://agent-auth.dev.tiis.io/services/google/token \
-H "Authorization: Bearer <SESSION_TOKEN>"
Response (HTTP 200):
{
"access_token": "ya29.a0A...",
"service": "google",
"expires_at": "2026-05-26T18:00:00Z"
}
Call this endpoint whenever you need a fresh access token — the service rotates and re-fetches the underlying token automatically. If the session has expired or the service is not yet authorized, the response is HTTP 404 with {"error":"service_not_authorized"} — see the next section for renewal.
What's happening: you exchange the session handle for a short-lived access token that DIS vends for one service. DIS looks up the service token it holds in Vault, refreshes it if needed, and returns a fresh one. Call this each time you need a token rather than caching a long-lived service credential yourself — DIS remains the authorized party and you only ever hold a short-lived, DIS-issued token.
Renewing your session after Okta re-authentication
Sessions expire (idle after 15 minutes, absolute after 24 hours). When a request returns 401, the client directs the user to re-authenticate via the Okta tile, then calls POST /refresh to rotate to a fresh session_token. The old token is invalidated — this bounds the attacker window if a token is ever stolen, since a single re-auth makes it useless.
Step 1. When a call fails with 401, check the X-Session-Action response header:
X-Session-Action: refresh→ the session is flagged for rotation. Skip to Step 3.- No header → the session is expired but not yet flagged. Go through Step 2 first.
Step 2. Direct the user to open this URL in a browser to re-authenticate via Okta:
https://agent-auth.dev.tiis.io/oidc/login
The user signs in via SSO and lands on a "Session refreshed" confirmation page. Server-side, every session indexed under the user is now flagged for rotation. The user can close the tab.
Step 3. Call POST /refresh with the OLD session_token to rotate. The new token comes back in the X-Session-Token response header.
# Poll until rotated=true (the OIDC re-auth may take a few seconds)
curl -is -X POST https://agent-auth.dev.tiis.io/refresh \
-H "Authorization: Bearer <OLD_SESSION_TOKEN>"
Responses:
- 200
{"rotated":true,"expires_at":"…","csrf_token":"…"}with headerX-Session-Token: <NEW_SESSION_TOKEN>— adopt the new token and continue to Step 4. - 400
{"rotated":false,"reason":"no_pending_rotation"}— the OIDC re-auth in Step 2 hasn't completed yet. Wait and retry. - 401
{"error":"unauthorized"}— the session record is gone (over 7 days expired, or revoked). Start over from the device flow at the top of this page.
After a successful rotation the OLD session_token remains valid for a 30-second grace window so any in-flight requests sent with it still succeed. After grace, the old token returns 401.
Step 4. Use the NEW session_token to fetch a service access token:
curl -s https://agent-auth.dev.tiis.io/services/google/token \
-H "Authorization: Bearer <NEW_SESSION_TOKEN>"