Resolves the token exchange implementation gap where get_session_client()
was implemented but never used by tools. Unifies token acquisition into a
single async get_client() method that handles both pass-through and token
exchange modes transparently.
Core Changes:
- Make get_client() async and merge token exchange logic into it
- Remove scopes parameter from token exchange (Nextcloud doesn't support OAuth scopes)
- Update all 8 tool modules to use await get_client(ctx)
- Fix provisioning decorator to skip checks in BasicAuth mode
Token Acquisition Modes:
1. BasicAuth: Returns shared client (no token operations)
2. OAuth pass-through (default): Verifies and passes Flow 1 token to Nextcloud
3. OAuth token exchange (opt-in): Exchanges Flow 1 token for ephemeral token via RFC 8693
Key Architectural Clarifications:
- Progressive Consent (Flow 1/2) = Authorization architecture
- Token Exchange = Token acquisition pattern during tool execution
- Refresh tokens from Flow 2 are NEVER used for tool calls (only background jobs)
- Nextcloud scopes are "soft-scopes" enforced by MCP server, not IdP
Documentation Updates:
- ADR-004: Added comprehensive token acquisition patterns section
- CRITICAL-TOKEN-EXCHANGE-PATTERN.md: Updated to reflect implementation status
- CLAUDE.md: Updated architectural patterns with async get_client()
Testing:
- All 36 unit tests passing
- All 4 smoke tests passing (BasicAuth mode)
- Linting issues fixed (ruff)
Configuration:
ENABLE_TOKEN_EXCHANGE=false (default) - pass-through mode
ENABLE_TOKEN_EXCHANGE=true (opt-in) - token exchange mode
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace hybrid flow model with true progressive consent where MCP client authenticates directly to IdP (Flow 1) and server requests separate explicit provisioning for Nextcloud access (Flow 2). This separates client authentication from resource authorization, uses distinct client_id for each flow, and keeps server stateless by default until user explicitly grants offline access via provision_nextcloud_access tool.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Refactor ADR-004 to document the proper OAuth architecture where MCP
clients are registered at the IdP level (not with MCP server) and use
a progressive consent pattern with dual OAuth flows.
## Key Changes
### MCP Client Registration
- Document that MCP clients (Claude Desktop, etc.) register at IdP level
- Show DCR and pre-registration options
- Clarify client validation happens against IdP registry
### Progressive Consent Architecture
Replace single "Hybrid Flow" with three-phase progressive consent:
**Phase 1: MCP Client Authentication** (Always)
- MCP client uses own client_id (e.g., "claude-desktop")
- User consents to "Claude Desktop accessing MCP Server"
- MCP server validates client exists at IdP
- Stores MCP client access token
**Phase 2: Nextcloud Consent** (Conditional)
- Only if MCP server doesn't have refresh token for user
- MCP server uses own client_id ("nextcloud-mcp-server")
- User consents to "MCP Server accessing Nextcloud offline"
- MCP server stores master refresh token
- SSO: If already authenticated, only consent needed
**Phase 3: Token Exchange** (Standard PKCE)
- Client exchanges MCP authorization code
- Validates PKCE code_verifier
- Returns access token (aud: mcp-server)
- Client never sees master refresh token
### Implementation Status Section
- Document current implementation as "simplified hybrid flow"
- List what's implemented vs what needs refactoring
- Clarify current tests use simplified version
- Note progressive consent is target architecture
## Benefits of Progressive Consent
✅ Standards-compliant: Proper OAuth clients at IdP level
✅ Secure: Client validation against IdP registry
✅ Efficient: Nextcloud consent only once per user
✅ Transparent: Users understand each authorization step
✅ SSO-friendly: Minimal re-authentication in Phase 2
## Implementation Tracking
The refactoring from simplified hybrid flow to progressive consent will
be tracked in a separate issue. Current implementation demonstrates:
- MCP server can intercept OAuth callbacks
- Refresh tokens stored securely
- PKCE flow works end-to-end
- Tool execution succeeds
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Critical architectural corrections to properly implement secure token brokering:
## Key Changes:
1. **Removed Dual Token Concept**: MCP server no longer generates its own JWTs.
Instead, it acts as a token broker using IdP-issued tokens with proper
audience validation.
2. **Strict Audience Isolation**:
- Tokens with `aud: "mcp-server"` can ONLY authenticate to MCP server
- Tokens with `aud: "nextcloud"` can ONLY access Nextcloud APIs
- No tokens have multiple audiences (security boundary violation)
- Compromised MCP tokens cannot access Nextcloud directly
3. **Linked Authorization Pattern**: Single OAuth flow obtains a master
refresh token capable of minting tokens for different audiences as needed.
This solves the challenge of needing both MCP authentication and Nextcloud
access from a single user authorization.
4. **Token Broker Implementation**:
- Validates incoming tokens have `audience: "mcp-server"`
- Uses stored refresh tokens to obtain `audience: "nextcloud"` tokens
- Never exposes Nextcloud tokens to MCP clients
- Maintains short-lived cache for performance
5. **PKCE and Native Client Updates**:
- Proper 302 redirects (no HTML pages)
- Complete PKCE verification in token endpoint
- IdP tokens returned directly (not MCP-generated)
6. **Security Enhancements**:
- Comprehensive audience validation examples
- Token exchange pattern documentation
- Keycloak configuration for audience mapping
- Trust boundary diagrams
This architecture maintains strict security boundaries while enabling the
MCP server to act on behalf of users for both authentication and resource
access, following OAuth best practices and enterprise security standards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
- Supersedes ADR-002 which fundamentally misunderstood MCP protocol constraints
- Introduces "Sign-in with Nextcloud" architecture pattern
- MCP server becomes OAuth client to enable offline/background operations
- Implements full token rotation with reuse detection for security
- Includes comprehensive implementation details and migration strategy
Key architectural shift:
- From: Pass-through authentication (stateless, no offline access)
- To: MCP server as OAuth client (stateful, full offline capabilities)
The solution enables background workers to operate independently of MCP
sessions by storing and rotating refresh tokens securely.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>