- User → DeployStack OAuth (Social Login) - See OAuth Providers
- MCP Client → DeployStack OAuth (API Access) - See OAuth2 Server - How VS Code, Cursor, Claude.ai authenticate to satellite APIs
- User → MCP Server OAuth (External Service Access) - This document - How users authorize external services like Notion, Box, Linear
Overview
This document covers the backend implementation for OAuth 2.1 authentication with external MCP servers that require user authorization, such as Notion, Box, Linear, and GitHub Copilot.When MCP Servers Require OAuth
MCP servers that access user-specific resources (files, issues, repositories) require OAuth authorization. Examples:- Notion MCP Server (
https://mcp.notion.com/) - Access user’s Notion pages - Box MCP Server (
https://mcp.box.com/) - Access user’s Box files - Linear MCP Server (
https://mcp.linear.app/sse) - Access user’s Linear issues - GitHub Copilot MCP - Access GitHub repositories
User Flow
- Install - User initiates MCP server installation in frontend
- Authorize - Backend redirects to OAuth provider’s authorization page
- Callback - OAuth provider redirects back with authorization code
- Token Storage - Backend exchanges code for tokens, encrypts and stores them
- Use - Satellite injects tokens when connecting to MCP server
Architecture Overview
The OAuth implementation includes:- OAuth Discovery Service - Detects OAuth requirement and discovers endpoints using RFC 8414/9728
- Authorization Endpoint - Initiates OAuth flow with PKCE, state parameter, and resource parameter
- Callback Endpoint - Exchanges authorization code for tokens
- Token Service - Handles token exchange and refresh operations
- Client Registration Service - Implements RFC 7591 Dynamic Client Registration (DCR)
- Encryption Service - AES-256-GCM encryption for tokens at rest
- Token Refresh Job - Background cron job refreshing expiring tokens
Database Tables
mcpOauthProviders- Pre-registered OAuth providers (for non-DCR auth servers)oauthPendingFlows- Temporary storage during OAuth flow (10-minute expiry)mcpServerInstallations- MCP server installationsmcpOauthTokens- Encrypted access and refresh tokens
Implementation Components
OAuthDiscoveryService
File: services/backend/src/services/OAuthDiscoveryService.ts Purpose: Detects if an MCP server requires OAuth and discovers OAuth endpoints using RFC 8414 and RFC 9728.OAuth Detection
The service makes a test request to the MCP server and checks for OAuth requirement:- HTTP 401 Unauthorized response
WWW-Authenticate: Bearerheader present
OAuth Metadata Discovery
Once OAuth is detected, the service discovers endpoints using two RFCs: RFC 9728 - Protected Resource Metadata (Primary method):.well-known/openid-configuration)
Metadata Structure
Pre-registered Provider Matching
If the discovered authorization server matches a pre-registered provider pattern, the service returns the provider configuration:Authorization Endpoint
File: services/backend/src/routes/mcp/installations/authorize.ts Endpoint:POST /api/teams/:teamId/mcp/installations/authorize
Purpose: Initiates the OAuth 2.1 authorization flow with PKCE for MCP server installation.
Request Body
Authorization Flow Steps
Verify OAuth Requirement
requires_oauth: true in the catalog.Extract Server URL
remotes (HTTP/SSE) or packages (stdio) configuration.Discover OAuth Endpoints
OAuthDiscoveryService.detectAndDiscoverOAuth() to get authorization endpoints.Dynamic Client Registration or Provider Match
- If
registration_endpointexists: Register new client via RFC 7591 - Else if pre-registered provider matches: Use provider credentials
- Else: Return error (cannot proceed)
Generate PKCE Pair
Generate State Parameter
Generate Resource Parameter
Create Pending Flow Record
oauthPendingFlows table (expires in 10 minutes).Build Authorization URL
Return Authorization URL
Callback Endpoint
File: services/backend/src/routes/mcp/installations/callback.ts Endpoint:GET /api/teams/:teamId/mcp/oauth/callback/:flowId
Purpose: Receives authorization code from OAuth provider, exchanges it for tokens, and completes installation.
Callback Flow Steps
Validate OAuth Callback
Find Pending Flow
Check Flow Expiration
Exchange Code for Tokens
Create Installation
Encrypt and Store Tokens
Delete Pending Flow
Notify Satellites
Return Success Page
OAuthTokenService
File: services/backend/src/services/OAuthTokenService.ts Purpose: Handles token exchange and refresh operations with OAuth servers.Token Exchange with PKCE
Exchanges authorization code for access/refresh tokens using PKCE verification:none- Public client (PKCE only, no client secret)client_secret_post- Client secret in request body (GitHub, most OAuth providers)client_secret_basic- HTTP Basic Auth header (enterprise providers)
Token Refresh
Refreshes expired access tokens using refresh token:Update Refreshed Tokens
Updates database with newly refreshed encrypted tokens:OAuthClientRegistrationService
File: services/backend/src/services/OAuthClientRegistrationService.ts Purpose: Implements RFC 7591 (OAuth 2.0 Dynamic Client Registration Protocol).Dynamic Client Registration
Registers a new OAuth client with MCP server’s registration endpoint:- MCP server supports
registration_endpointin OAuth metadata - Client ID is generated dynamically per installation
- No pre-registration required with OAuth provider
- MCP server does NOT support
registration_endpoint - Pre-registered provider configured in
mcpOauthProviderstable - Uses fixed client ID and client secret
- Example: GitHub OAuth Apps for GitHub MCP server
Database Schema
mcpOauthProviders Table
Pre-registered OAuth providers for MCP servers that don’t support Dynamic Client Registration.oauthPendingFlows Table
Temporary storage for OAuth flows during authorization (expires in 10 minutes).mcpOauthTokens Table
Encrypted OAuth tokens for MCP server installations.iv:authTag:encryptedData (all hex-encoded)
- IV: 16 bytes (128 bits)
- Auth Tag: 16 bytes (128 bits)
- Encrypted Data: Variable length
(installation_id, user_id, team_id) for fast token lookups by satellite.
Token Lifecycle
Token Issuance
- User authorizes application at OAuth provider
- OAuth provider redirects to callback with authorization code
- Backend exchanges code for access/refresh tokens using PKCE
- Tokens encrypted using AES-256-GCM
- Encrypted tokens stored in
mcpOauthTokenstable - Installation status set to
connecting
Automatic Token Refresh
File: services/backend/src/jobs/refresh-oauth-tokens.ts Cron Schedule: Every 5 minutes Refresh Criteria:- Token has
refresh_token(NOT NULL) - Token has
expires_attimestamp (NOT NULL) - Token expires within next 10 minutes
- Token not already expired
Find Expiring Tokens
Discover OAuth Endpoints
Decrypt Refresh Token
Call Token Endpoint
Encrypt and Update
Handle Failures
requires_reauth.Token Expiration Handling
During token refresh cron job:- Installation status →
requires_reauth - User sees “Reconnect” button in frontend
- User must re-authorize to get new tokens
- Satellite checks
expires_attimestamp - If expired and no refresh possible → Return error to MCP client
- MCP client receives authentication error
- User must re-authenticate
Token Revocation
When user deletes an MCP server installation:- Installation deleted from
mcpServerInstallations(CASCADE) - Tokens automatically deleted from
mcpOauthTokens(CASCADE foreign key) - Future enhancement: Call OAuth provider’s revocation endpoint
- Satellite receives configuration update removing the installation
Security Implementation
PKCE (Proof Key for Code Exchange)
Required for all OAuth flows to prevent authorization code interception attacks. PKCE Generation:SHA256(code_verifier) == code_challenge before issuing tokens.
State Parameter
Purpose: CSRF protection during OAuth flow. Generation:- Backend generates random state before redirecting to OAuth provider
- State stored in
oauthPendingFlowstable - OAuth provider includes state in callback URL
- Backend verifies state matches stored value
- If mismatch → Reject callback (potential CSRF attack)
Resource Parameter
Purpose: Token audience binding (RFC 8707) to prevent token misuse. Generation:- Tokens bound to specific MCP server and team
- Prevents token reuse across different installations
- OAuth provider includes resource in issued token
Token Encryption
Algorithm: AES-256-GCM (Authenticated Encryption with Associated Data) Encryption:- AES-256: Industry-standard symmetric encryption
- GCM mode: Authenticated encryption prevents tampering
- Random IV: Each encryption uses unique initialization vector
- AAD: Additional authenticated data binds encryption context
- Scrypt: Key derivation function resistant to brute-force attacks
HTTPS Requirements
All OAuth endpoints require HTTPS:- Authorization endpoint
- Token endpoint
- Callback endpoint (redirect URI)
http://localhost allowed for testing.
OAuth Discovery Process
Step-by-Step Discovery Flow
Make Test Request
Check for OAuth Requirement
Try Protected Resource Metadata (RFC 9728)
Fetch Authorization Server Metadata (RFC 8414)
Fallback to OpenID Connect Discovery
Validate Metadata
Check PKCE Support
Match Pre-registered Provider (Optional)
Return Discovery Result
Error Handling
Discovery failures:- Protected resource metadata not found: Try authorization server metadata directly (if MCP server provides hint)
- Authorization server metadata not found: Try OpenID Connect discovery
- All discovery methods fail: Return error to user - OAuth configuration cannot be determined
- Network timeout: Retry with exponential backoff (3 attempts)
- Invalid JSON: Log error and return OAuth not supported
- RFC 9728 Protected Resource Metadata
- RFC 8414 Authorization Server Metadata
- OpenID Connect Discovery
- Give up and return error
Integration Points
Frontend → Backend
Installation initiation:Backend → Satellite
Satellite retrieves OAuth tokens during configuration fetch: The satellite calls/api/satellites/config which includes OAuth tokens for installations:
Satellite → MCP Server
Token injection in HTTP/SSE requests: Satellite addsAuthorization header when connecting to OAuth-enabled MCP servers:
Testing and Debugging
Manual Testing with Real MCP Servers
Notion MCP Server:- Add Notion to catalog:
https://mcp.notion.com/ - Backend detects OAuth requirement automatically
- Install as user → Opens Notion OAuth page
- Authorize → Callback completes installation
- Check
mcpOauthTokenstable for encrypted tokens - Verify satellite receives decrypted token in config
- Add Box to catalog:
https://mcp.box.com/ - Follow same flow as Notion
- Verify PKCE S256 is used (check logs)
- Test token refresh by manually updating
expires_atto past
Testing Dynamic Client Registration
MCP servers with DCR support:- Notion: ✅ Supports RFC 7591 registration endpoint
- Box: ✅ Supports RFC 7591 registration endpoint
- Linear: ✅ Supports RFC 7591 registration endpoint
- Ensure no pre-registered provider matches
- Check logs for “Registering dynamic OAuth client”
- Verify
oauth_client_idis dynamically generated - Check that client can refresh tokens using generated client ID
Testing Pre-registered Providers
Setup test provider:- Add GitHub MCP server requiring OAuth
- Install → Should match provider by auth server pattern
- Check logs for “Using pre-registered OAuth provider: GitHub (Test)”
- Verify client_id from provider is used instead of DCR
Common Issues
Issue: “OAuth provider not configured” error- Cause: MCP server doesn’t support DCR and no pre-registered provider matches
- Fix: Add provider to
mcpOauthProviderstable with matching auth server pattern
- Cause: Cron job not running or refresh token missing
- Fix: Check
refreshExpiringOAuthTokenscron job logs, verifyrefresh_tokenfield is not NULL
- Cause: User took more than 10 minutes to authorize
- Fix: Increase expiry in
authorize.tsor inform user to complete authorization faster
- Cause: Satellite hasn’t polled configuration yet
- Fix: Check satellite logs, verify satellite commands created, wait for next config poll
- Cause:
DEPLOYSTACK_ENCRYPTION_SECRETchanged between encryption and decryption - Fix: Ensure encryption secret is consistent across deployments
Log Analysis
Successful OAuth flow:Related Documentation
- OAuth Token Injection - How satellites inject tokens into MCP servers
- OAuth2 Server - MCP client API authentication (different OAuth system)
- OAuth Providers - Social login (GitHub, Google)
- MCP OAuth User Guide - User-facing documentation

