test: Add patch for user_oidc app and update docs

This commit is contained in:
Chris Coutinho
2025-10-14 01:23:31 +02:00
parent 7d8ba39434
commit 17979accb6
5 changed files with 300 additions and 7 deletions
+97
View File
@@ -0,0 +1,97 @@
# Root Cause Analysis: OAuth2 Bearer Token Session Invalidation
## Problem
Bearer token authentication fails for app-specific APIs (like Notes) with 401 Unauthorized, even though it works for OCS APIs (capabilities).
## Root Cause
The CORSMiddleware in Nextcloud server is logging out the session created by Bearer token authentication:
```
/home/chris/Software/server/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php:84
$this->session->logout();
```
### Why Session is Logged Out
1. Notes API has @CORS annotation
2. Bearer auth via user_oidc creates a logged-in session
3. Request has NO CSRF token
4. Request has NO AppAPI auth flag
5. Request has NO PHP_AUTH_USER/PHP_AUTH_PW (basic auth)
6. Therefore CORSMiddleware calls logout()
### Log Evidence
```
{"message":"[TokenInvalidatedListener] Could not find the OIDC session related with an invalidated token"}
```
Token validated successfully, then immediately invalidated by session logout.
## Token Type Investigation (Opaque vs JWT)
- **Finding**: Token type (opaque vs JWT) does NOT affect the issue
- **Reason**: Session invalidation happens AFTER successful token validation
- Both opaque and JWT tokens validate correctly via TokenValidationRequestEvent
- The logout happens in CORSMiddleware, not in token validation
## ✅ SOLUTION (Tested & Working)
### Option A: Set AppAPI Flag for Bearer Auth ✅
**Status**: Successfully tested and verified working
Modified user_oidc `Backend.php` `getCurrentUserId()` method to set the `app_api` session flag before returning the user ID:
```php
$this->session->set('app_api', true);
```
This bypasses CORS middleware's logout logic at line 81-82 by setting the same flag used by Nextcloud's AppAPI framework.
### Implementation
The flag is added before all successful Bearer token authentication return statements in `/var/www/html/custom_apps/user_oidc/lib/User/Backend.php`:
- Line ~243: After OIDC provider validation
- Line ~310: After auto-provisioning with bearer provisioning
- Line ~315: After existing user authentication
- Line ~337: After LDAP user sync
### Test Results
All OAuth Bearer token operations now work correctly:
**Capabilities endpoint** (OCS API) - 200 OK
**Notes API listing** - 200 OK
**Notes API create** - 200 OK (created note 112)
**Notes API delete** - 200 OK (deleted note 112)
No session invalidation occurs, and all API operations complete successfully.
### Patch File
See `patches/user_oidc-bearer-auth-app-api-flag.patch` for the exact changes.
## Alternative Solutions (Not Tested)
### Option B: Avoid Creating Full Session for Bearer Auth
Bearer token auth should not create a full session that triggers CORS middleware checks. This would require deeper architectural changes.
### Option C: Add CSRF Exemption
Modify CORSMiddleware to exempt Bearer token authenticated requests from CSRF check. This would require changes to Nextcloud core.
### Option D: Use Basic Auth Headers
Set PHP_AUTH_USER/PHP_AUTH_PW server variables during Bearer auth so CORSMiddleware can re-authenticate. This could have security implications.
## Recommendations
### Short-term (Current Implementation)
The `app_api` flag solution works correctly and follows Nextcloud's existing pattern for API authentication. This is the recommended approach for immediate use.
### Long-term (Upstream Contribution)
Consider submitting this fix to the upstream user_oidc project as it enables proper Bearer token authentication for all Nextcloud APIs, not just OCS endpoints.
## Files Involved
- `/home/chris/Software/user_oidc/lib/User/Backend.php` (getCurrentUserId) - **MODIFIED**
- `/home/chris/Software/server/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php` (logout logic)
- `/home/chris/Software/user_oidc/lib/Listener/TokenInvalidatedListener.php` (cleanup handler)
## Testing
Run the OAuth interactive test to verify:
```bash
uv run pytest tests/integration/test_oauth_interactive.py -v
```
+96
View File
@@ -0,0 +1,96 @@
# Fix Bearer Token Authentication Causing Session Logout
## Problem
Bearer token authentication with OIDC fails for app-specific APIs (like Notes, Calendar, etc.) with `401 Unauthorized` errors, even though the same Bearer token works fine for OCS APIs (like `/ocs/v2.php/cloud/capabilities`).
### Root Cause
When using Bearer token authentication:
1. ✅ Bearer token validation successfully authenticates the user
2. ✅ A session is created for the authenticated user
3.**Nextcloud's `CORSMiddleware` detects the logged-in session but no CSRF token**
4.**`CORSMiddleware` calls `$this->session->logout()` to prevent CSRF attacks**
5. ❌ The logout invalidates the session, breaking the API request with 401 Unauthorized
This occurs because app-specific APIs (Notes, Calendar, etc.) use the `@CORS` annotation, which triggers the `CORSMiddleware` security checks. The OCS APIs don't have this annotation, which is why they work correctly.
### Error Logs
```
[TokenInvalidatedListener] Could not find the OIDC session related with an invalidated token
Session token invalidated before logout
Logging out
```
## Solution
Set the `app_api` session flag during Bearer token authentication. This instructs `CORSMiddleware` to skip the CSRF check and logout logic, as the authentication is API-based rather than session-based.
This is the same mechanism used by Nextcloud's [AppAPI framework](https://github.com/cloud-py-api/app_api) for external application authentication.
### Changes
The fix adds `$this->session->set('app_api', true);` before all successful Bearer token authentication return statements in `lib/User/Backend.php`:
- **Line 243**: After OIDC Identity Provider validation
- **Line 310**: After auto-provisioning with bearer provisioning
- **Line 315**: After existing user authentication
- **Line 337**: After LDAP user sync
## Testing
Tested with the [nextcloud-mcp-server](https://github.com/cccs-nik/nextcloud-mcp-server) project's integration tests:
### Before Fix
```
✅ Capabilities endpoint (OCS API) - 200 OK
❌ Notes API listing - 401 Unauthorized
❌ Notes API create - 401 Unauthorized
```
### After Fix
```
✅ Capabilities endpoint (OCS API) - 200 OK
✅ Notes API listing - 200 OK
✅ Notes API create - 200 OK
✅ Notes API delete - 200 OK
```
All OAuth Bearer token operations now work correctly across all Nextcloud APIs without session invalidation.
## Configuration
This fix works with the standard Bearer token validation configuration:
```php
// config.php
'user_oidc' => [
'oidc_provider_bearer_validation' => true,
],
```
And in the OIDC Identity Provider app:
```bash
php occ config:app:set oidc dynamic_client_registration --value='true'
```
## Impact
This fix enables proper Bearer token authentication for:
- All Nextcloud app APIs (Notes, Calendar, Contacts, etc.)
- External applications using OAuth 2.0 / OpenID Connect
- MCP servers and other API integrations
- Any application using the `Authorization: Bearer` header
## Related Files
- `lib/User/Backend.php` - Modified to set `app_api` flag
- `/server/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php` - Contains the CSRF/logout logic that this bypasses
## References
- [Nextcloud CORS Middleware](https://github.com/nextcloud/server/blob/master/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php)
- [Nextcloud AppAPI](https://github.com/cloud-py-api/app_api)
- [OpenID Connect Bearer Token Usage](https://openid.net/specs/openid-connect-core-1_0.html#TokenUsage)