Chris Coutinho
e331544cee
feat: Implement RFC 8693 token exchange for Keycloak (ADR-002 Tier 2)
...
Implements OAuth 2.0 Token Exchange (RFC 8693) enabling the MCP server to
exchange service account tokens for user-scoped tokens. This provides an
alternative to refresh tokens for background operations.
**Core Implementation:**
- Added `get_service_account_token()` method to KeycloakOAuthClient for
client_credentials grant
- Added `exchange_token_for_user()` method implementing RFC 8693 token exchange
- Fixed Fernet encryption key handling in RefreshTokenStorage (was incorrectly
base64 decoding already-encoded keys)
- Updated OAuth configuration to support offline_access scope and refresh token
storage infrastructure
**Keycloak Configuration:**
- Enabled `serviceAccountsEnabled` in realm-export.json
- Added `token.exchange.grant.enabled` attribute
- Added `client.token.exchange.standard.enabled` attribute (required for
Keycloak 26.2+ Standard Token Exchange V2)
- Fresh Keycloak imports now correctly enable token exchange
**Docker Compose:**
- Added TOKEN_ENCRYPTION_KEY and ENABLE_OFFLINE_ACCESS environment variables
- Created oauth-tokens volume for refresh token storage
- Configured both mcp-oauth and mcp-keycloak services
**Testing & Documentation:**
- Added tests/manual/test_token_exchange.py - Validates complete RFC 8693 flow
- Added tests/manual/test_nextcloud_impersonate.py - Documents session-based
impersonation limitations
- Added docs/oauth-impersonation-findings.md - Comprehensive investigation
findings and resolution documentation
**Verified Working:**
✅ Service account token acquisition (client_credentials grant)
✅ RFC 8693 token exchange for internal-to-internal tokens
✅ Exchanged tokens validate with Nextcloud APIs
✅ Keycloak 26.4.2 Standard Token Exchange V2 support
**Known Limitations:**
- User impersonation (requested_subject) requires Keycloak Legacy V1 with
preview features
- Cross-client token exchange limited to same realm
- Refresh token storage infrastructure ready but unused (MCP protocol limitation)
Dependencies: aiosqlite>=0.20.0
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:19 +01:00
Chris Coutinho
f34366a260
feat: Add Keycloak OAuth provider support with refresh token storage
...
Implements Keycloak as an external OIDC provider following ADR-002
architecture for background job authentication using offline_access.
## Features
- Keycloak OAuth provider with PKCE and offline_access support
- Refresh token storage with Fernet encryption
- Token verifier for both JWT and opaque tokens
- Multi-client validation (realm-level trust)
- Sample configuration for Keycloak integration
## Implementation
### OAuth Provider (keycloak_oauth.py)
- Authorization Code Flow with PKCE
- Refresh token exchange
- OIDC discovery endpoint support
- Token validation with JWKS
### Token Storage (refresh_token_storage.py)
- Encrypted storage using Fernet symmetric encryption
- SQLite backend for persistence
- Token rotation support
- Per-user token management
### Token Verifier Updates
- Support both JWT (self-encoded) and opaque tokens
- JWKS-based JWT signature verification
- Introspection endpoint fallback for opaque tokens
- Scope extraction from both token types
### Configuration
- .env.keycloak.sample: Example configuration with Keycloak URLs
- docs/keycloak-multi-client-validation.md: Realm-level validation documentation
- app-hooks/post-installation/10-install-user_oidc-app.sh: Updated dependencies
## Architecture Notes
- MCP Server is a protected resource (requires OAuth)
- MCP Client initiates OAuth flow and shares refresh tokens
- Refresh tokens enable background operations without admin credentials
- Supports future token exchange delegation when Keycloak implements it
## References
- ADR-002: Vector Database Background Sync Authentication
- RFC 6749: OAuth 2.0 (offline_access, refresh tokens)
- RFC 7517: JSON Web Key (JWK)
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:19 +01:00
Chris Coutinho
529dc4616b
docs: Implement separate clients architecture for Keycloak integration
...
Implements proper OAuth 2.0 separation following RFC 8707 best practices
with distinct resource server and OAuth client configurations.
## Architecture Changes
- Create separate "nextcloud" bearer-only client (resource server)
- Configure "nextcloud-mcp-server" OAuth client with audience mapper
- Audience mapper targets "nextcloud" resource server
- Token flow: aud="nextcloud", azp="nextcloud-mcp-server"
## Benefits
- Proper OAuth client vs resource server separation
- Support for future multi-resource tokens: aud=["nextcloud", "other-service"]
- RFC 8707 Resource Indicators compliance
- Clear requester identification via azp claim
## Documentation Updates
- Correct OAuth flow: MCP Client initiates, handles redirect, shares tokens
- Explain MCP Server as protected resource architecture
- Document offline_access with refresh tokens (Tier 1, current)
- Document token exchange with delegation (Tier 2, future when Keycloak adds support)
- Reference Keycloak issue #38279 for delegation status
## Files
- keycloak/realm-export.json: Add separate clients configuration
- app-hooks/post-installation/15-setup-keycloak-provider.sh: Setup user_oidc with "nextcloud" client
- docs/audience-validation-setup.md: Comprehensive documentation with corrected OAuth flow and delegation comparison
- docker-compose.yml: Fix Keycloak healthcheck (bash TCP instead of curl)
- scripts/test_separate_clients.sh: Verification script for architecture
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:19 +01:00
Chris Coutinho
ef07b1a6c9
docs: Add ADRs
2025-10-31 02:59:44 +01:00
Chris Coutinho
49f9cead69
docs: Update OAuth architecture
2025-10-25 21:54:30 +02:00
Chris Coutinho
415b1c901b
docs: Parse available scopes from registered tools and update docs
2025-10-25 21:16:40 +02:00
Chris Coutinho
d452684535
feat: Split read/write scopes into app:read/write scopes
2025-10-24 04:38:49 +02:00
Chris Coutinho
f2d2dd8068
feat: Enable token introspection for opaque tokens
2025-10-23 15:51:27 +02:00
Chris Coutinho
d915efd3f6
docs: Update jwt docs [skip ci]
2025-10-23 15:26:51 +02:00
Chris Coutinho
1a7ce5b7a7
docs: Update jwt docs [skip ci]
2025-10-23 12:22:34 +02:00
Chris Coutinho
b4039e2e40
docs: Update jwt docs
2025-10-23 11:20:49 +02:00
Chris Coutinho
aa0b6dc5dd
docs: Update docs
2025-10-20 19:10:23 +02:00
Chris Coutinho
c3ff92a8c1
test: Cleanup testing fixtures regarding canceled scopes
2025-10-18 22:02:25 +02:00
Chris Coutinho
83917b3786
perf(notes): Improve notes search performance using async iterators
2025-10-18 22:02:19 +02:00
Chris Coutinho
9de59db718
feat(cookbook): Add full Cookbook app support with 13 tools and 2 resources
...
- Import recipes from URLs using schema.org metadata
- Full CRUD operations for recipes
- Search, categorize, and organize recipes
- Manage keywords/tags and categories
- Configure app settings and trigger reindexing
2025-10-17 03:08:16 +02:00
Chris Coutinho
3ad9198f36
fix(oauth): Remove the option to force_register new clients
2025-10-15 16:27:22 +02:00
Chris Coutinho
97bbc18121
docs: Update README
...
Add comparison to the Nextcloud Assistant & Context Agent
2025-10-15 14:47:43 +02:00
Chris Coutinho
3ed24bd5e3
docs: restructure documentation
2025-10-14 01:23:49 +02:00
Chris Coutinho
4b19964817
docs: Update docs
2025-10-14 01:23:38 +02:00
Chris Coutinho
ea468889ce
docs: Remove pip
2025-10-14 01:23:38 +02:00
Chris Coutinho
9ef9fff2b0
docs: Update Docs
2025-10-14 01:23:38 +02:00
Chris Coutinho
2489a714b8
docs: Update README and docs
2025-10-14 01:23:37 +02:00
Chris Coutinho
17979accb6
test: Add patch for user_oidc app and update docs
2025-10-14 01:23:31 +02:00
Chris Coutinho
9711d1d161
docs: fix duplicate
2025-09-11 17:31:00 +02:00
Chris Coutinho
b3cd2ace34
chore: Update README.md, move docs to directory
2025-09-11 17:28:13 +02:00
Chris Coutinho
c1763ebc6a
ADR search and handling categories in notes
2025-05-07 23:06:22 +02:00
Chris Coutinho
04e4a8e0a8
Add support for attachments in notes
2025-05-06 02:52:51 +02:00