Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/changelogs/v1.5.0-prerelease2.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ description: "v1.5.0-prerelease2 changelog - 2026-04-08"
<Update label="Bifrost(HTTP)" description="1.5.0-prerelease2">
## ✨ Features

- **Model Alias** — Map model names to provider-specific identifiers (deployment names, inference profile ARNs, fine-tuned model IDs, custom model names, etc.) via per-key alias config
- **Realtime Support** — Add WebSocket, WebRTC, and client secret handlers with session state management and transport context helpers
- **Fireworks AI Provider** — Add Fireworks AI as a first-class provider with native completions, responses, embeddings, and image generations (thanks [@ivanetchart](https://github.com/ivanetchart)!)
- **Per-User OAuth Consent** — Add per-user OAuth consent flow with identity selection and MCP authentication
Expand Down Expand Up @@ -48,8 +49,24 @@ description: "v1.5.0-prerelease2 changelog - 2026-04-08"
- **Data Race Fix** — Fix race in data reading from fasthttp request for integrations
- **Model Listing** — Unify /api/models and /api/models/details listing behavior

<Warning>
**v1.5.0 contains multiple breaking changes.** See the [v1.5.0 Migration Guide](/migration-guides/v1.5.0) for full before/after examples and a migration checklist.
</Warning>

## Breaking Changes in This Release

This prerelease introduces 3 additional breaking changes on top of those in prerelease1. See the **[v1.5.0 Migration Guide](/migration-guides/v1.5.0)** for full before/after examples, automatic migration details, and a step-by-step checklist.
Comment thread
Pratham-Mishra04 marked this conversation as resolved.

| # | Breaking Change | Affected |
|---|---|---|
| [9](/migration-guides/v1.5.0#breaking-change-9-provider-deployments-removed-migrate-to-aliases) | Provider `deployments` removed — migrate Azure, Bedrock, Vertex, and Replicate deployment maps to the unified top-level `aliases` field | `config.json`, REST API, Go SDK |
| [10](/migration-guides/v1.5.0#breaking-change-10-go-sdk-extrafields-model-fields-renamed) | Go SDK: `ExtraFields.ModelRequested` replaced by `OriginalModelRequested` + `ResolvedModelUsed` | Go SDK |
| [11](/migration-guides/v1.5.0#breaking-change-11-go-sdk-streamaccumulatorresult-field-renamed) | Go SDK: `StreamAccumulatorResult.Model` replaced by `RequestedModel` + `ResolvedModel` | Go SDK |

---
</Update>
<Update label="Core" description="1.5.1">
- feat: add model alias support — map model names to provider-specific identifiers per key
- feat: add Fireworks AI as a first-class provider (thanks [@ivanetchart](https://github.com/ivanetchart)!)
- feat: add realtime provider interfaces, schemas, and engine hooks
- feat: add session log storage and realtime request normalization
Expand All @@ -72,6 +89,7 @@ description: "v1.5.0-prerelease2 changelog - 2026-04-08"

</Update>
<Update label="Framework" description="1.3.1">
- feat: add model alias storage and encryption in key config
- feat: add per-user OAuth consent flow with identity selection and MCP authentication
- feat: add access profiles for fine-grained permission control
- feat: add user level OAuth for MCP gateway
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
"mcp/overview",
"mcp/connecting-to-servers",
"mcp/oauth",
"mcp/per-user-oauth",
"mcp/tool-execution",
"mcp/agent-mode",
"mcp/code-mode",
Expand Down
37 changes: 28 additions & 9 deletions docs/mcp/connecting-to-servers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ Bifrost supports three connection protocols, each with different authentication
| Type | Description | Best For | Auth Support |
|------|-------------|----------|--------------|
| **STDIO** | Spawns a subprocess and communicates via stdin/stdout | Local tools, CLI utilities, scripts | None |
| **HTTP** | Sends requests to an HTTP endpoint | Remote APIs, microservices, cloud functions | Headers, OAuth 2.0 |
| **SSE** | Server-Sent Events for persistent connections | Real-time data, streaming tools | Headers, OAuth 2.0 |
| **HTTP** | Sends requests to an HTTP endpoint | Remote APIs, microservices, cloud functions | Headers, OAuth 2.0, Per-User OAuth |
| **SSE** | Server-Sent Events for persistent connections | Real-time data, streaming tools | Headers, OAuth 2.0, Per-User OAuth |

### STDIO Connections

Expand Down Expand Up @@ -50,9 +50,10 @@ STDIO connections launch external processes and communicate via standard input/o

HTTP connections communicate with MCP servers via HTTP requests. Ideal for remote services and microservices.

HTTP connections support two authentication methods:
HTTP connections support three authentication methods:
- **Header-based authentication**: Static headers (API keys, custom tokens)
- **OAuth 2.0**: Dynamic token-based authentication with automatic token refresh
- **OAuth 2.0**: Shared token managed by an admin, with automatic refresh
- **Per-User OAuth**: Each end-user authenticates with their own credentials

Comment thread
Pratham-Mishra04 marked this conversation as resolved.
#### Header-Based Authentication

Expand Down Expand Up @@ -107,11 +108,23 @@ Use OAuth 2.0 for secure, user-based authentication with automatic token refresh
[→ Learn more about OAuth authentication →](./oauth)

**Use Cases:**
- User-delegated access
- Third-party service integrations
- Secure credential management
- Shared service integrations where all users access the same account
- Admin-managed third-party connections
- Compliance with OAuth 2.0 standards

#### Per-User OAuth

Use per-user OAuth when each end-user should access the upstream service under their own account (e.g., each user's personal Notion workspace or GitHub repos). Bifrost acts as an OAuth 2.1 Authorization Server — users authenticate through a consent flow and their tokens are stored per-identity.

Per-user OAuth is configured through the Web UI only (Bifrost runs a test OAuth flow and pre-fetches tools at setup time).

[→ Learn more about Per-User OAuth →](./per-user-oauth)

**Use Cases:**
- Multi-tenant apps where users access their own data
- Personal integrations (Notion, GitHub, Google Drive)
- Scenarios requiring per-user audit trails and token isolation

**Overall HTTP Use Cases:**
- Remote API integrations
- Cloud-hosted MCP services
Expand All @@ -120,7 +133,7 @@ Use OAuth 2.0 for secure, user-based authentication with automatic token refresh

### SSE Connections

Server-Sent Events (SSE) connections provide real-time, persistent connections to MCP servers. Like HTTP connections, SSE supports both header-based and OAuth authentication.
Server-Sent Events (SSE) connections provide real-time, persistent connections to MCP servers. Like HTTP connections, SSE supports header-based authentication, OAuth 2.0, and per-user OAuth.

#### Header-Based Authentication

Expand Down Expand Up @@ -159,7 +172,13 @@ Server-Sent Events (SSE) connections provide real-time, persistent connections t
- Real-time market data
- Live system monitoring
- Event-driven workflows
- User-authenticated streaming connections
- Shared streaming connections managed by an admin

#### Per-User OAuth

Same as HTTP — each user authenticates under their own account. Configured through the Web UI only.

[→ Learn more about Per-User OAuth →](./per-user-oauth)

[→ Learn more about OAuth authentication →](./oauth)

Expand Down
16 changes: 16 additions & 0 deletions docs/mcp/gateway-url.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,22 @@ The MCP Server dynamically updates its tool registry from the tool manager.

---

## Per-User OAuth for MCP Clients

When at least one MCP server is configured with `per_user_oauth`, the `/mcp` endpoint automatically advertises OAuth support via standard discovery headers. OAuth-capable MCP clients (Claude Code, Cursor, and others) detect this automatically — no manual configuration is needed on the client side.

When an unauthenticated client connects, Bifrost responds with a `401` that points to the discovery endpoints:

```
WWW-Authenticate: Bearer resource_metadata="https://your-bifrost-domain/.well-known/oauth-protected-resource"
```

The client then fetches the OAuth metadata and kicks off a consent flow where the user can attach an identity and connect their upstream services. Subsequent requests use a Bifrost-issued session token as the Bearer credential.

See [Per-User OAuth →](./per-user-oauth) for the full flow and identity options.

---

## Security Considerations

<Warning>
Expand Down
4 changes: 4 additions & 0 deletions docs/mcp/oauth.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ description: "Configure OAuth 2.0 authentication for MCP HTTP and SSE connection
icon: "lock"
---

<Info>
This page covers **server-level OAuth**, where an admin authenticates once and the token is shared across all requests to that MCP server. If you need each end-user to authenticate with their own credentials (e.g., personal Notion or GitHub accounts), see [Per-User OAuth](./per-user-oauth).
</Info>

## Overview

OAuth 2.0 authentication enables secure, user-delegated access to MCP servers. Bifrost handles:
Expand Down
3 changes: 3 additions & 0 deletions docs/mcp/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ By default, Bifrost does NOT automatically execute tool calls. All tool executio
<Card title="OAuth Authentication" icon="lock" href="./oauth">
Secure OAuth 2.0 authentication with automatic token refresh
</Card>
<Card title="Per-User OAuth" icon="users" href="./per-user-oauth">
Let each end-user authenticate with upstream services under their own credentials
</Card>
<Card title="Tool Execution" icon="play" href="./tool-execution">
Execute tools with full control over approval and conversation flow
</Card>
Expand Down
192 changes: 192 additions & 0 deletions docs/mcp/per-user-oauth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
---
title: "Per-User OAuth"
sidebarTitle: "Per-User OAuth"
description: "Let each end-user authenticate with upstream MCP services under their own credentials. Works with both the MCP Gateway and LLM Gateway."
icon: "users"
---

## Overview

**Per-user OAuth** lets each end-user connect to upstream MCP services (Notion, GitHub, etc.) using their own credentials. Instead of a single shared admin token, every user gets their own access — scoped to their account, their data.

This is different from [server-level OAuth](./oauth), where an admin authenticates once and every request uses the same shared token:

| | Server-level OAuth | Per-user OAuth |
|---|---|---|
| Who authenticates | Admin, once | Each end-user individually |
| Token scope | Shared across all requests | Per-user, per-service |
| Identity required | No | Yes (VK, User ID, or session) |
| Persists across sessions | Yes (background refresh) | Yes, when tied to VK or User ID |
| Works with MCP Gateway | Yes | Yes |
| Works with LLM Gateway | Yes | Yes |

---

## Setup

Per-user OAuth is configured through the Web UI only. During setup, Bifrost runs a test OAuth flow and pre-fetches the available tools from the upstream service — this is why file-based config is not supported for this auth type.

<Tabs>
<Tab title="Web UI">

1. Navigate to **MCP Gateway** and click **New MCP Server**
2. Select **HTTP** or **SSE** as the connection type and enter the server URL
3. Set **Auth Type** to **Per-User OAuth**
4. Fill in the OAuth application credentials:
- **Client ID** — your upstream OAuth app's client ID
- **Client Secret** — optional for PKCE flows
- **Authorize URL** — upstream authorization endpoint (or leave blank for auto-discovery)
- **Token URL** — upstream token endpoint (or leave blank for auto-discovery)
- **Scopes** — comma-separated list of requested scopes
5. Click **Create** — Bifrost runs a test OAuth flow to validate the config and pre-fetches the tool list
6. Complete the authorization in your browser
7. Save the MCP client

![Per-User OAuth MCP Server Configuration](../media/ui-mcp-per-user-oauth-setup.png)

</Tab>
</Tabs>

<Info>
If your upstream server supports OAuth Discovery (RFC 8414), you can leave the authorize and token URLs blank and provide only the **Server URL**. Bifrost will discover the endpoints automatically.
</Info>

---

## How it works: MCP Gateway

When you expose Bifrost as an MCP server (via the `/mcp` endpoint) and at least one MCP client is configured with `per_user_oauth`, Bifrost becomes an **OAuth 2.1 Authorization Server**. OAuth-capable MCP clients like Claude Code and Cursor detect this automatically — no manual configuration required on the client side.

The full flow involves three distinct phases: **discovery** (the client finds Bifrost's OAuth endpoints), **consent** (the user attaches an identity and connects upstream services), and **authenticated use** (all subsequent tool calls carry the user's tokens transparently). The diagram below shows all three phases end to end.

![MCP Gateway per-user OAuth flow — discovery, consent, and authenticated tool execution](../media/ui-mcp-per-user-oauth-flow-mcp.svg)

### First connection: the consent flow

The first time a client connects, Bifrost walks the user through a two-step consent screen:

**Step 1 — Identity selection**

The user chooses how to identify themselves for this session:

- **Virtual Key** — ties upstream tokens to the VK permanently; tokens survive session restarts and work across the LLM Gateway too
- **User ID** — a self-declared identifier with the same persistence guarantees as a VK
- **Skip** — no identity attached; tokens are scoped to this session only and won't carry over to other sessions or the LLM Gateway

![Consent identity selection screen](../media/ui-mcp-per-user-oauth-consent-identity.png)

**Step 2 — Connect upstream services**

The user sees all per-user OAuth MCP servers available on their Virtual Key. They can connect all of them at once or just the ones they want right now.

![MCP services connection screen](../media/ui-mcp-per-user-oauth-consent-mcps.png)

For each selected service, the user is redirected to the upstream OAuth provider (Notion, GitHub, etc.) to authorize access. After authorizing, they return to Bifrost and can connect additional services or finish.

**Step 3 — Done**

Bifrost issues a 24-hour session token. The MCP client receives this token and proceeds normally. All subsequent tool calls use the user's upstream tokens transparently.

### Lazy auth for skipped services

If the user skips a service during consent — or a new per-user MCP server is added later — Bifrost handles it lazily. When a tool call hits a service the user hasn't authenticated with yet, Bifrost returns an auth URL in the tool result instead of executing the tool:

```
Authentication required for Notion. Open this URL to connect:
https://your-bifrost-domain.com/api/oauth/per-user/upstream/authorize?...
```

![Auth URL returned inline in a tool result in Claude Code](../media/ui-mcp-per-user-oauth-llm-prompt-mcp.png)

The user opens the URL, completes the upstream OAuth flow, and Bifrost saves the token against their session identity. The next tool call proceeds without any re-auth. This lazy pattern is the same one used by the LLM Gateway — the only difference is the auth URL surfaces as a tool result message rather than an API response field.

---

## How it works: LLM Gateway

When using per-user OAuth through the LLM Gateway (`/v1/chat/completions`), there is no upfront consent screen. Auth is **entirely lazy** — Bifrost waits until a tool actually needs a token before asking for one. This is also the same pattern used when a service is skipped during MCP Gateway consent.

The pattern is simple: every request carries an identity header, and any tool call to an unauthenticated service returns an auth URL instead of a result. The user completes auth once at that URL; all subsequent calls to that service execute normally. The diagram below shows the full cycle.

![LLM Gateway per-user OAuth flow — lazy auth on first tool call, transparent execution on retry](../media/ui-mcp-per-user-oauth-flow-llm.svg)

1. The user makes a request with an identity header attached (required — see below)
2. The LLM suggests a tool call to a per-user OAuth service
3. If no token exists for that user + service, Bifrost returns an `mcp_auth_required` response with an `authorize_url` **instead of executing the tool** — the rest of the LLM response still comes through normally

![API response showing mcp_auth_required with an authorize_url when the user has not yet authenticated](../media/ui-mcp-per-user-oauth-llm-prompt-llm.png)

4. The user opens the URL and completes the upstream OAuth flow
5. Bifrost saves the token against their identity — no action needed on your side
6. On the next request, the tool call executes normally — no re-auth, no special handling required

### Identity is required

The LLM Gateway has no session management, so an identity must be declared on every request. Without one, Bifrost has no stable key to look up or store tokens against.

Pass one of:

```bash
# Virtual Key (recommended — also works with MCP Gateway)
-H "x-bf-virtual-key: vk_your_key"

# Self-declared User ID
-H "X-Bf-User-Id: user_123"
```
Comment thread
Pratham-Mishra04 marked this conversation as resolved.

<Note>
**Enterprise**: When enterprise user identity is configured, the user's identity is automatically attached as the User ID — no manual header required.
</Note>

---

## Cross-gateway token sharing

Tokens are stored against an **identity** (Virtual Key or User ID), not against a gateway. This means:

- Authenticate via the **LLM Gateway** with a VK → that token is immediately usable on the **MCP Gateway** with the same VK
- Authenticate via the **MCP Gateway** consent flow with a VK → that VK works on the **LLM Gateway** with no re-auth needed

The only exception is **Skip** (session-only) auth: those tokens are not associated with any persistent identity and cannot be used from the LLM Gateway.

| Identity mode | Set via | Cross-gateway portable | Persists across sessions |
|---|---|---|---|
| Virtual Key | Consent screen or `x-bf-virtual-key` header | Yes | Yes |
| User ID | Consent screen or `X-Bf-User-Id` header | Yes | Yes |
| Skip (MCP Gateway only) | Consent screen | No | No |

---

## Config reference

Per-user OAuth is configured on the MCP client via `auth_type`. When `auth_type` is `per_user_oauth`, an `oauth_config_id` linking to the OAuth credentials is required (set automatically during UI setup):

```json
{
"mcp": {
"mcp_clients": [
{
"name": "notion",
"connection_type": "http",
"connection_string": "https://mcp.notion.so/sse",
"auth_type": "per_user_oauth",
"oauth_config_id": "oauth_cfg_abc123",
"tools_to_execute": ["*"]
}
]
}
}
```

| Field | Type | Description |
|---|---|---|
| `auth_type` | string | Set to `"per_user_oauth"` |
| `oauth_config_id` | string | ID of the OAuth config created during UI setup |

---

## Next Steps

- [Server-level OAuth →](./oauth) — admin authenticates once, shared token for all requests
- [MCP Gateway URL →](./gateway-url) — expose Bifrost as an MCP server for Claude Code and Cursor
- [Tool Filtering →](./filtering) — control which per-user tools are available per Virtual Key
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media/ui-mcp-per-user-oauth-consent-mcps.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading