Files
Beyhan Oğur 880f412e2c first commit
2026-04-26 21:52:23 +03:00

770 lines
26 KiB
YAML

oauth-callback:
get:
operationId: handleOAuthCallback
summary: OAuth callback endpoint
description: |
Handles the OAuth provider callback after user authorization.
This endpoint processes the authorization code and exchanges it for an access token.
On success, displays an HTML page that closes the authorization window.
tags:
- OAuth
parameters:
- name: state
in: query
required: true
description: State parameter for OAuth security (CSRF protection)
schema:
type: string
- name: code
in: query
required: true
description: Authorization code from the OAuth provider
schema:
type: string
- name: error
in: query
required: false
description: Error code if authorization failed
schema:
type: string
- name: error_description
in: query
required: false
description: Error description if authorization failed
schema:
type: string
responses:
'200':
description: OAuth authorization successful. Returns HTML page that closes the authorization window.
content:
text/html:
schema:
type: string
'400':
description: OAuth authorization failed or missing required parameters
content:
text/html:
schema:
type: string
oauth-config-status:
get:
operationId: getOAuthConfigStatus
summary: Get OAuth config status
description: |
Retrieves the current status of an OAuth configuration.
Shows whether the OAuth flow is pending, authorized, or failed,
and includes token expiration and scopes if authorized.
tags:
- OAuth
parameters:
- name: id
in: path
required: true
description: OAuth config ID
schema:
type: string
responses:
'200':
description: OAuth config status retrieved successfully
content:
application/json:
schema:
$ref: '../../schemas/management/oauth.yaml#/OAuthConfigStatus'
'404':
description: OAuth config not found
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'500':
$ref: '../../openapi.yaml#/components/responses/InternalError'
delete:
operationId: revokeOAuthConfig
summary: Revoke OAuth config
description: |
Revokes an OAuth configuration and its associated access token.
After revocation, the MCP client will no longer be able to use this OAuth token.
tags:
- OAuth
parameters:
- name: id
in: path
required: true
description: OAuth config ID
schema:
type: string
responses:
'200':
description: OAuth token revoked successfully
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/SuccessResponse'
'500':
$ref: '../../openapi.yaml#/components/responses/InternalError'
# ─── Per-User OAuth 2.1 Authorization Server ───────────────────────────────
# These endpoints implement RFC 7591 (dynamic registration), RFC 7636 (PKCE),
# and the OAuth 2.1 authorization code flow. MCP clients use them automatically
# when connecting to Bifrost's /mcp endpoint. Only active when at least one MCP
# client is configured with auth_type: per_user_oauth.
per-user-oauth-register:
post:
operationId: registerPerUserOAuthClient
summary: Register OAuth client (RFC 7591)
description: |
Dynamic Client Registration per RFC 7591. MCP clients (Claude Code, Cursor, etc.)
call this endpoint to obtain a `client_id` before initiating the authorization flow.
This endpoint is only available when at least one MCP client is configured with
`auth_type: per_user_oauth`. Returns `404` otherwise.
Authentication is not required — this is part of the unauthenticated OAuth bootstrap flow.
tags:
- OAuth
- Per-User OAuth
requestBody:
required: true
content:
application/json:
schema:
$ref: '../../schemas/management/oauth.yaml#/PerUserOAuthClientRegistrationRequest'
example:
client_name: "Claude Code"
redirect_uris: ["http://localhost:54321/callback"]
grant_types: ["authorization_code"]
response_types: ["code"]
token_endpoint_auth_method: "none"
responses:
'201':
description: Client registered successfully
content:
application/json:
schema:
$ref: '../../schemas/management/oauth.yaml#/PerUserOAuthClientRegistrationResponse'
example:
client_id: "550e8400-e29b-41d4-a716-446655440000"
client_name: "Claude Code"
redirect_uris: ["http://localhost:54321/callback"]
grant_types: ["authorization_code"]
token_endpoint_auth_method: "none"
'400':
$ref: '../../openapi.yaml#/components/responses/BadRequest'
'404':
description: No per-user OAuth MCP clients configured
content:
text/plain:
schema:
type: string
'503':
description: Config store is disabled
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
per-user-oauth-authorize:
get:
operationId: authorizePerUserOAuth
summary: Authorization endpoint (OAuth 2.1)
description: |
OAuth 2.1 authorization endpoint. Validates the request parameters, creates a
browser-bound `PendingFlow` record (15-minute TTL), and redirects the user to
the Bifrost consent screen at `/oauth/consent?flow_id=xxx`.
**PKCE is required** — `code_challenge` and `code_challenge_method=S256` must
be provided. Plain code challenges are not supported.
A `__bifrost_flow_secret` HttpOnly SameSite=Lax cookie is set on redirect to
bind the consent flow to the initiating browser session (CSRF protection).
Authentication is not required — this is part of the unauthenticated OAuth bootstrap flow.
tags:
- OAuth
- Per-User OAuth
parameters:
- name: response_type
in: query
required: true
description: Must be `code`
schema:
type: string
enum: [code]
- name: client_id
in: query
required: true
description: Client ID obtained from the registration endpoint
schema:
type: string
- name: redirect_uri
in: query
required: true
description: Must match a URI registered for this client
schema:
type: string
- name: code_challenge
in: query
required: true
description: PKCE code challenge (Base64URL-encoded SHA-256 of the code verifier)
schema:
type: string
- name: code_challenge_method
in: query
required: true
description: Must be `S256`
schema:
type: string
enum: [S256]
- name: state
in: query
required: false
description: Opaque value to maintain state between request and callback (CSRF protection)
schema:
type: string
responses:
'302':
description: Redirect to consent screen at `/oauth/consent?flow_id=xxx`
headers:
Location:
schema:
type: string
description: URL of the consent screen
Set-Cookie:
schema:
type: string
description: "`__bifrost_flow_secret` HttpOnly SameSite=Lax cookie for browser binding"
'400':
$ref: '../../openapi.yaml#/components/responses/BadRequest'
'404':
description: No per-user OAuth MCP clients configured, or unknown client_id
content:
text/plain:
schema:
type: string
'503':
description: Config store is disabled
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
per-user-oauth-token:
post:
operationId: exchangePerUserOAuthToken
summary: Token endpoint (OAuth 2.1)
description: |
OAuth 2.1 token endpoint. Exchanges a single-use authorization code (5-minute TTL)
for a Bifrost-issued access token (24-hour TTL) using PKCE verification.
The request body must be `application/x-www-form-urlencoded`.
The returned `access_token` is the Bearer token to use on subsequent `/mcp` requests.
It carries the user's upstream service tokens (Notion, GitHub, etc.) linked to their
identity (Virtual Key or User ID) from the consent flow.
Authentication is not required — this is part of the unauthenticated OAuth bootstrap flow.
tags:
- OAuth
- Per-User OAuth
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
required:
- grant_type
- code
- code_verifier
properties:
grant_type:
type: string
description: Must be `authorization_code`
enum: [authorization_code]
code:
type: string
description: Authorization code received in the redirect callback
redirect_uri:
type: string
description: Must match the redirect_uri used in the authorize request (if provided)
client_id:
type: string
description: Client ID (optional — code is already bound to the client)
code_verifier:
type: string
description: PKCE code verifier — the raw secret whose SHA-256 matches the code_challenge
responses:
'200':
description: Token issued successfully
content:
application/json:
schema:
$ref: '../../schemas/management/oauth.yaml#/PerUserOAuthTokenResponse'
example:
access_token: "abc123xyz..."
token_type: "Bearer"
expires_in: 86400
scope: "mcp:read mcp:write"
'400':
description: Invalid grant, expired code, PKCE failure, or unsupported grant type
content:
application/json:
schema:
type: object
properties:
error:
type: string
enum: [invalid_grant, invalid_request, unsupported_grant_type]
error_description:
type: string
'404':
description: No per-user OAuth MCP clients configured
content:
text/plain:
schema:
type: string
'500':
description: Server error or session creation failed
content:
application/json:
schema:
type: object
properties:
error:
type: string
enum: [server_error]
error_description:
type: string
per-user-oauth-upstream-authorize:
get:
operationId: authorizeUpstreamPerUserOAuth
summary: Upstream OAuth proxy — authorize with upstream service
description: |
Initiates an OAuth flow with an upstream MCP service (Notion, GitHub, etc.)
on behalf of the current user. Used during the consent flow (via "Connect" buttons
on the MCPs page) and at runtime when a tool call is made to an unauthenticated service.
**Consent flow** — provide `flow_id` (from the pending consent flow). The browser-binding
cookie (`__bifrost_flow_secret`) is validated.
**Runtime flow** — provide `session` (the Bifrost session ID from the token endpoint).
Used when a service was skipped during consent and needs to be connected later.
On success, redirects the user to the upstream provider's authorize URL. After the user
grants access, the upstream callback lands at `/api/oauth/callback`, stores the upstream
token against the user's identity, and redirects back to the consent screen (consent flow)
or returns an authorization success page (runtime flow).
Authentication is not required — cookie/session validation is performed instead.
tags:
- OAuth
- Per-User OAuth
parameters:
- name: mcp_client_id
in: query
required: true
description: ID of the per-user OAuth MCP client to authenticate with
schema:
type: string
- name: flow_id
in: query
required: false
description: |
Pending consent flow ID. Required if `session` is not provided.
The `__bifrost_flow_secret` cookie must be present and match the flow.
schema:
type: string
- name: session
in: query
required: false
description: |
Bifrost session ID (from the token endpoint). Required if `flow_id` is not provided.
Used for runtime (post-consent) upstream authorization.
schema:
type: string
responses:
'302':
description: Redirect to upstream OAuth provider's authorize URL
headers:
Location:
schema:
type: string
description: Upstream provider authorization URL with PKCE parameters
'400':
$ref: '../../openapi.yaml#/components/responses/BadRequest'
'401':
description: Invalid or expired flow/session
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'403':
description: Browser-binding cookie mismatch (CSRF protection)
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'404':
description: MCP client not found or not configured for per-user OAuth
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'503':
description: Config store is disabled
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
# ─── Per-User OAuth Consent Flow (browser UI) ──────────────────────────────
# These endpoints serve HTML pages and handle form submissions for the
# multi-step consent flow. They are browser-facing, not JSON API endpoints.
# All endpoints validate the __bifrost_flow_secret browser-binding cookie.
consent-identity-page:
get:
operationId: getConsentIdentityPage
summary: Consent identity selection page
description: |
Renders the identity selection screen where the user chooses how to identify
themselves for the session: Virtual Key, User ID, or Skip (session-only auth).
The `__bifrost_flow_secret` HttpOnly cookie set during `/api/oauth/per-user/authorize`
must be present — it binds the consent flow to the initiating browser.
The Skip option is only shown when `enforce_auth_on_inference` is `false` in config.
tags:
- Per-User OAuth
- Consent Flow
parameters:
- name: flow_id
in: query
required: true
description: Pending flow ID from the authorize redirect
schema:
type: string
- name: error
in: query
required: false
description: Error message to display (used on redirect-back from failed form submissions)
schema:
type: string
responses:
'200':
description: Identity selection HTML page
content:
text/html:
schema:
type: string
'400':
description: Missing or expired flow_id
content:
text/plain:
schema:
type: string
'403':
description: Browser-binding cookie mismatch
content:
text/plain:
schema:
type: string
consent-mcps-page:
get:
operationId: getConsentMCPsPage
summary: Consent MCP services page
description: |
Renders the MCP services connection screen. Shows all per-user OAuth MCP servers
available on the user's Virtual Key (or all servers if no VK was selected).
Each service shows a "Connect" link or a "Connected ✓" badge.
Requires the `__bifrost_flow_secret` browser-binding cookie.
tags:
- Per-User OAuth
- Consent Flow
parameters:
- name: flow_id
in: query
required: true
description: Pending flow ID
schema:
type: string
responses:
'200':
description: MCP services connection HTML page
content:
text/html:
schema:
type: string
'400':
description: Missing or expired flow_id
content:
text/plain:
schema:
type: string
'403':
description: Browser-binding cookie mismatch
content:
text/plain:
schema:
type: string
consent-submit-vk:
post:
operationId: submitConsentVirtualKey
summary: Submit Virtual Key identity
description: |
Validates the submitted Virtual Key and links it to the pending flow as the user's
identity. On success, redirects to the MCPs page. On failure, redirects back to the
identity page with an error message.
Request body is `application/x-www-form-urlencoded` (browser form submission).
tags:
- Per-User OAuth
- Consent Flow
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
required: [flow_id, vk]
properties:
flow_id:
type: string
description: Pending flow ID
vk:
type: string
description: Virtual Key value (validated against the database)
responses:
'302':
description: |
Redirect to `/oauth/consent/mcps?flow_id=xxx` on success, or back to
`/oauth/consent?flow_id=xxx&error=...` on failure.
headers:
Location:
schema:
type: string
consent-submit-user-id:
post:
operationId: submitConsentUserID
summary: Submit User ID identity
description: |
Links a self-declared User ID to the pending flow as the user's identity.
On success, redirects to the MCPs page.
The User ID is self-declared with no server-side verification — it matches
the trust model of the `X-Bf-User-Id` header in the LLM Gateway path.
Request body is `application/x-www-form-urlencoded` (browser form submission).
tags:
- Per-User OAuth
- Consent Flow
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
required: [flow_id, user_id]
properties:
flow_id:
type: string
description: Pending flow ID
user_id:
type: string
description: Self-declared user identifier (max 255 characters)
responses:
'302':
description: |
Redirect to `/oauth/consent/mcps?flow_id=xxx` on success, or back to
`/oauth/consent?flow_id=xxx&error=...` on failure.
headers:
Location:
schema:
type: string
consent-skip:
post:
operationId: skipConsentIdentity
summary: Skip identity selection
description: |
Skips identity selection and proceeds directly to the MCPs page. Upstream service
tokens will be stored against the session token only (not a persistent identity),
so they will not carry over to other sessions or the LLM Gateway.
Only available when `enforce_auth_on_inference` is `false` in config. Returns a
redirect back to the identity page with an error if auth enforcement is enabled.
Request body is `application/x-www-form-urlencoded` (browser form submission).
tags:
- Per-User OAuth
- Consent Flow
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
required: [flow_id]
properties:
flow_id:
type: string
description: Pending flow ID
responses:
'302':
description: |
Redirect to `/oauth/consent/mcps?flow_id=xxx` on success, or back to
`/oauth/consent?flow_id=xxx&error=...` if identity enforcement is required.
headers:
Location:
schema:
type: string
consent-submit:
post:
operationId: submitConsent
summary: Finalize consent flow
description: |
Finalizes the consent flow atomically:
1. Creates a `TablePerUserOAuthSession` (24h Bifrost session token)
2. Transfers upstream tokens from the flow proxy to the session
3. Issues a single-use `TablePerUserOAuthCode` (5-minute TTL, PKCE-bound)
4. Deletes the `PendingFlow`
5. Redirects to the MCP client's `redirect_uri` with `code` and `state`
The MCP client then exchanges the code at `/api/oauth/per-user/token`.
Request body is `application/x-www-form-urlencoded` (browser form submission).
tags:
- Per-User OAuth
- Consent Flow
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
type: object
required: [flow_id]
properties:
flow_id:
type: string
description: Pending flow ID
responses:
'302':
description: |
Redirect to the MCP client's registered `redirect_uri` with
`?code=xxx&state=yyy` query parameters.
headers:
Location:
schema:
type: string
description: MCP client callback URL with code and state
'400':
$ref: '../../openapi.yaml#/components/responses/BadRequest'
'403':
description: Browser-binding cookie mismatch
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'409':
description: Consent flow already submitted (duplicate submission prevention)
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'410':
description: Consent flow expired
content:
application/json:
schema:
$ref: '../../schemas/management/common.yaml#/ErrorResponse'
'500':
$ref: '../../openapi.yaml#/components/responses/InternalError'
# ─── OAuth Discovery (RFC 9728 + RFC 8414) ─────────────────────────────────
# These well-known endpoints enable MCP clients to auto-discover Bifrost's
# OAuth configuration. Only active when at least one MCP client is configured
# with auth_type: per_user_oauth.
oauth-protected-resource-metadata:
get:
operationId: getOAuthProtectedResourceMetadata
summary: Protected Resource Metadata (RFC 9728)
description: |
Returns the OAuth 2.0 Protected Resource Metadata document per RFC 9728.
MCP clients fetch this after receiving a `401` response from `/mcp` (with a
`WWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource"`
header). The response tells the client which authorization server(s) protect the
`/mcp` resource so it can proceed with discovery.
Returns `404` when no MCP clients are configured with `auth_type: per_user_oauth`.
tags:
- OAuth
- Per-User OAuth
responses:
'200':
description: Protected resource metadata
content:
application/json:
schema:
$ref: '../../schemas/management/oauth.yaml#/ProtectedResourceMetadata'
example:
resource: "https://your-bifrost-domain.com/mcp"
authorization_servers: ["https://your-bifrost-domain.com"]
scopes_supported: ["mcp:read", "mcp:write"]
bearer_methods_supported: ["header"]
'404':
description: No per-user OAuth MCP clients configured
content:
text/plain:
schema:
type: string
oauth-authorization-server-metadata:
get:
operationId: getOAuthAuthorizationServerMetadata
summary: Authorization Server Metadata (RFC 8414)
description: |
Returns the OAuth 2.0 Authorization Server Metadata document per RFC 8414.
After fetching the Protected Resource Metadata, MCP clients fetch this endpoint
to discover Bifrost's OAuth endpoints (register, authorize, token) and capabilities
(PKCE methods, grant types, etc.).
Returns `404` when no MCP clients are configured with `auth_type: per_user_oauth`.
tags:
- OAuth
- Per-User OAuth
responses:
'200':
description: Authorization server metadata
content:
application/json:
schema:
$ref: '../../schemas/management/oauth.yaml#/AuthorizationServerMetadata'
example:
issuer: "https://your-bifrost-domain.com"
authorization_endpoint: "https://your-bifrost-domain.com/api/oauth/per-user/authorize"
token_endpoint: "https://your-bifrost-domain.com/api/oauth/per-user/token"
registration_endpoint: "https://your-bifrost-domain.com/api/oauth/per-user/register"
response_types_supported: ["code"]
grant_types_supported: ["authorization_code"]
code_challenge_methods_supported: ["S256"]
token_endpoint_auth_methods_supported: ["none"]
scopes_supported: ["mcp:read", "mcp:write"]
'404':
description: No per-user OAuth MCP clients configured
content:
text/plain:
schema:
type: string