This PR fixes multiple OAuth-related issues:
## Unified OAuth Callback
- Consolidated `/oauth/callback-nextcloud` and `/oauth/login-callback` into single `/oauth/callback` endpoint
- Flow type determined by session lookup via state parameter (no query params in redirect_uri)
- Fixes redirect_uri validation issues with IdPs requiring exact match
- Legacy endpoints kept as aliases for backwards compatibility
## PKCE Implementation
- Implemented PKCE (RFC 7636) for Flow 2 (resource provisioning)
- Generate code_verifier and code_challenge
- Store code_verifier in session storage
- Retrieve and use in token exchange
- Fixed PKCE for browser login (integrated mode)
- Previously only worked for external IdP (Keycloak)
- Now works for both Nextcloud OIDC and external IdP
## Login Elicitation Fixes (ADR-006)
- Fixed elicitation URL to route through MCP server endpoint
- Changed from direct Nextcloud URL to `/oauth/authorize-nextcloud`
- Ensures PKCE is properly handled by server
- Fixed login detection after OAuth flow completes
- Look up refresh token by state parameter instead of user_id
- Works even when Flow 1 token not present
- Added `get_refresh_token_by_provisioning_client_id()` method
## Session Authentication
- Fixed `/user/page` redirect loop
- Shared oauth_context with mounted browser_app
- SessionAuthBackend can now validate sessions correctly
## Tests
- Added comprehensive login elicitation test suite
- Updated scope authorization test expectations
- All 43 OAuth tests passing
## Files Changed
- `app.py`: Shared oauth_context, unified callback route
- `oauth_routes.py`: Unified callback, PKCE for Flow 2
- `browser_oauth_routes.py`: PKCE for integrated mode
- `oauth_tools.py`: Fixed elicitation URL generation
- `refresh_token_storage.py`: Added lookup by provisioning_client_id
- `test_login_elicitation.py`: New test suite
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements /user and /user/page endpoints for displaying authenticated
user information in both BasicAuth and OAuth modes.
Key Features:
- Separate browser OAuth flow (/oauth/login, /oauth/login-callback, /oauth/logout)
- Session-based authentication using signed cookies
- Token refresh for persistent sessions
- HTML and JSON user info endpoints
- IdP profile information retrieval
Architecture:
- BasicAuth mode: Always authenticated as configured user
- OAuth mode: Browser-based authorization code flow with refresh tokens
- Session stored in SQLite with encrypted refresh tokens
- Server-side token refresh using internal Docker hostnames
OAuth Flow:
- /oauth/login: Initiates browser OAuth flow
- /oauth/login-callback: Handles IdP callback and stores refresh token
- /oauth/logout: Clears session cookie
- /user: JSON API endpoint (requires authentication)
- /user/page: HTML page endpoint (requires authentication)
DCR Scopes Fix:
- MCP server DCR now only requests basic OIDC scopes (openid profile email offline_access)
- Nextcloud app scopes (notes:read, etc.) are for MCP clients, not the server itself
- PRM endpoint dynamically advertises supported scopes from tool decorators
Files:
- nextcloud_mcp_server/auth/browser_oauth_routes.py: Browser OAuth flow handlers
- nextcloud_mcp_server/auth/session_backend.py: Starlette session authentication
- nextcloud_mcp_server/auth/userinfo_routes.py: User info endpoints with token refresh
- tests/server/auth/test_userinfo_routes.py: Unit tests
- tests/server/oauth/test_userinfo_integration.py: OAuth integration tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>