15 KiB
OAuth Architecture
This document explains how OAuth2/OIDC authentication works in the Nextcloud MCP Server implementation.
Overview
The Nextcloud MCP Server acts as an OAuth 2.0 Resource Server, protecting access to Nextcloud resources. It relies on Nextcloud's OIDC Identity Provider for user authentication and token validation.
Architecture Diagram
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ MCP Client │ │ MCP Server │ │ Nextcloud │
│ (Claude, │ │ (Resource │ │ Instance │
│ etc.) │ │ Server) │ │ │
│ │ │ │ │ │
└──────┬──────┘ └────────┬─────────┘ └────────┬────────┘
│ │ │
│ │ │
│ 1. Connect to MCP │ │
├─────────────────────────────────>│ │
│ │ │
│ 2. Return auth settings │ │
│ (issuer_url, scopes) │ │
│<─────────────────────────────────┤ │
│ │ │
│ │ │
│ 3. Start OAuth flow (with PKCE) │ │
├──────────────────────────────────┼────────────────────────────────────>│
│ │ /apps/oidc/authorize │
│ │ │
│ 4. User authenticates in browser│ │
│<─────────────────────────────────┼─────────────────────────────────────┤
│ │ │
│ 5. Authorization code (redirect)│ │
│<─────────────────────────────────┤ │
│ │ │
│ 6. Exchange code for token │ │
├──────────────────────────────────┼────────────────────────────────────>│
│ │ /apps/oidc/token │
│ │ │
│ 7. Access token │ │
│<─────────────────────────────────┼─────────────────────────────────────┤
│ │ │
│ │ │
│ 8. API request with Bearer token│ │
├─────────────────────────────────>│ │
│ Authorization: Bearer xxx │ │
│ │ │
│ │ 9. Validate token via userinfo │
│ ├────────────────────────────────────>│
│ │ /apps/oidc/userinfo │
│ │ │
│ │ 10. User info (token valid) │
│ │<────────────────────────────────────┤
│ │ │
│ │ 11. Nextcloud API request │
│ ├────────────────────────────────────>│
│ │ Authorization: Bearer xxx │
│ │ (Notes, Calendar, etc.) │
│ │ │
│ │ 12. API response │
│ │<────────────────────────────────────┤
│ │ │
│ 13. MCP tool response │ │
│<─────────────────────────────────┤ │
│ │ │
Components
1. MCP Client
- Any MCP-compatible client (Claude Desktop, Claude Code, custom clients)
- Initiates OAuth flow with PKCE (Proof Key for Code Exchange)
- Stores and sends access token with each request
- Example: Claude Desktop, Claude Code
2. MCP Server (Resource Server)
- Role: OAuth 2.0 Resource Server
- Location: This Nextcloud MCP Server implementation
- Responsibilities:
- Validates Bearer tokens by calling Nextcloud's userinfo endpoint
- Caches validated tokens (default: 1 hour TTL)
- Creates authenticated Nextcloud client instances per-user
- Enforces PKCE requirements (S256 code challenge method)
- Exposes Nextcloud functionality via MCP tools
Key Files:
app.py- OAuth mode detection and configurationauth/token_verifier.py- Token validation logicauth/context_helper.py- Per-user client creation
3. Nextcloud OIDC Apps
a) oidc - OIDC Identity Provider
- Role: OAuth 2.0 Authorization Server
- Location: Nextcloud app (
apps/oidc) - Endpoints:
/.well-known/openid-configuration- Discovery endpoint/apps/oidc/authorize- Authorization endpoint/apps/oidc/token- Token endpoint/apps/oidc/userinfo- User info endpoint (token validation)/apps/oidc/jwks- JSON Web Key Set/apps/oidc/register- Dynamic client registration
Configuration:
# Enable dynamic client registration (optional)
# Settings → OIDC → "Allow dynamic client registration"
b) user_oidc - OpenID Connect User Backend
- Role: Bearer token validation middleware
- Location: Nextcloud app (
apps/user_oidc) - Responsibilities:
- Validates Bearer tokens for Nextcloud API requests
- Creates user sessions from valid Bearer tokens
- Integrates with Nextcloud's authentication system
Configuration:
# Enable Bearer token validation (required)
php occ config:system:set user_oidc oidc_provider_bearer_validation --value=true --type=boolean
Important
The
user_oidcapp requires a patch to properly support Bearer token authentication for non-OCS endpoints. See Upstream Status for details.
4. Nextcloud Instance
- Role: Resource Owner / API Provider
- Provides: Notes, Calendar, Contacts, Deck, Files, etc.
Authentication Flow
Phase 1: OAuth Authorization (Steps 1-7)
- Client Connects: MCP client connects to MCP server
- Auth Settings: MCP server returns OAuth settings:
{ "issuer_url": "https://nextcloud.example.com", "resource_server_url": "http://localhost:8000", "required_scopes": ["openid", "profile"] } - OAuth Flow: Client initiates OAuth flow with PKCE
- Generates
code_verifier(random string) - Calculates
code_challenge= SHA256(code_verifier) - Redirects user to
/apps/oidc/authorizewithcode_challenge
- Generates
- User Authentication: User logs in to Nextcloud via browser
- Authorization Code: Nextcloud redirects back with authorization code
- Token Exchange: Client exchanges code for access token
- Sends
code+code_verifierto/apps/oidc/token - OIDC app validates PKCE challenge
- Sends
- Access Token: Client receives access token (JWT or opaque)
Phase 2: API Access (Steps 8-13)
- API Request: Client sends MCP request with Bearer token
- Token Validation: MCP server validates token:
- Checks cache (1-hour TTL by default)
- If not cached, calls
/apps/oidc/userinfowith Bearer token - Extracts username from
suborpreferred_usernameclaim
- User Info: Nextcloud returns user info if token is valid
- Nextcloud API Call: MCP server calls Nextcloud API on behalf of user
- Creates
NextcloudClientinstance with Bearer token - User-specific permissions apply
- Creates
- API Response: Nextcloud returns data
- MCP Response: MCP server returns formatted response to client
Token Validation
The MCP server validates tokens using the userinfo endpoint approach:
Why Userinfo (vs JWT Validation)?
Advantages:
- Works with both JWT and opaque tokens
- No need to manage JWKS rotation
- Always up-to-date (respects token revocation)
- Simpler implementation
Caching Strategy:
- Validated tokens cached for 1 hour (configurable)
- Cache keyed by token string
- Expired tokens re-validated automatically
Implementation: See NextcloudTokenVerifier
PKCE Requirement
The MCP server requires PKCE with S256 code challenge method:
- Server validates OIDC discovery advertises PKCE support
- Checks for
code_challenge_methods_supportedfield - Verifies
S256is included in supported methods - Logs error if PKCE not properly advertised
Why PKCE?:
- Required by MCP specification
- Protects against authorization code interception
- Essential for public clients (desktop apps, CLI tools)
Implementation: See validate_pkce_support()
Client Registration
The MCP server supports two client registration modes:
Automatic Registration (Dynamic Client Registration)
# No client credentials needed
NEXTCLOUD_HOST=https://nextcloud.example.com
How it works:
- Server checks
/.well-known/openid-configurationforregistration_endpoint - Calls
/apps/oidc/registerto register a client on first startup - Saves credentials to
.nextcloud_oauth_client.json - Reuses these credentials on subsequent startups
- Re-registers only if credentials are missing or expired
Best for: Development, testing, quick deployments
Pre-configured Client
# Manual client registration via CLI
php occ oidc:create --name="MCP Server" --type=confidential --redirect-uri="http://localhost:8000/oauth/callback"
# Configure MCP server
NEXTCLOUD_HOST=https://nextcloud.example.com
NEXTCLOUD_OIDC_CLIENT_ID=abc123
NEXTCLOUD_OIDC_CLIENT_SECRET=xyz789
Best for: Production, long-running deployments
Per-User Client Instances
Each authenticated user gets their own NextcloudClient instance:
# From MCP context (contains validated token)
client = get_client_from_context(ctx)
# Creates NextcloudClient with:
# - username: from token's 'sub' or 'preferred_username' claim
# - auth: BearerAuth(token)
Benefits:
- User-specific permissions
- Audit trail (actions appear from correct user)
- No shared credentials
- Multi-user support
Implementation: See get_client_from_context()
Security Considerations
Token Storage
- MCP client stores access token
- MCP server does NOT store tokens (validates per-request)
- Token validation results cached in-memory only
PKCE Protection
- Server validates PKCE is advertised
- Client MUST use PKCE with S256
- Protects against authorization code interception
Scopes
- Required scopes:
openid,profile - Additional scopes inferred from userinfo response
Token Validation
- Every MCP request validates Bearer token
- Cached for performance (1-hour default)
- Calls userinfo endpoint for validation
Configuration
See Configuration Guide for all OAuth environment variables:
| Variable | Purpose |
|---|---|
NEXTCLOUD_HOST |
Nextcloud instance URL |
NEXTCLOUD_OIDC_CLIENT_ID |
Pre-configured client ID (optional) |
NEXTCLOUD_OIDC_CLIENT_SECRET |
Pre-configured client secret (optional) |
NEXTCLOUD_MCP_SERVER_URL |
MCP server URL for OAuth callbacks |
NEXTCLOUD_OIDC_CLIENT_STORAGE |
Path for auto-registered credentials |
Testing
The integration test suite includes comprehensive OAuth testing:
- Automated tests (Playwright):
tests/client/test_oauth_playwright.py - Fixtures:
tests/conftest.py
Run OAuth tests:
# Start OAuth-enabled MCP server
docker-compose up --build -d mcp-oauth
# Run automated tests
uv run pytest tests/client/test_oauth_playwright.py --browser firefox -v
See Also
- OAuth Setup Guide - Configuration steps
- OAuth Quick Start - Get started quickly
- Upstream Status - Required upstream patches
- OAuth Troubleshooting - Common issues
- RFC 6749 - OAuth 2.0 Authorization Framework
- RFC 7636 - PKCE
- OpenID Connect Core 1.0