Chris Coutinho
23360485a8
refactor: Remove NEXTCLOUD_OIDC_CLIENT_STORAGE environment variable
...
Remove the NEXTCLOUD_OIDC_CLIENT_STORAGE environment variable from all
configuration files. OAuth client credentials are now always stored in the
SQLite database, with no option to use a custom JSON file path.
Changes:
- Remove NEXTCLOUD_OIDC_CLIENT_STORAGE from .env.keycloak.sample
- Remove NEXTCLOUD_OIDC_CLIENT_STORAGE from docker-compose.yml (mcp-oauth and mcp-keycloak services)
- Remove NEXTCLOUD_OIDC_CLIENT_STORAGE from Helm deployment template
- Remove NEXTCLOUD_OIDC_CLIENT_STORAGE from test_cli.py test assertions
- Remove --headed flag from pytest addopts (use CLI arg instead)
This simplifies configuration by enforcing a single storage mechanism
(SQLite database) for OAuth client credentials.
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:21 +01:00
Chris Coutinho
4c7d1cfc8d
test: Add scope-based authorization tests for Keycloak external IdP
...
This enhances the Keycloak integration test suite with comprehensive
scope-based authorization validation, matching the OIDC test structure.
Changes:
- Add 3 test users to Keycloak realm (read-only, write-only, no-custom-scopes)
- Create OAuth token fixtures with different scope combinations
- Create MCP client fixtures for each scope configuration
- Add 4 new tests validating scope-based tool filtering:
* Read-only tokens filter out write tools
* Write-only tokens filter out read tools
* Full access tokens show all 90+ tools
* No custom scopes result in zero tools
Test Results:
- All 15 Keycloak integration tests pass (11 existing + 4 new)
- Validates proper JWT scope enforcement in external IdP architecture
- Confirms security isolation when users decline custom scopes
This completes ADR-002 scope authorization testing for the Keycloak
external identity provider integration.
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:21 +01:00
Chris Coutinho
849c67c32a
fix: Complete Keycloak external IdP integration with all tests passing
...
This commit completes the Keycloak external IdP integration for the MCP
server, implementing ADR-002 Tier 2 (External Identity Provider) with
full Bearer token authentication support.
Key Changes:
1. **Keycloak backchannel-dynamic configuration**
- Added --hostname-strict=false and --hostname-backchannel-dynamic=true
- Allows external issuer (localhost:8888) with internal endpoints (keycloak:8080)
- Solves Docker networking issue where containers can't reach localhost
2. **CORSMiddleware Bearer token patch**
- Created app-hooks/patches/cors-bearer-token.patch from upstream commit 8fb5e77db82
- Allows Bearer tokens to bypass CORS/CSRF checks (stateless authentication)
- Applied via post-installation hook 20-apply-cors-bearer-token-patch.sh
- Enables app-specific APIs (Notes, Calendar, etc.) to work with Bearer tokens
3. **Patch organization**
- Moved patches to app-hooks/patches/ directory
- Updated docker-compose.yml to mount entire app-hooks directory
- Consolidated patch management for better maintainability
4. **Test improvements**
- All 11 Keycloak integration tests passing
- Tests validate OAuth token acquisition, MCP connectivity, token validation,
tool execution, token persistence, user provisioning, scope filtering,
and error handling
Architecture:
- Keycloak acts as external OAuth/OIDC identity provider
- MCP server uses Keycloak tokens to access Nextcloud APIs
- Nextcloud user_oidc app validates Bearer tokens from Keycloak
- No admin credentials needed - all API access uses user's OAuth tokens
Cache Note:
- Discovery and JWKS caches must be cleared when switching Keycloak configurations
- Use: docker compose exec redis redis-cli DEL "<cache-key>"
- Or: docker compose exec app php occ user_oidc:provider keycloak --clientid nextcloud
Related:
- ADR-002: Vector sync background jobs authentication
- Validates external IdP integration pattern
- Demonstrates offline_access with refresh tokens (Tier 1 & 2)
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:20 +01:00
Chris Coutinho
b3725dd2f5
test: Remove --headed from pytest addopts
2025-11-02 22:03:20 +01:00
Chris Coutinho
403f8be429
feat: Add Keycloak external IdP integration with custom scopes
...
Add comprehensive support for using Keycloak as an external identity
provider with Nextcloud custom scopes. This enables testing of ADR-002
external IdP integration patterns.
**Keycloak Realm Configuration:**
- Add frontendUrl attribute to issue tokens with public issuer URL
- Define 18 Nextcloud custom client scopes (notes:read/write,
calendar:read/write, contacts:read/write, cookbook:read/write,
deck:read/write, tables:read/write, files:read/write,
sharing:read/write, todo:read/write)
- Add all custom scopes to nextcloud-mcp-server client optional scopes
- Scopes include consent screen text for user-friendly OAuth flow
**MCP Server Configuration:**
- Add OIDC_JWKS_URI environment variable support
- Implement JWKS URI override logic for Docker networking
- Update NEXTCLOUD_PUBLIC_ISSUER_URL to include full realm path
- Enable MCP server to fetch JWKS from internal Docker network
**Test Infrastructure:**
- Add keycloak_oauth_client_credentials fixture (session-scoped)
- Add keycloak_oauth_token fixture with Playwright automation
- Implement PKCE (S256) support for Keycloak OAuth flow
- Add nc_mcp_keycloak_client fixture for MCP testing
- Create comprehensive test suite in test_keycloak_external_idp.py
**Tests Created:**
- test_keycloak_oauth_token_acquisition: Token acquisition via Playwright
- test_keycloak_oauth_client_credentials_discovery: OIDC discovery
- test_mcp_client_connects_to_keycloak_server: MCP connectivity
- test_external_idp_server_initialization: Server auto-detection
- test_external_idp_token_validation: Token validation flow
- test_tools_work_with_keycloak_token: End-to-end tool execution
- test_keycloak_token_persistence: Multi-operation token reuse
- test_user_auto_provisioning: Nextcloud user provisioning
- test_scope_filtering_with_keycloak: Scope-based tool filtering
- test_keycloak_error_handling: Error handling
- test_external_idp_architecture: Architecture documentation
**Current Status:**
- ✅ Keycloak realm configuration complete
- ✅ Custom scopes defined and available
- ✅ OAuth token acquisition working (1 test passing)
- ⚠️ Token validation needs additional work (external IdP userinfo)
**Files Modified:**
- keycloak/realm-export.json: Realm configuration with scopes
- tests/conftest.py: Keycloak OAuth fixtures (+285 lines)
- tests/server/oauth/test_keycloak_external_idp.py: New test suite
- docker-compose.yml: OIDC_JWKS_URI and issuer configuration
- nextcloud_mcp_server/app.py: JWKS URI override logic
🤖 Generated with [Claude Code](https://claude.com/claude-code )
Co-Authored-By: Claude <noreply@anthropic.com >
2025-11-02 22:03:20 +01:00
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
github-actions[bot]
5259658458
bump: version 0.22.6 → 0.22.7
2025-10-29 11:18:41 +00:00
github-actions[bot]
e1aca04aff
bump: version 0.22.5 → 0.22.6
2025-10-29 10:57:44 +00:00
github-actions[bot]
e647c87dd8
bump: version 0.22.4 → 0.22.5
2025-10-29 10:54:54 +00:00
github-actions[bot]
202058bdc8
bump: version 0.22.3 → 0.22.4
2025-10-29 10:44:11 +00:00
Chris Coutinho
e602684743
fix(helm): Update helm version with release
2025-10-29 11:43:02 +01:00
github-actions[bot]
8221046d8a
bump: version 0.22.2 → 0.22.3
2025-10-29 10:35:58 +00:00
Chris Coutinho
3e45b6ca25
fix(helm): Update helm version with release
2025-10-29 11:34:58 +01:00
github-actions[bot]
9ec7637579
bump: version 0.22.1 → 0.22.2
2025-10-29 10:30:39 +00:00
Chris Coutinho
670188f9e4
fix(helm): Update helm version with release
2025-10-29 11:29:59 +01:00
github-actions[bot]
3878beaf65
bump: version 0.22.0 → 0.22.1
2025-10-29 10:17:08 +00:00
github-actions[bot]
0e7e74867f
bump: version 0.21.0 → 0.22.0
2025-10-29 09:32:27 +00:00
github-actions[bot]
57a2157c58
bump: version 0.20.0 → 0.21.0
2025-10-25 18:33:56 +00:00
yuisheaven
f0e5333e43
Merge branch 'master' into feature/introduce_files_parsing_with_unstructured_service_for_webdav_files_retrieval
2025-10-25 17:23:38 +02:00
github-actions[bot]
04e0ab127a
bump: version 0.19.1 → 0.20.0
2025-10-24 18:24:45 +00:00
Chris Coutinho
1117a83a52
Merge pull request #237 from cbcoutinho/feature/app-scopes
...
Feature/app scopes
2025-10-24 20:24:15 +02:00
github-actions[bot]
50a824155c
bump: version 0.19.0 → 0.19.1
2025-10-24 04:36:51 +00:00
renovate-bot-cbcoutinho[bot]
3baf10662f
fix(deps): update dependency mcp to >=1.19,<1.20
2025-10-24 04:06:55 +00:00
Chris Coutinho
d452684535
feat: Split read/write scopes into app:read/write scopes
2025-10-24 04:38:49 +02:00
github-actions[bot]
bfbaed9a66
bump: version 0.18.0 → 0.19.0
2025-10-23 23:50:51 +00:00
yuisheaven
29df645d53
Merge branch 'master' into feature/introduce_files_parsing_with_unstructured_service_for_webdav_files_retrieval
2025-10-23 21:30:09 +02:00
github-actions[bot]
87c6f077f3
bump: version 0.17.1 → 0.18.0
2025-10-23 10:23:48 +00:00
Chris Coutinho
e48f5f3f30
feat(server): Add support for custom OIDC scopes and permissions via JWTs
2025-10-23 08:37:36 +02:00
Chris Coutinho
c069d78f80
feat: Initialize JWT-scoped tools
2025-10-22 06:21:16 +02:00
yuisheaven
64649c902d
Merge branch 'master' into feature/introduce_files_parsing_with_unstructured_service_for_webdav_files_retrieval
2025-10-21 20:37:00 +02:00
github-actions[bot]
4984496d81
bump: version 0.17.0 → 0.17.1
2025-10-20 21:16:09 +00:00
Chris Coutinho
989b6de3c0
build: Switch to uv build backend
2025-10-20 20:10:57 +02:00
Chris Coutinho
aa0b6dc5dd
docs: Update docs
2025-10-20 19:10:23 +02:00
github-actions[bot]
45bbf97033
bump: version 0.16.0 → 0.17.0
2025-10-19 22:55:23 +00:00
Chris Coutinho
d398a8c8e6
refactor: Migrate from internal CalendarClient to caldav library
2025-10-19 15:47:17 +02:00
github-actions[bot]
cb7a609ec2
bump: version 0.15.2 → 0.16.0
2025-10-19 00:13:49 +00:00
Chris Coutinho
d5e6411c45
test: disable asyncio fixture
2025-10-19 00:49:24 +02:00
Chris Coutinho
b72514bb32
ci: Add pytest-timeout to dev deps
2025-10-19 00:27:19 +02:00
Chris Coutinho
1459fe9bc8
test: Replace pytest-asyncio plugin fixtures with anyio fixtures
2025-10-18 22:02:25 +02:00
Chris Coutinho
37164dbdbc
chore: sort imports
2025-10-18 22:02:25 +02:00
github-actions[bot]
a389f2940e
bump: version 0.15.1 → 0.15.2
2025-10-17 23:17:32 +00:00
github-actions[bot]
7549c988f4
bump: version 0.15.0 → 0.15.1
2025-10-17 02:49:37 +00:00
github-actions[bot]
0aeef1b87e
bump: version 0.14.3 → 0.15.0
2025-10-17 01:25:56 +00:00
github-actions[bot]
6734de8389
bump: version 0.14.2 → 0.14.3
2025-10-17 00:04:25 +00:00
renovate-bot-cbcoutinho[bot]
16b9123af3
fix(deps): update dependency mcp to >=1.18,<1.19
2025-10-16 19:20:47 +00:00
github-actions[bot]
e0a68d47a5
bump: version 0.14.1 → 0.14.2
2025-10-16 08:32:29 +00:00
renovate-bot-cbcoutinho[bot]
7b2002c1b5
fix(deps): update dependency pillow to v12
2025-10-15 22:09:01 +00:00
github-actions[bot]
9e4c20a4b1
bump: version 0.14.0 → 0.14.1
2025-10-15 15:26:35 +00:00
Chris Coutinho
3ad9198f36
fix(oauth): Remove the option to force_register new clients
2025-10-15 16:27:22 +02:00
github-actions[bot]
46deb0f726
bump: version 0.13.0 → 0.14.0
2025-10-15 09:53:45 +00:00