Major rewrite of ADR-004 to reflect federated authentication pattern with shared identity provider (IdP) instead of direct Nextcloud authentication. Key changes: - Replaced "Sign-in with Nextcloud" with "Federated Authentication" - Added shared IdP (Keycloak, Okta, Azure AD) as central auth provider - MCP server now acts as OAuth client to shared IdP, not Nextcloud - Single user authentication grants both identity and Nextcloud access - Updated all diagrams to show 4-party architecture - Removed authorize_nextcloud tool - uses standard 401 flow - Added proper token rotation with reuse detection - Clarified Pattern 3 vs Pattern 4 differences in comparison doc - Pattern 3 can use external IdPs via user_oidc (not limited to NC) Architecture benefits: - True single sign-on with enterprise IdP support - OAuth-compliant on-behalf-of pattern - Supports SAML/LDAP backends through IdP - Nextcloud validates IdP tokens, not MCP-specific tokens 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
OAuth Architecture Comparison: MCP Server Authentication Patterns
This document compares three authentication architectures for the MCP server, explaining the evolution from pass-through authentication to true offline access capabilities.
Pattern 1: Pass-Through Authentication (Current Implementation)
Architecture
┌─────────────┐ OAuth Flow ┌─────────────┐
│ MCP Client │◄──────────────────│ OAuth │
│ (Claude) │ │ Provider │
└──────┬──────┘ └─────────────┘
│
│ Access Token
│ (per request)
▼
┌─────────────┐ ┌─────────────┐
│ MCP Server │───────────────────►│ Nextcloud │
│(Pass-through) │ APIs │
└─────────────┘ └─────────────┘
Characteristics
| Aspect | Description |
|---|---|
| Token Flow | MCP Client → MCP Server → Nextcloud |
| Token Storage | None (tokens exist only during request) |
| Offline Access | ❌ Impossible |
| Background Workers | ❌ Not supported |
| User Consent | Single OAuth flow (client-managed) |
| Complexity | Low |
| Security | High (no token persistence) |
How It Works
- MCP Client performs OAuth with provider
- Client includes access token in each MCP request
- MCP Server validates token and forwards to Nextcloud
- Token discarded after request completes
Limitations
- No operations possible without active MCP session
- Background sync/indexing impossible
- Cannot refresh tokens independently
Pattern 2: Token Exchange Delegation (ADR-002 - Flawed)
Architecture
┌─────────────┐ ┌─────────────┐
│ MCP Client │────────────────────│ OAuth │
│ (Claude) │ │ Provider │
└──────┬──────┘ └──────┬──────┘
│ │
│ Access Token │ Service Account Token
▼ ▼
┌─────────────────────────────────────────────┐
│ MCP Server │
│ ┌────────────────────────────────────┐ │
│ │ Token Exchange (RFC 8693) │ │
│ │ Subject: Service Account │ │
│ │ Target: User │ │
│ └────────────────────────────────────┘ │
└───────────────┬─────────────────────────────┘
│ Exchanged Token
▼
┌─────────────┐
│ Nextcloud │
│ APIs │
└─────────────┘
Characteristics
| Aspect | Description |
|---|---|
| Token Flow | Service Account → Exchange → User Token |
| Token Storage | None (MCP server still stateless) |
| Offline Access | ❌ Still impossible (circular dependency) |
| Background Workers | ❌ Requires service account (rejected) |
| User Consent | Implicit through service account |
| Complexity | High |
| Security | ⚠️ Service accounts violate OAuth principles |
Why It Fails
- Circular Dependency: To exchange tokens, you need a token to exchange
- Service Account Problem: Creates Nextcloud user identity for service
- OAuth Violation: Service acts as itself, not on behalf of users
- No Bootstrap: Still can't obtain initial tokens offline
The Fatal Flaw
Q: How does background worker get tokens?
A: Use token exchange with service account
Q: How does service account get authorized?
A: Client credentials grant creates user account (violates OAuth)
Q: Can we use user's refresh token?
A: MCP server never sees refresh tokens (by design)
Pattern 3: Sign-in with Nextcloud (Previous ADR-004 Draft)
Architecture
┌─────────────┐ ┌─────────────────┐ ┌────────────┐
│ MCP Client ├───────────────────> │ MCP Server ├────────────────────>│ Nextcloud │
│ (Claude) │ (MCP Protocol) │ (OAuth Client) │ (OIDC + APIs) │ (IdP) │
└─────────────┘ └─────────────────┘ └────────────┘
│
┌──────▼────────┐
│ Token Storage │
│ (NC Tokens) │
└───────────────┘
Characteristics
| Aspect | Description |
|---|---|
| Token Flow | MCP Server uses Nextcloud as identity provider |
| Token Storage | ✅ Encrypted Nextcloud refresh tokens |
| Offline Access | ✅ Full support |
| Background Workers | ✅ Use stored refresh tokens |
| User Consent | Single OAuth flow (Nextcloud only) |
| Complexity | Medium |
| Security | High (with token rotation) |
How It Works
-
Initial Setup:
- User tries to use MCP tool
- MCP server returns auth required
- User authenticates with Nextcloud's OIDC endpoint
- Nextcloud may use user_oidc to delegate to external IdP (Keycloak, etc.)
- MCP server stores Nextcloud-issued refresh token (encrypted)
-
Subsequent Requests:
- MCP server uses stored Nextcloud tokens
- Refreshes automatically when expired
- No client involvement needed
-
Background Operations:
- Worker retrieves stored refresh token
- Refreshes with Nextcloud directly
- Performs operations independently
Advantages
- ✅ Single sign-on with Nextcloud
- ✅ True offline access capability
- ✅ OAuth-compliant with proper consent
- ✅ Supports external IdPs via user_oidc
- ✅ Simpler integration - only one OAuth endpoint
Trade-offs
- Authentication flows through Nextcloud
- Nextcloud manages IdP relationships (via user_oidc)
- MCP server only knows about Nextcloud, not the underlying IdP
Pattern 4: Federated Authentication Architecture (ADR-004 - Solution)
Architecture
┌─────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌────────────┐
│ MCP Client │◄──────401──────│ MCP Server │◄────OAuth──────│ Shared IdP │──Validates──►│ Nextcloud │
│ (Claude) │ │ (OAuth Client) │ (On-Behalf) │ (Keycloak) │ Tokens │(Resource) │
└─────────────┘ └─────────────────┘ └──────────────┘ └────────────┘
│
┌───────▼────────┐
│ Token Storage │
│ (IdP Tokens) │
└────────────────┘
Characteristics
| Aspect | Description |
|---|---|
| Token Flow | Shared IdP issues tokens for Nextcloud access |
| Token Storage | ✅ Encrypted IdP refresh tokens |
| Offline Access | ✅ Full support |
| Background Workers | ✅ Use stored IdP refresh tokens |
| User Consent | Single OAuth flow (IdP manages consent) |
| Complexity | Medium-High |
| Security | Highest (enterprise-grade IdP) |
How It Works
-
Initial Setup:
- MCP client connects, receives 401
- Browser opens MCP server OAuth URL
- MCP server redirects to shared IdP
- User authenticates once to IdP
- IdP shows consent for both identity and Nextcloud access
- MCP server stores IdP refresh token (encrypted)
- MCP server issues session token to client
-
Subsequent Requests:
- MCP server validates session token
- Uses stored IdP token for Nextcloud
- Refreshes with IdP when expired
- No client involvement needed
-
Background Operations:
- Worker retrieves stored IdP refresh token
- Gets new access token from IdP
- Uses token to access Nextcloud
- Performs operations independently
Advantages
- ✅ True single sign-on (SSO)
- ✅ Enterprise-ready with SAML/LDAP support
- ✅ OAuth-compliant with proper delegation
- ✅ Direct IdP relationship - no intermediary
- ✅ Flexible - can swap resource servers
- ✅ Industry-standard federated pattern
Trade-offs
- Requires shared IdP infrastructure
- More complex initial setup
- Token validation overhead
Comparison Matrix
| Feature | Pass-Through | Token Exchange | Sign-in with NC | Federated Auth |
|---|---|---|---|---|
| Offline Access | ❌ No | ❌ No | ✅ Yes | ✅ Yes |
| Background Workers | ❌ No | ❌ No* | ✅ Yes | ✅ Yes |
| Token Storage | None | None | NC refresh tokens | IdP refresh tokens |
| OAuth Compliance | ✅ Full | ⚠️ Violates | ✅ Full | ✅ Full |
| User Consent | Once | Implicit | Once (NC) | Once (IdP) |
| Implementation Complexity | Low | High | Medium | Medium-High |
| Security | High | Medium | High | Highest |
| Enterprise Ready | ❌ No | ❌ No | ⚠️ Indirect | ✅ Yes |
| Identity Provider | Client-managed | N/A | Nextcloud (+user_oidc) | Shared IdP |
| Suitable For | Interactive only | N/A (flawed) | Small teams | Enterprise |
* Requires service accounts that violate OAuth principles
Evolution Summary
Stage 1: Simple Pass-Through ✅
- Goal: Basic MCP functionality
- Result: Works well for interactive use
- Limitation: No offline capabilities
Stage 2: Attempted Delegation ❌
- Goal: Enable offline access without changing architecture
- Result: Circular dependencies, OAuth violations
- Learning: MCP protocol constraints are fundamental
Stage 3: Sign-in with Nextcloud ⚠️
- Goal: True offline access with OAuth compliance
- Result: MCP server uses Nextcloud as identity provider
- Limitation: Tight coupling to Nextcloud, no enterprise IdP
Stage 4: Federated Pattern ✅
- Goal: Enterprise-ready offline access
- Result: Shared IdP for both MCP server and Nextcloud
- Trade-off: Additional infrastructure justified by enterprise needs
Key Insights
-
Pattern 3 vs Pattern 4: Both support external IdPs, but differ in integration approach:
- Pattern 3: MCP → Nextcloud OIDC → (user_oidc) → External IdP
- Pattern 4: MCP → External IdP directly (Nextcloud also uses same IdP)
- Choose Pattern 3 for Nextcloud-centric deployments, Pattern 4 for IdP-centric enterprises
-
The MCP Protocol Boundary: The MCP protocol creates a fundamental boundary between client and server token management. Attempting to breach this boundary (ADR-002) leads to architectural contradictions.
-
Service Accounts Don't Solve User Problems: Using service accounts for user operations violates OAuth's core principle of acting on behalf of users, not as a service identity.
-
Double OAuth is Industry Standard: Major platforms (Zapier, IFTTT, Microsoft Power Automate) use this pattern - the integration platform is an OAuth client that maintains its own relationships with upstream services.
-
Refresh Tokens Are The Solution: The OAuth spec designed refresh tokens specifically for offline access. Rejecting them (as ADR-002 did) means rejecting the standard solution.
-
Complexity is Justified: The additional complexity of managing OAuth flows is acceptable when offline access is a requirement. The alternative is no offline access at all.
Recommendations
For Simple Deployments
Use Pattern 1 (Pass-Through) if:
- Offline access not needed
- Only interactive operations required
- Simplicity is priority
For Teams Using Nextcloud
Use Pattern 3 (Sign-in with Nextcloud) if:
- Background sync/indexing required
- Nextcloud manages your authentication
- Can use external IdPs via user_oidc
- Prefer single integration point through Nextcloud
For Enterprise Deployments
Use Pattern 4 (Federated Authentication) if:
- Enterprise IdP already exists (Keycloak, Okta, Azure AD)
- Multiple resource servers beyond Nextcloud
- Compliance requirements for centralized auth
- Building platform for multiple organizations
Never Use Pattern 2
Token Exchange with service accounts should not be used as it:
- Doesn't enable true offline access
- Violates OAuth principles
- Adds complexity without solving the problem