Checkpoint Docs
Govern (KYA-OS)

OAuth Integration

Integrate OAuth flows with KYA-OS for AI agent authorization

Overview

Checkpoint exposes two OAuth-shaped surfaces, and they solve different problems:

  • Gateway OAuth 2.1 (/api/v1/gateway/oauth/*) — a standards-compliant OAuth 2.1 authorization server with mandatory PKCE and rotating refresh tokens. MCP clients (Claude Desktop, Cursor, VS Code) use it to connect to gateway-protected MCP servers.
  • Bouncer delegation flow (/api/v1/bouncer/*) — the consent flow that turns an agent's scope request into a delegation. The user authenticates (optionally through an upstream OAuth provider like GitHub or Google) on a consent page and approves; Checkpoint mints a delegation token. These endpoints are delegation-specific — they deliberately do not implement the OAuth 2.1 parameter set.

If you are wiring an MCP client to a protected server, use the gateway endpoints. If you are building an agent that needs user-approved scopes, use the delegation flow.

Prerequisites

  • A Checkpoint project. See Credentials for how to find your Project ID and API key in Installations.
  • For the delegation flow's OAuth mode: an upstream OAuth provider app (GitHub, Google, etc.) registered with its callback URL pointed at /api/v1/bouncer/oauth/callback — see Provider Configuration below.

Gateway OAuth 2.1

Authorization Endpoint

The client starts the flow by sending the user's browser to:

https://kya.vouched.id/api/v1/gateway/oauth/authorize?
  response_type=code
  &client_id={client_id}
  &redirect_uri={callback_url}
  &state={random_state}
  &code_challenge={pkce_challenge}
  &code_challenge_method=S256
  &scope=mcp

Checkpoint signs the user in (redirecting to /signin when there is no session), then shows the consent screen. Approving consent creates the delegation, issues the authorization code, and redirects back to your redirect_uri with code and state.

Parameters

ParameterRequiredDescription
response_typeYesMust be code
client_idYesClient identifier. Accepted without pre-registration; pair with dynamic client registration (below)
redirect_uriYesHTTPS URL, loopback HTTP, or a native-app private-use scheme such as cursor:// or claude:// (RFC 8252 §§7.1–7.3)
stateYesRandom value for CSRF protection — requests without it are rejected
code_challengeYesPKCE challenge (base64url of the SHA-256 of the verifier)
code_challenge_methodYesMust be S256 — plain PKCE is rejected
scopeNoSpace-separated scope identifiers (up to 16). Defaults to mcp

PKCE (Proof Key for Code Exchange) is required for all gateway authorization requests, and only the S256 method is accepted. Errors are reported per RFC 6749 §4.1.2.1: redirected to your redirect_uri with ?error=...&state=... when the redirect target itself is valid, returned as a direct 400 otherwise.

Clients without a fixed client_id can obtain one from POST /api/v1/gateway/oauth/register (RFC 7591 dynamic client registration; issued client IDs are ephemeral).

Token Exchange

Exchange the authorization code for tokens. The body must be application/x-www-form-urlencoded — JSON bodies are rejected:

curl -X POST https://kya.vouched.id/api/v1/gateway/oauth/token \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=auth_code_xyz" \
  --data-urlencode "redirect_uri=https://agent.example.com/callback" \
  --data-urlencode "client_id={client_id}" \
  --data-urlencode "code_verifier={original_pkce_verifier}"

Response:

{
  "access_token": "eyJhbGciOiJFZERTQSIs...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "b64url_random_token",
  "scope": "mcp"
}

The access token is a KYA-OS delegation VC-JWT (a W3C Verifiable Credential in JWT form) minted under the issuing organization's DID, so the gateway can verify it without a database lookup. Authorization codes are single-use — a replayed, expired, or mismatched code fails with invalid_grant, as does a code_verifier that does not hash to the original code_challenge. The verifier must be 43–128 characters (RFC 7636 §4.1).

Refresh Tokens

Access tokens live 15 minutes; use the refresh token to obtain a new one:

curl -X POST https://kya.vouched.id/api/v1/gateway/oauth/token \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token={refresh_token}" \
  --data-urlencode "client_id={client_id}"

Every refresh rotates the token: the response carries a new refresh_token and the one you presented is revoked. Replaying an already-used refresh token is treated as theft — all tokens in that delegation's chain are revoked. Refresh tokens expire 30 days after issue.

Token grants never outlive the delegation. If the underlying delegation has been revoked or is no longer active, both grant types fail with invalid_grant and the client must start a new authorization flow.

Bouncer Delegation Flow

The delegation endpoints look OAuth-like (authorize, token, callback), but they speak delegation vocabulary: the "client" is an agent identified by DID, and the artifact you end up with is a delegation token, not an OAuth access token.

1. Agent → GET/POST /api/v1/bouncer/authorize (agent_did + requested_scopes)
2. Checkpoint resolves the provider and authorization mode → authorization_url or consent_url
3. User signs in with the upstream provider (OAuth mode) and approves on the consent page
4. Provider redirects to Checkpoint's callback; Checkpoint verifies the sign-in and creates the delegation
5. Checkpoint delivers the delegation token to the project's configured worker URL
6. Agent presents the delegation token on subsequent verification calls

Requesting Authorization

https://kya.vouched.id/api/v1/bouncer/authorize?
  agent_did=did:key:z6Mk...
  &requested_scopes=files:read,files:write
  &expires_in_days=7
  &project_id={projectId}

GET is a user-clickable link — no API key required, so project_id must be passed as a query parameter. POST accepts the same fields as a JSON body and authenticates with your API key instead.

ParameterRequiredDescription
agent_didYesDID of the AI agent requesting authorization
requested_scopesYes1–20 scopes. Comma-separated in the GET query, a JSON array in POST
agent_nameNoHuman-readable name shown on the consent page
expires_in_daysNoDelegation lifetime, 1–365 days (default 7)
session_idNoMCP session ID carried through consent
worker_urlNoHTTPS endpoint that receives the delegation token
providerNoExplicit provider ID (e.g. github, credentials)
tool_nameNoTool whose authorization requirement selects the provider
metadataNoArbitrary JSON carried through the flow
project_idGET onlyRequired when no API key is presented

Response (OAuth provider configured):

{
  "success": true,
  "data": {
    "provider_type": "oauth2",
    "authorization_url": "https://github.com/login/oauth/authorize?...",
    "expires_at": "2026-07-27T12:10:00.000Z",
    "project_id": "proj_abc123",
    "agent_did": "did:key:z6Mk...",
    "requested_scopes": ["files:read", "files:write"]
  },
  "metadata": { "requestId": "req_...", "timestamp": "2026-07-27T12:00:00.000Z" }
}

Send the user to authorization_url within 10 minutes (expires_at). Consent-only projects return provider_type: "none" with a consent_url instead of an authorization URL; credential providers return provider_type: "password" plus the login-form configuration.

Provider selection follows this priority: explicit provider parameter → the tool's authorization requirement (from tool_name or the first scope's tool) → the project's default provider → the first configured provider.

Callback Handling

/api/v1/bouncer/oauth/callback is Checkpoint's callback for the upstream provider — not the agent's. When you register an OAuth app with a provider (GitHub, Google, …), point its callback URL at this endpoint. The provider redirects back with code and state; Checkpoint exchanges the code server-side, resolves the signed-in user's identity, and creates the delegation. Agents never implement an OAuth callback handler in this flow.

Delivery to the agent happens out of band: Checkpoint POSTs a delegation.granted payload — including the delegation_token — to the project's configured worker URL. Delivery is HTTPS-only, and loopback or private-network hosts are refused.

Token Exchange

Agents holding an authorization code exchange it for the delegation token:

curl -X POST https://kya.vouched.id/api/v1/bouncer/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "authorization_code",
    "code": "auth_code_xyz",
    "agent_did": "did:key:z6Mk...",
    "project_id": "{projectId}"
  }'

Response:

{
  "delegation_token": "eyJhbGciOiJFZERTQSIs...",
  "token_type": "Bearer",
  "expires_in": 604800,
  "delegation_id": "del_abc123",
  "scopes": ["files:read", "files:write"],
  "session_id": "mcp_session_123",
  "user_did": "did:key:z6Mk..."
}

Authorization codes are single-use, expire after 5 minutes, and are bound to the requesting agent_did and project_id. authorization_code is the only grant type this endpoint supports.

There are no refresh tokens in the delegation flow. The delegation token lives as long as the delegation itself (expires_in_days, default 7 days). When it expires, the agent requests a new delegation.

The delegation_token is a W3C VC-JWT signed with EdDSA (Ed25519) — KYA-OS servers verify it cryptographically without a database lookup. The delegation_id identifies the delegation in the dashboard and appears as the delegationRef field in KYA-OS proof metadata.

Provider Configuration

Configure authentication methods in the Checkpoint dashboard at Policy → Auth (/dashboard/{orgId}/{projectId}/policy/auth):

  1. Add an OAuth method and enter the Client ID, Client secret, Redirect URI, and authorization/token endpoints from your provider's OAuth app
  2. Assign an auth method to each tool — which tools require which method, and with which scopes
  3. Customize the consent screen branding and copy

Per-tool OAuth scope display and scope refresh for already-connected tools still live on the legacy Control Access surface (legacy surface: /dashboard/{orgId}/{projectId}/control-access).

You can also read provider configuration via the API:

curl -X GET https://kya.vouched.id/api/v1/bouncer/projects/{projectId}/providers \
  -H "X-API-Key: $CHECKPOINT_API_KEY"

Supported OAuth Providers

Checkpoint includes pre-configured support for GitHub, Google, Microsoft, Linear, and Auth0 in direct (PKCE) mode, and Apple, Discord, LinkedIn, Slack, Notion, Stripe, and Shopify in proxy (client-secret) mode. See Authentication Methods → Supported Providers for the canonical provider/default-scope table — the dashboard configuration forms and the providers API both advertise those same defaults.

Two caveats specific to this flow:

  • Fallback scope drift. The authorize flow keeps its own fallback scope table that doesn't yet cover every provider (and disagrees with GitHub's default) — scopes saved in your provider configuration always win, so set them explicitly if the default matters to you.
  • Scope derivation for proxy-mode providers. Default scopes apply only when nothing more specific is available: in practice Checkpoint derives provider scopes from the delegation's requested tool scopes (for example, user:read maps to GitHub's read:user), and scopes saved in the provider configuration take precedence over both.

You can also configure custom OAuth providers. See Authentication Methods for details on all supported authentication modes.

Security Considerations

PKCE Requirement

All gateway authorization requests must use PKCE with the S256 method — this is mandatory, not optional, and requests without a valid code_challenge are rejected. The bouncer delegation flow does not use PKCE; its authorization codes are instead single-use, short-lived, and bound to the requesting agent's DID and project.

State Parameter

The gateway requires a cryptographically random state parameter and rejects requests without one; verify it in your callback to prevent CSRF. In the delegation flow, Checkpoint generates and verifies the state itself across the upstream-provider round trip — agents don't manage it.

Redirect URI Validation

The gateway only accepts redirect URIs in the RFC 8252 shapes listed above (HTTPS, loopback HTTP, or a native app's private-use scheme). For upstream providers in the delegation flow, the redirect URI comes from your provider configuration at Policy → Auth and should point at Checkpoint's callback endpoint.

Token Storage

Agents should store access tokens, refresh tokens, and delegation tokens securely. Never expose tokens in client-side code or logs.

Next Steps