Skip to content

Comments

Add OAuth 2.1 Bearer token support for HTTP transport#32

Open
pushpak1300 wants to merge 1 commit intoprism-php:mainfrom
pushpak1300:oauth_support
Open

Add OAuth 2.1 Bearer token support for HTTP transport#32
pushpak1300 wants to merge 1 commit intoprism-php:mainfrom
pushpak1300:oauth_support

Conversation

@pushpak1300
Copy link
Collaborator

@pushpak1300 pushpak1300 commented Feb 21, 2026

MCP HTTP servers protected by OAuth 2.1 (per the 2025-11-25 spec) require a Bearer token on every request. Relay had no way to supply a per-request runtime token — only a static api_key in config — making per-user OAuth flows impossible.

Usage

// Caller handles the OAuth flow and obtains a token, then passes it to Relay:
$tools = Relay::withToken($user->mcp_token)->tools('github');

// Catch 401s to trigger re-authorization:
try {
    $tools = Relay::withToken($token)->tools('github');
} catch (AuthorizationException $e) {
    return redirect('/oauth/reconnect'); // token expired or rejected
}

How the flow works

sequenceDiagram
    actor User
    participant App as Your Laravel App
    participant Relay
    participant MCP as MCP Server
    participant AS as Authorization Server

    Note over User,AS: Phase 1 — One-time OAuth setup (your app's responsibility)

    User->>App: Connect MCP account
    App->>MCP: GET /mcp (no token)
    MCP-->>App: 401 + WWW-Authenticate: Bearer resource_metadata="..."

    App->>MCP: GET /.well-known/oauth-protected-resource
    MCP-->>App: { authorization_servers: ["https://auth.example.com"] }

    App->>AS: GET /.well-known/oauth-authorization-server
    AS-->>App: { authorization_endpoint, token_endpoint, ... }

    App->>User: Redirect to authorization URL (+ PKCE challenge + resource param)
    User->>AS: Log in & approve
    AS-->>App: Authorization code via redirect

    App->>AS: POST /token (code + PKCE verifier + resource)
    AS-->>App: { access_token, refresh_token }

    App->>App: Store tokens in DB (per user, per server)

    Note over User,AS: Phase 2 — Runtime tool use (Relay's responsibility)

    User->>App: Make a request that needs MCP tools
    App->>Relay: Relay::withToken($user->mcp_token)->tools('github')
    Relay->>MCP: POST /mcp tools/list\nAuthorization: Bearer <token>
    MCP-->>Relay: Tool definitions
    Relay-->>App: [Tool, Tool, Tool]

    App->>Relay: Prism calls a tool via handler
    Relay->>MCP: POST /mcp tools/call\nAuthorization: Bearer <token>
    MCP-->>Relay: Tool result
    Relay-->>App: "result string"

    Note over User,AS: Phase 3 — Token expiry (your app's responsibility)

    App->>Relay: Relay::withToken($expiredToken)->tools('github')
    Relay->>MCP: POST /mcp\nAuthorization: Bearer <expired>
    MCP-->>Relay: 401 Unauthorized
    Relay-->>App: throws AuthorizationException

    alt Has valid refresh token
        App->>AS: POST /token (refresh_token)
        AS-->>App: { new access_token, new refresh_token }
        App->>App: Save new tokens to DB
        App->>Relay: Relay::withToken($newToken)->tools('github')
        Relay->>MCP: POST /mcp (retried with new token)
        MCP-->>Relay: Tool definitions
        Relay-->>App: [Tool, Tool, Tool]
    else Refresh expired or revoked
        App->>User: Redirect to /oauth/reconnect (Phase 1 again)
    end
Loading

Your application is responsible for Phase 1 (discovery, PKCE authorization code flow, token storage/refresh). Relay handles Phase 2 — attaching Authorization: Bearer <token> to every MCP request and surfacing AuthorizationException on 401 so your app knows when to re-authorize.

@pushpak1300 pushpak1300 marked this pull request as ready for review February 21, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant