Files
mcp-nextcloud/docs/oauth-troubleshooting.md
T
Chris CoutinhoandClaude Opus 4.6 29fd0486c9 refactor: change OAuth scope separator from colon to dot for IDP compatibility
Many identity providers (AWS Cognito, Okta, Azure AD) reject or mishandle
colons in OAuth scope names. This migrates all custom scopes from
`resource:action` to `resource.action` format (e.g., `notes:read` →
`notes.read`), which is universally accepted and aligns with industry
conventions (Microsoft, Google).

Includes Alembic migration 004 for stored scope strings and ADR-024
documenting the rationale and RFC references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:07:02 +02:00

643 lines
16 KiB
Markdown

# OAuth Troubleshooting
This guide covers OAuth-specific issues and solutions for the Nextcloud MCP server.
For general troubleshooting, see [Troubleshooting Guide](troubleshooting.md).
## Quick Diagnosis
Start here to identify your issue:
| Symptom | Likely Cause | Quick Fix Link |
|---------|--------------|----------------|
| "OAuth mode requires NEXTCLOUD_HOST" | Missing environment variable | [Missing NEXTCLOUD_HOST](#missing-nextcloud_host) |
| "OAuth mode requires client credentials OR dynamic registration" | OIDC apps not configured | [Missing OIDC Apps](#missing-or-misconfigured-oidc-apps) |
| "PKCE support validation failed" | OIDC app doesn't advertise PKCE | [PKCE Not Advertised](#pkce-not-advertised) |
| "Stored client has expired" | Dynamic client expired | [Client Expired](#client-expired) |
| Only seeing Notes tools (7 instead of 90+) | Limited OAuth scopes granted | [Limited Scopes](#limited-scopes---only-seeing-notes-tools) |
| HTTP 401 for Notes API | Bearer token patch missing | [Bearer Token Auth Fails](#bearer-token-authentication-fails) |
| "OIDC discovery failed" | Network or configuration issue | [Discovery Failed](#oidc-discovery-failed) |
| "Database error" on OAuth client storage | Database permissions issue | [Database Permission Error](#database-permission-error) |
## Configuration Issues
### Missing NEXTCLOUD_HOST
**Error Message**:
```
OAuth mode requires NEXTCLOUD_HOST environment variable
```
**Cause**: The `NEXTCLOUD_HOST` environment variable is not set or empty.
**Solution**:
1. Add to your `.env` file:
```bash
NEXTCLOUD_HOST=https://your.nextcloud.instance.com
```
2. Reload environment variables:
```bash
export $(grep -v '^#' .env | xargs)
```
3. Verify it's set:
```bash
echo $NEXTCLOUD_HOST
# Should output: https://your.nextcloud.instance.com
```
---
### Missing or Misconfigured OIDC Apps
**Error Message**:
```
OAuth mode requires either client credentials OR dynamic client registration
```
**Cause**: The required Nextcloud OIDC apps are either:
- Not installed
- Not enabled
- Missing configuration
**Solution**:
**Step 1**: Verify both apps are installed:
```bash
# Check installed apps
php occ app:list | grep -E "oidc|user_oidc"
# Should show:
# - oidc: enabled
# - user_oidc: enabled
```
If not installed:
1. Open Nextcloud as administrator
2. Navigate to **Apps** → **Security**
3. Install **"OIDC"** (OIDC Identity Provider)
4. Install **"OpenID Connect user backend"** (user_oidc)
5. Enable both apps
**Step 2**: Enable dynamic client registration:
1. Go to **Settings** → **OIDC** (Administration)
2. Enable **"Allow dynamic client registration"**
**Step 3**: Configure Bearer token validation:
```bash
php occ config:system:set user_oidc oidc_provider_bearer_validation --value=true --type=boolean
```
**Step 4**: Verify discovery endpoint:
```bash
curl https://your.nextcloud.instance.com/.well-known/openid-configuration | jq '.registration_endpoint'
# Should output:
# "https://your.nextcloud.instance.com/apps/oidc/register"
```
**Alternative**: Use pre-configured client credentials:
```bash
# Register client via CLI
php occ oidc:create \
--name="Nextcloud MCP Server" \
--type=confidential \
--redirect-uri="http://localhost:8000/oauth/callback"
# Add to .env
echo "NEXTCLOUD_OIDC_CLIENT_ID=<client-id>" >> .env
echo "NEXTCLOUD_OIDC_CLIENT_SECRET=<client-secret>" >> .env
```
---
### Client Expired
**Error Message**:
```
Stored client has expired
```
**Cause**: Dynamically registered OAuth clients expire (default: 1 hour).
**Solution**:
**Option 1: Restart the Server** (Automatic re-registration)
```bash
uv run nextcloud-mcp-server --oauth
# Server automatically re-registers if credentials expired
```
**Option 2: Use Pre-configured Credentials** (Recommended for production)
```bash
# Register permanent client via Nextcloud CLI
php occ oidc:create \
--name="Nextcloud MCP Server" \
--type=confidential \
--redirect-uri="http://localhost:8000/oauth/callback"
# Add to .env
NEXTCLOUD_OIDC_CLIENT_ID=<from-output>
NEXTCLOUD_OIDC_CLIENT_SECRET=<from-output>
```
Pre-configured clients don't expire.
**Option 3: Increase Expiration Time**
```bash
# Via Nextcloud CLI (default: 3600 seconds = 1 hour)
php occ config:app:set oidc expire_time --value "86400" # 24 hours
```
---
### Database Permission Error
**Error Message**:
```
Permission denied when accessing SQLite database
Database is locked
```
**Cause**: The server cannot access the SQLite database file.
**Solution**:
```bash
# Check database directory permissions
ls -la /app/data/
# Ensure directory is writable
chmod 755 /app/data
# Check if database file exists and has correct permissions
ls -la /app/data/tokens.db
chmod 644 /app/data/tokens.db
# If running in Docker, ensure volume is mounted correctly
docker compose logs mcp-oauth | grep -i "database\|sqlite"
```
**For Docker deployments**:
Ensure the data directory is properly mounted as a volume:
```yaml
volumes:
- ./data:/app/data # Persistent storage for SQLite database
```
---
## Discovery and Connection Issues
### OIDC Discovery Failed
**Error Message**:
```
OIDC discovery failed
Cannot reach OIDC discovery endpoint
```
**Cause**: The server cannot reach the Nextcloud OIDC discovery endpoint.
**Solution**:
**Step 1**: Verify Nextcloud URL is correct:
```bash
echo $NEXTCLOUD_HOST
# Should be full URL: https://your.nextcloud.instance.com
```
**Step 2**: Test discovery endpoint manually:
```bash
curl https://your.nextcloud.instance.com/.well-known/openid-configuration
# Should return JSON with OIDC configuration
# {
# "issuer": "https://your.nextcloud.instance.com",
# "authorization_endpoint": "https://your.nextcloud.instance.com/apps/oidc/authorize",
# ...
# }
```
**Step 3**: Check network connectivity:
```bash
# Test basic connectivity
ping your.nextcloud.instance.com
# Test HTTPS
curl -I https://your.nextcloud.instance.com
```
**Step 4**: Verify both OIDC apps are enabled:
```bash
php occ app:list | grep -E "oidc|user_oidc"
```
**Step 5**: Check firewall rules (if using Docker):
```bash
# Check if MCP server can reach Nextcloud
docker exec nextcloud-mcp-server curl https://your.nextcloud.instance.com/.well-known/openid-configuration
```
---
## Authentication Issues
### Bearer Token Authentication Fails
**Error Message**:
```
HTTP 401 Unauthorized when calling Nextcloud APIs
```
**Symptoms**:
- OCS APIs work (`/ocs/v2.php/cloud/capabilities`)
- App APIs fail (`/apps/notes/api/`, `/apps/calendar/`, etc.)
**Cause**: The `user_oidc` app's CORS middleware interferes with Bearer token authentication for non-OCS endpoints.
**Solution**: Apply the Bearer token patch to `user_oidc` app.
See [Upstream Status](oauth-upstream-status.md#1-bearer-token-support-for-non-ocs-endpoints) for details.
**Quick Patch**:
```bash
# SSH into Nextcloud server
cd /path/to/nextcloud/apps/user_oidc
# Edit lib/User/Backend.php
# Add this line before each return statement in getCurrentUserId() method:
$this->session->set('app_api', true);
# Lines to modify: ~243, ~310, ~315, ~337
```
**Test the fix**:
```bash
# Get an OAuth token (from MCP client or test)
TOKEN="your_access_token"
# Test Notes API
curl -H "Authorization: Bearer $TOKEN" \
https://your.nextcloud.instance.com/apps/notes/api/v1/notes
# Should return notes JSON (not 401)
```
---
### PKCE Not Advertised
**Error Message**:
```
ERROR: OIDC CONFIGURATION ERROR - Missing PKCE Support Advertisement
⚠️ MCP clients (like Claude Code) WILL REJECT this provider!
```
**Cause**: The OIDC discovery endpoint doesn't include `code_challenge_methods_supported` field.
**Impact**:
- Some MCP clients may refuse to connect
- Standards compliance issue (RFC 8414)
- **Functionality still works** (PKCE is accepted, just not advertised)
**Solution**:
**Short-term**: The MCP server logs a warning but continues. OAuth flow still works.
**Long-term**: Update the `oidc` app to advertise PKCE support.
See [Upstream Status](oauth-upstream-status.md#2-pkce-support-advertisement-in-discovery) for tracking.
**Verify**:
```bash
curl https://your.nextcloud.instance.com/.well-known/openid-configuration | jq '.code_challenge_methods_supported'
# Should return:
# ["S256", "plain"]
# If null, PKCE isn't advertised (but still works)
```
---
## Runtime Issues
### MCP Client Can't Authenticate
**Symptoms**:
- Client connects but OAuth flow fails
- Authorization redirects don't work
- Token exchange fails
**Diagnosis**:
**Step 1**: Verify OAuth is configured correctly:
```bash
uv run nextcloud-mcp-server --oauth --log-level debug
```
Look for:
```
INFO OAuth initialization complete
INFO MCP server ready at http://127.0.0.1:8000
```
**Step 2**: Check OIDC discovery:
```bash
curl https://your.nextcloud.instance.com/.well-known/openid-configuration
```
**Step 3**: Verify MCP server URL matches client expectations:
```bash
echo $NEXTCLOUD_MCP_SERVER_URL
# Should match the URL clients use to connect
# Default: http://localhost:8000
```
If MCP server is on a different host/port, update:
```bash
NEXTCLOUD_MCP_SERVER_URL=http://actual-host:actual-port
```
**Step 4**: Check redirect URI configuration:
For pre-configured clients, ensure redirect URI matches:
```bash
# Client redirect URI should be:
http://your-mcp-server-url/oauth/callback
# Example for local server:
http://localhost:8000/oauth/callback
```
---
### Tools Return 401 Errors
**Symptoms**:
- OAuth flow completes successfully
- Token is valid
- MCP tools return 401 errors
**Cause**: Bearer token not working with Nextcloud APIs.
**Solution**: See [Bearer Token Authentication Fails](#bearer-token-authentication-fails) above.
---
### Limited Scopes - Only Seeing Notes Tools
**Symptoms**:
- MCP client (e.g., Claude Code) successfully connects via OAuth
- Only Notes tools are available (7 tools instead of 90+)
- Token scopes show only `mcp:notes.read` and `mcp:notes.write`
**Cause**: During the OAuth consent flow, the user only granted access to Notes scopes, or the client only requested those scopes.
**Diagnosis**:
Check what scopes the client has been granted:
```bash
# View registered clients and their allowed scopes
php occ oidc:list | jq '.[] | select(.name | contains("Claude Code")) | {name, allowed_scopes}'
```
Look for the client's `allowed_scopes` field. If it's empty or only contains notes scopes, that's the issue.
**Solution**:
**Option 1: Delete Client and Reconnect** (Recommended for MCP clients)
```bash
# Find the client ID
php occ oidc:list | jq '.[] | select(.name | contains("Claude Code")) | {name, client_id}'
# Delete the client
php occ oidc:delete <client_id>
# Reconnect from Claude Code
# This will trigger a new OAuth flow where you can grant all scopes
```
When reconnecting, you'll see a consent screen listing all available scopes. Make sure to approve all the scopes you want the client to access.
**Option 2: Update Client Scopes via CLI**
```bash
# Update allowed scopes for an existing client
php occ oidc:update <client_id> \
--allowed-scopes "openid profile email mcp:notes.read mcp:notes.write mcp:calendar.read mcp:calendar.write mcp:contacts.read mcp:contacts.write mcp:cookbook.read mcp:cookbook.write mcp:deck.read mcp:deck.write mcp:tables.read mcp:tables.write mcp:files.read mcp:files.write mcp:sharing.read mcp:sharing.write"
# User will need to reconnect to get new token with updated scopes
```
**Verify Available Scopes**:
Check what scopes the MCP server advertises:
```bash
curl http://localhost:8001/.well-known/oauth-protected-resource | jq '.scopes_supported'
# Should show all 16 scope categories:
# - openid
# - mcp:notes.read, mcp:notes.write
# - mcp:calendar.read, mcp:calendar.write
# - mcp:contacts.read, mcp:contacts.write
# - mcp:cookbook.read, mcp:cookbook.write
# - mcp:deck.read, mcp:deck.write
# - mcp:tables.read, mcp:tables.write
# - mcp:files.read, mcp:files.write
# - mcp:sharing.read, mcp:sharing.write
```
**Understanding Scope Filtering**:
The MCP server dynamically filters tools based on the scopes in your access token:
- Check server logs for: `✂️ JWT scope filtering: X/90 tools available for scopes: {...}`
- This shows how many tools are visible vs total available
- Each tool requires specific scopes (read and/or write)
**Available Scope Categories**:
| Scope Prefix | Nextcloud App | Read Operations | Write Operations |
|--------------|---------------|-----------------|------------------|
| `mcp:notes:*` | Notes | Get, search, list | Create, update, delete, append |
| `mcp:calendar:*` | Calendar (CalDAV) | Get events, todos, calendars | Create/update/delete events, todos |
| `mcp:contacts:*` | Contacts (CardDAV) | Get contacts, address books | Create/update/delete contacts |
| `mcp:cookbook:*` | Cookbook | Get recipes, search | Create/update recipes |
| `mcp:deck:*` | Deck | Get boards, cards | Create/update boards, cards |
| `mcp:tables:*` | Tables | Get rows, tables | Create/update/delete rows |
| `mcp:files:*` | Files (WebDAV) | List, read files | Upload, delete, move files |
| `mcp:sharing:*` | Sharing | Get shares | Create/update shares |
---
## Switching Authentication Modes
### From BasicAuth to OAuth
```bash
# 1. Remove or comment out USERNAME/PASSWORD in .env
sed -i 's/^NEXTCLOUD_USERNAME/#NEXTCLOUD_USERNAME/' .env
sed -i 's/^NEXTCLOUD_PASSWORD/#NEXTCLOUD_PASSWORD/' .env
# 2. Ensure NEXTCLOUD_HOST is set
grep NEXTCLOUD_HOST .env
# 3. Restart server with OAuth
export $(grep -v '^#' .env | xargs)
uv run nextcloud-mcp-server --oauth
```
### From OAuth to BasicAuth
```bash
# 1. Add USERNAME/PASSWORD to .env
echo "NEXTCLOUD_USERNAME=your-username" >> .env
echo "NEXTCLOUD_PASSWORD=your-password" >> .env
# 2. Restart server (BasicAuth auto-detected)
export $(grep -v '^#' .env | xargs)
uv run nextcloud-mcp-server --no-oauth
```
---
## Advanced Debugging
### Enable Debug Logging
```bash
uv run nextcloud-mcp-server --oauth --log-level debug
```
Look for:
- OIDC discovery details
- Client registration attempts
- Token validation logs
- API request/response details
### Test Discovery Endpoint
```bash
# Full discovery response
curl https://your.nextcloud.instance.com/.well-known/openid-configuration | jq
# Check specific fields
curl https://your.nextcloud.instance.com/.well-known/openid-configuration | jq '{
issuer,
authorization_endpoint,
token_endpoint,
userinfo_endpoint,
registration_endpoint,
code_challenge_methods_supported
}'
```
### Test Token Validation
```bash
# Get userinfo with token
curl -H "Authorization: Bearer $TOKEN" \
https://your.nextcloud.instance.com/apps/oidc/userinfo
# Should return user info:
# {
# "sub": "username",
# "preferred_username": "username",
# "name": "Display Name",
# ...
# }
```
### Test Nextcloud API Access
```bash
# Test OCS API (should work)
curl -H "Authorization: Bearer $TOKEN" \
"$NEXTCLOUD_HOST/ocs/v2.php/cloud/capabilities?format=json" \
-H "OCS-APIRequest: true"
# Test app API (requires patch)
curl -H "Authorization: Bearer $TOKEN" \
"$NEXTCLOUD_HOST/apps/notes/api/v1/notes"
```
---
## Getting Help
If you continue to experience issues:
### 1. Collect Diagnostic Information
```bash
# Server version
uv run nextcloud-mcp-server --version
# Python version
python3 --version
# Server logs with debug
uv run nextcloud-mcp-server --oauth --log-level debug 2>&1 | tee mcp-server.log
# OIDC discovery
curl https://your.nextcloud.instance.com/.well-known/openid-configuration > oidc-discovery.json
# Nextcloud version
# Check in Nextcloud admin panel or:
php occ -V
```
### 2. Check Documentation
- [OAuth Architecture](oauth-architecture.md) - How OAuth works
- [OAuth Setup Guide](oauth-setup.md) - Configuration steps
- [Upstream Status](oauth-upstream-status.md) - Required patches
- [Configuration](configuration.md) - Environment variables
### 3. Open an Issue
If problems persist, [open an issue](https://github.com/cbcoutinho/nextcloud-mcp-server/issues) with:
- **Error messages** (full text)
- **Server logs** (with `--log-level debug`)
- **OIDC discovery response** (from curl command above)
- **Nextcloud version**
- **OIDC app versions** (`oidc` and `user_oidc`)
- **Steps to reproduce**
- **Environment details** (OS, Python version, Docker vs local)
---
## See Also
- [OAuth Quick Start](quickstart-oauth.md) - Get started quickly
- [OAuth Setup Guide](oauth-setup.md) - Detailed configuration
- [OAuth Architecture](oauth-architecture.md) - Technical details
- [Upstream Status](oauth-upstream-status.md) - Required patches
- [General Troubleshooting](troubleshooting.md) - Non-OAuth issues