docs: Update jwt docs [skip ci]
This commit is contained in:
@@ -1,100 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== JWT OAuth Client Setup ==="
|
||||
echo "Installing and configuring OIDC app for JWT tokens..."
|
||||
|
||||
# Wait for Nextcloud to be fully initialized
|
||||
sleep 5
|
||||
|
||||
# Install OIDC app if not already installed
|
||||
if ! php /var/www/html/occ app:list | grep -q "oidc"; then
|
||||
echo "Installing OIDC app..."
|
||||
php /var/www/html/occ app:install oidc
|
||||
else
|
||||
echo "OIDC app already installed"
|
||||
fi
|
||||
|
||||
# Enable the app
|
||||
php /var/www/html/occ app:enable oidc
|
||||
|
||||
# Check if JWT client already exists
|
||||
# Use a location that www-data user owns
|
||||
CLIENT_DIR="/var/www/html/.oauth-jwt"
|
||||
CLIENT_FILE="$CLIENT_DIR/nextcloud_oauth_client.json"
|
||||
|
||||
if [ -f "$CLIENT_FILE" ]; then
|
||||
echo "JWT OAuth client already exists at $CLIENT_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create directory owned by www-data
|
||||
mkdir -p "$CLIENT_DIR"
|
||||
|
||||
# Create JWT OAuth client with proper scopes
|
||||
echo "Creating JWT OAuth client..."
|
||||
|
||||
# The redirect URI for the MCP server
|
||||
REDIRECT_URI="http://127.0.0.1:8002/oauth/callback"
|
||||
|
||||
# Create the client with JWT token type
|
||||
OUTPUT=$(php /var/www/html/occ oidc:create \
|
||||
--token_type=jwt \
|
||||
--allowed_scopes="openid profile email nc:read nc:write" \
|
||||
"Nextcloud MCP Server JWT" \
|
||||
"$REDIRECT_URI")
|
||||
|
||||
echo "Client creation output:"
|
||||
echo "$OUTPUT"
|
||||
|
||||
# Parse the JSON output to extract client_id, client_secret, and issued_at
|
||||
# Output format is JSON
|
||||
CLIENT_ID=$(echo "$OUTPUT" | grep '"client_id"' | sed 's/.*"client_id": "\([^"]*\)".*/\1/')
|
||||
CLIENT_SECRET=$(echo "$OUTPUT" | grep '"client_secret"' | sed 's/.*"client_secret": "\([^"]*\)".*/\1/')
|
||||
ISSUED_AT=$(echo "$OUTPUT" | grep '"issued_at"' | sed 's/.*"issued_at": \([0-9]*\).*/\1/')
|
||||
|
||||
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
|
||||
echo "ERROR: Failed to parse client credentials from output"
|
||||
echo "Output was: $OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use issued_at if available, otherwise use current timestamp
|
||||
if [ -z "$ISSUED_AT" ]; then
|
||||
ISSUED_AT=$(date +%s)
|
||||
fi
|
||||
|
||||
# Set expiration to 10 years in the future (JWT clients don't expire like DCR clients)
|
||||
EXPIRES_AT=$((ISSUED_AT + 315360000))
|
||||
|
||||
echo "Successfully created JWT client:"
|
||||
echo " Client ID: ${CLIENT_ID:0:16}..."
|
||||
echo " Client Secret: [hidden]"
|
||||
|
||||
# Create the credentials file in the format expected by the MCP server
|
||||
# This matches the format from ClientInfo.to_dict() in client_registration.py
|
||||
cat > "$CLIENT_FILE" << EOF
|
||||
{
|
||||
"client_id": "$CLIENT_ID",
|
||||
"client_secret": "$CLIENT_SECRET",
|
||||
"client_id_issued_at": $ISSUED_AT,
|
||||
"client_secret_expires_at": $EXPIRES_AT,
|
||||
"redirect_uris": ["$REDIRECT_URI"]
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Credentials saved to $CLIENT_FILE"
|
||||
|
||||
# Also save to environment variable format for easy access
|
||||
cat > "$CLIENT_DIR/client_env.sh" << EOF
|
||||
export NEXTCLOUD_OIDC_CLIENT_ID="$CLIENT_ID"
|
||||
export NEXTCLOUD_OIDC_CLIENT_SECRET="$CLIENT_SECRET"
|
||||
export NEXTCLOUD_OIDC_SCOPES="openid profile email nc:read nc:write"
|
||||
EOF
|
||||
|
||||
chmod 600 "$CLIENT_DIR/client_env.sh"
|
||||
|
||||
echo "=== JWT OAuth Client Setup Complete ==="
|
||||
echo "Client credentials are available at:"
|
||||
echo " JSON: $CLIENT_FILE"
|
||||
echo " ENV: $CLIENT_DIR/client_env.sh"
|
||||
+204
-66
@@ -27,13 +27,12 @@ The Nextcloud MCP Server supports OAuth authentication with both **JWT** (RFC 90
|
||||
|
||||
### Key Features
|
||||
|
||||
✅ **JWT Token Support** - RFC 9068 compliant access tokens with RS256 signatures
|
||||
✅ **Custom Scopes** - `nc:read` and `nc:write` for read/write access control
|
||||
✅ **Dynamic Tool Filtering** - Tools filtered based on user's token scopes
|
||||
✅ **Automatic Client Creation** - JWT OAuth clients auto-generated on container startup
|
||||
✅ **Scope Challenges** - RFC-compliant `WWW-Authenticate` headers for insufficient scopes
|
||||
✅ **Protected Resource Metadata** - RFC 8959 endpoint for scope discovery
|
||||
✅ **Backward Compatible** - BasicAuth mode bypasses all scope checks
|
||||
- ✅ **JWT Token Support** - RFC 9068 compliant access tokens with RS256 signatures
|
||||
- ✅ **Custom Scopes** - `nc:read` and `nc:write` for read/write access control
|
||||
- ✅ **Dynamic Tool Filtering** - Tools filtered based on user's token scopes
|
||||
- ✅ **Scope Challenges** - RFC-compliant `WWW-Authenticate` headers for insufficient scopes
|
||||
- ✅ **Protected Resource Metadata** - RFC 8959 endpoint for scope discovery
|
||||
- ✅ **Backward Compatible** - BasicAuth mode bypasses all scope checks
|
||||
|
||||
### Supported Scopes
|
||||
|
||||
@@ -88,14 +87,23 @@ The Nextcloud OIDC app supports two token formats, configured per-client:
|
||||
**Advantages:**
|
||||
- ✅ Smaller size (72 characters)
|
||||
- ✅ No payload visible to client
|
||||
- ✅ Direct scope access via introspection endpoint (RFC 7662)
|
||||
|
||||
**Disadvantages:**
|
||||
- ❌ Requires userinfo endpoint call for each validation
|
||||
- ❌ Scopes must be inferred from userinfo response (not directly available)
|
||||
- ❌ Higher latency (HTTP call required)
|
||||
- ❌ Higher latency - Requires HTTP call to introspection endpoint
|
||||
- ❌ Slower than JWT signature verification (network roundtrip)
|
||||
|
||||
**Validation Method:**
|
||||
Opaque tokens are validated using the **introspection endpoint** (`/apps/oidc/introspect`), which returns:
|
||||
- Token active status
|
||||
- Scope claim (direct access, no inference needed)
|
||||
- User information (`sub`, `username`)
|
||||
- Token metadata (`exp`, `iat`, `client_id`)
|
||||
|
||||
Falls back to userinfo endpoint only if introspection is unavailable.
|
||||
|
||||
**When to Use:**
|
||||
- Use **JWT tokens** for production (better performance, direct scope access)
|
||||
- Use **JWT tokens** for production (better performance, no HTTP call)
|
||||
- Use **opaque tokens** for compatibility with clients that don't support JWT
|
||||
|
||||
---
|
||||
@@ -228,8 +236,9 @@ The development environment includes three MCP server variants:
|
||||
|
||||
### JWT Service Configuration
|
||||
|
||||
The `mcp-oauth-jwt` service uses automatic client creation:
|
||||
The `mcp-oauth-jwt` service uses **Dynamic Client Registration (DCR)** by default:
|
||||
|
||||
**Default Configuration (DCR):**
|
||||
```yaml
|
||||
mcp-oauth-jwt:
|
||||
build: .
|
||||
@@ -240,59 +249,150 @@ mcp-oauth-jwt:
|
||||
- NEXTCLOUD_HOST=http://app:80
|
||||
- NEXTCLOUD_MCP_SERVER_URL=http://localhost:8002
|
||||
- NEXTCLOUD_PUBLIC_ISSUER_URL=http://localhost:8080
|
||||
- NEXTCLOUD_OIDC_CLIENT_STORAGE=/var/www/html/.oauth-jwt/nextcloud_oauth_client.json
|
||||
- NEXTCLOUD_OIDC_SCOPES=openid profile email nc:read nc:write
|
||||
- NEXTCLOUD_OIDC_TOKEN_TYPE=jwt
|
||||
volumes:
|
||||
- nextcloud:/var/www/html:ro
|
||||
- ./oauth-storage:/app/.oauth # Optional: persist DCR credentials
|
||||
```
|
||||
|
||||
**With Pre-Configured Credentials:**
|
||||
```yaml
|
||||
mcp-oauth-jwt:
|
||||
build: .
|
||||
command: ["--transport", "streamable-http", "--oauth", "--port", "8002"]
|
||||
ports:
|
||||
- 127.0.0.1:8002:8002
|
||||
environment:
|
||||
- NEXTCLOUD_HOST=http://app:80
|
||||
- NEXTCLOUD_MCP_SERVER_URL=http://localhost:8002
|
||||
- NEXTCLOUD_PUBLIC_ISSUER_URL=http://localhost:8080
|
||||
- NEXTCLOUD_OIDC_CLIENT_ID=<your_client_id> # Skips DCR
|
||||
- NEXTCLOUD_OIDC_CLIENT_SECRET=<your_client_secret> # Skips DCR
|
||||
- NEXTCLOUD_OIDC_TOKEN_TYPE=jwt
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- No `NEXTCLOUD_OIDC_CLIENT_ID` or `CLIENT_SECRET` needed (loaded from storage)
|
||||
- JWT client auto-created during Nextcloud initialization
|
||||
- Credentials stored in `/var/www/html/.oauth-jwt/nextcloud_oauth_client.json`
|
||||
- Volume mounted read-only for security
|
||||
- **No credentials needed** - DCR automatically registers the client on first start
|
||||
- **Credentials persist** - Saved to `.nextcloud_oauth_client.json` and reused
|
||||
- **JWT tokens** - Set `TOKEN_TYPE=jwt` for better performance
|
||||
- **Pre-configured credentials** - Providing `CLIENT_ID`/`CLIENT_SECRET` skips DCR
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `NEXTCLOUD_HOST` | Nextcloud base URL | `http://localhost:8080` |
|
||||
| `NEXTCLOUD_MCP_SERVER_URL` | MCP server external URL | (required) |
|
||||
| `NEXTCLOUD_MCP_SERVER_URL` | MCP server external URL for OAuth callbacks | (required in OAuth mode) |
|
||||
| `NEXTCLOUD_PUBLIC_ISSUER_URL` | Public issuer URL for JWT validation | (uses `NEXTCLOUD_HOST`) |
|
||||
| `NEXTCLOUD_OIDC_CLIENT_STORAGE` | Path to client credentials JSON | (optional - uses DCR if unset) |
|
||||
| `NEXTCLOUD_OIDC_CLIENT_ID` | Pre-configured OAuth client ID | (optional - uses DCR if unset) |
|
||||
| `NEXTCLOUD_OIDC_CLIENT_SECRET` | Pre-configured OAuth client secret | (optional - uses DCR if unset) |
|
||||
| `NEXTCLOUD_OIDC_CLIENT_STORAGE` | Path to persist DCR-registered credentials | `.nextcloud_oauth_client.json` |
|
||||
| `NEXTCLOUD_OIDC_SCOPES` | Space-separated scopes to request | `"openid profile email nc:read nc:write"` |
|
||||
| `NEXTCLOUD_OIDC_TOKEN_TYPE` | Token format: `"jwt"` or `"Bearer"` | `"Bearer"` |
|
||||
|
||||
### Dynamic Client Registration (DCR)
|
||||
|
||||
The MCP server supports **automatic OAuth client registration** using the OIDC Discovery registration endpoint. This eliminates the need for manual client creation in most cases.
|
||||
|
||||
**How It Works:**
|
||||
|
||||
When the MCP server starts in OAuth mode, it follows this **three-tier credential loading strategy**:
|
||||
|
||||
```
|
||||
1. Environment Variables (Highest Priority)
|
||||
├─ NEXTCLOUD_OIDC_CLIENT_ID
|
||||
└─ NEXTCLOUD_OIDC_CLIENT_SECRET
|
||||
|
||||
2. Storage File (Second Priority)
|
||||
└─ NEXTCLOUD_OIDC_CLIENT_STORAGE (.nextcloud_oauth_client.json)
|
||||
|
||||
3. Dynamic Client Registration (Automatic Fallback)
|
||||
├─ Discovers registration endpoint from /.well-known/openid-configuration
|
||||
├─ Registers new client with requested scopes and token type
|
||||
├─ Saves credentials to storage file for future use
|
||||
└─ Client credentials persist across restarts
|
||||
```
|
||||
|
||||
**Configuration:**
|
||||
|
||||
DCR automatically configures the client based on environment variables:
|
||||
|
||||
```bash
|
||||
# Minimal DCR configuration (no credentials needed!)
|
||||
export NEXTCLOUD_HOST=http://localhost:8080
|
||||
export NEXTCLOUD_MCP_SERVER_URL=http://localhost:8000
|
||||
export NEXTCLOUD_OIDC_SCOPES="openid profile email nc:read nc:write"
|
||||
export NEXTCLOUD_OIDC_TOKEN_TYPE=jwt # or "Bearer" for opaque tokens
|
||||
```
|
||||
|
||||
**Credential Storage:**
|
||||
|
||||
- Registered credentials are saved to `NEXTCLOUD_OIDC_CLIENT_STORAGE` (default: `.nextcloud_oauth_client.json`)
|
||||
- File has restrictive permissions (0600 - owner read/write only)
|
||||
- Credentials are reused on subsequent starts (no re-registration needed)
|
||||
- Storage file is checked for expiration (auto-regenerates if expired)
|
||||
|
||||
**Format:**
|
||||
```json
|
||||
{
|
||||
"client_id": "XBd2xqIisu3Kswg39Ub4BUhC36PEYjwwivx3G5nZdDgigvwKXrTHozs7m9DeoLSY",
|
||||
"client_secret": "xNKcy0qpUSau36T60pGGdb03pMEVLXtqykxjK8YkDpoNxNcZ4ClyAT3IAEse2AKT",
|
||||
"client_id_issued_at": 1761097039,
|
||||
"client_secret_expires_at": 2076457039,
|
||||
"redirect_uris": ["http://localhost:8000/oauth/callback"]
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ Zero-configuration OAuth setup
|
||||
- ✅ Automatic credential management
|
||||
- ✅ Supports both JWT and opaque tokens
|
||||
- ✅ Credentials persist across container restarts
|
||||
- ✅ Automatic re-registration if credentials expire
|
||||
- ✅ Properly sets `allowed_scopes` for JWT token validation
|
||||
|
||||
### Manual Client Creation
|
||||
|
||||
If not using automatic creation, create a JWT client manually:
|
||||
Manual client creation is **optional** but may be preferred when:
|
||||
- You want explicit control over client configuration
|
||||
- You're deploying to production environments with strict security policies
|
||||
- You need to pre-provision OAuth clients before deployment
|
||||
|
||||
**Create Client via OCC Command:**
|
||||
|
||||
```bash
|
||||
docker compose exec app php occ oidc:create \
|
||||
--token_type=jwt \
|
||||
--allowed_scopes="openid profile email nc:read nc:write" \
|
||||
"Nextcloud MCP Server" \
|
||||
"http://localhost:8002/oauth/callback"
|
||||
"http://localhost:8000/oauth/callback"
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"client_id": "...",
|
||||
"client_secret": "...",
|
||||
"client_id": "XBd2xqIisu3Kswg39Ub4BUhC36PEYjwwivx3G5nZdDgigvwKXrTHozs7m9DeoLSY",
|
||||
"client_secret": "xNKcy0qpUSau36T60pGGdb03pMEVLXtqykxjK8YkDpoNxNcZ4ClyAT3IAEse2AKT",
|
||||
"token_type": "jwt",
|
||||
"allowed_scopes": "openid profile email nc:read nc:write"
|
||||
}
|
||||
```
|
||||
|
||||
Then configure the MCP server:
|
||||
**Configure MCP Server with Pre-Configured Credentials:**
|
||||
|
||||
```bash
|
||||
# Option 1: Environment variables (highest priority)
|
||||
export NEXTCLOUD_OIDC_CLIENT_ID="<client_id>"
|
||||
export NEXTCLOUD_OIDC_CLIENT_SECRET="<client_secret>"
|
||||
export NEXTCLOUD_OIDC_TOKEN_TYPE="jwt"
|
||||
|
||||
# Option 2: Storage file (second priority)
|
||||
# Save the JSON response to .nextcloud_oauth_client.json
|
||||
# Server will automatically load it on startup
|
||||
```
|
||||
|
||||
When credentials are provided via environment variables or storage file, **DCR is skipped**.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
@@ -317,8 +417,8 @@ export NEXTCLOUD_OIDC_TOKEN_TYPE="jwt"
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ NextcloudTokenVerifier │ │
|
||||
│ │ - JWT signature verification (JWKS) │ │
|
||||
│ │ - Scope extraction from token payload │ │
|
||||
│ │ - Fallback to userinfo endpoint (opaque tokens) │ │
|
||||
│ │ - Introspection endpoint (opaque tokens) │ │
|
||||
│ │ - Userinfo fallback (last resort) │ │
|
||||
│ └───────────────────┬───────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ v │
|
||||
@@ -342,10 +442,13 @@ export NEXTCLOUD_OIDC_TOKEN_TYPE="jwt"
|
||||
### Key Components
|
||||
|
||||
**1. Token Verification** (`nextcloud_mcp_server/auth/token_verifier.py`)
|
||||
- JWT signature verification using JWKS (RS256)
|
||||
- Scope extraction from `scope` claim
|
||||
- Token caching with TTL
|
||||
- Fallback to userinfo endpoint for opaque tokens
|
||||
- **Three-tier validation strategy:**
|
||||
1. **JWT verification** (lines 116-124): JWKS signature validation for JWT tokens
|
||||
2. **Introspection** (lines 126-134): RFC 7662 endpoint for opaque tokens
|
||||
3. **Userinfo fallback** (lines 137-142): Last resort if introspection unavailable
|
||||
- Scope extraction from token payload (JWT) or introspection response (opaque)
|
||||
- Token caching with TTL to reduce repeated validations
|
||||
- Supports both access token formats transparently
|
||||
|
||||
**2. Scope Authorization** (`nextcloud_mcp_server/auth/scope_authorization.py`)
|
||||
- `@require_scopes()` decorator for tools
|
||||
@@ -369,28 +472,66 @@ export NEXTCLOUD_OIDC_TOKEN_TYPE="jwt"
|
||||
- Returns 403 with `WWW-Authenticate` header
|
||||
- Includes missing scopes and PRM endpoint URL
|
||||
|
||||
### Automatic Client Creation
|
||||
### Token Validation Flow
|
||||
|
||||
**Post-Installation Hook:** `app-hooks/post-installation/90-create-jwt-oauth-client.sh`
|
||||
The `NextcloudTokenVerifier` implements a **cascading validation strategy** that handles both JWT and opaque tokens efficiently:
|
||||
|
||||
On container startup, this script:
|
||||
1. Installs/enables OIDC app
|
||||
2. Creates JWT client via `occ oidc:create --token_type=jwt`
|
||||
3. Parses JSON output for credentials
|
||||
4. Saves to `/var/www/html/.oauth-jwt/nextcloud_oauth_client.json`
|
||||
|
||||
**Credential Format:**
|
||||
```json
|
||||
{
|
||||
"client_id": "XBd2xqIisu3Kswg39Ub4BUhC36PEYjwwivx3G5nZdDgigvwKXrTHozs7m9DeoLSY",
|
||||
"client_secret": "xNKcy0qpUSau36T60pGGdb03pMEVLXtqykxjK8YkDpoNxNcZ4ClyAT3IAEse2AKT",
|
||||
"client_id_issued_at": 1761097039,
|
||||
"client_secret_expires_at": 2076457039,
|
||||
"redirect_uris": ["http://localhost:8002/oauth/callback"]
|
||||
}
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ verify_token(token) │
|
||||
│ (nextcloud_mcp_server/auth/token_verifier.py:88-142) │
|
||||
└────────────────────────┬────────────────────────────────┘
|
||||
│
|
||||
├──> 1. Check cache (lines 106-109)
|
||||
│ ├─ Hit: Return cached AccessToken
|
||||
│ └─ Miss: Continue to validation
|
||||
│
|
||||
├──> 2. JWT Format Check (lines 112-124)
|
||||
│ ├─ Token has 3 parts (header.payload.signature)?
|
||||
│ │ └─ Yes: Attempt JWT verification
|
||||
│ │ ├─ Verify signature with JWKS (RS256)
|
||||
│ │ ├─ Validate issuer, expiration
|
||||
│ │ ├─ Extract scopes from payload
|
||||
│ │ └─ Success: Return AccessToken
|
||||
│ └─ Fail/Not JWT: Continue to introspection
|
||||
│
|
||||
├──> 3. Introspection (lines 126-134)
|
||||
│ ├─ POST to /apps/oidc/introspect
|
||||
│ ├─ Authenticate with client credentials
|
||||
│ ├─ Response contains:
|
||||
│ │ • active: true/false
|
||||
│ │ • scope: "openid nc:read nc:write"
|
||||
│ │ • sub, exp, iat, client_id
|
||||
│ ├─ Extract scopes from response
|
||||
│ └─ Success: Return AccessToken
|
||||
│
|
||||
└──> 4. Userinfo Fallback (lines 137-142)
|
||||
├─ GET /apps/oidc/userinfo
|
||||
├─ Bearer token in Authorization header
|
||||
├─ Infer scopes from response claims
|
||||
└─ Return AccessToken or None
|
||||
```
|
||||
|
||||
---
|
||||
**Validation Priorities:**
|
||||
|
||||
| Token Type | Method | Performance | Scope Access | Code Reference |
|
||||
|------------|--------|-------------|--------------|----------------|
|
||||
| JWT | JWKS Signature | ⚡ Fastest (local) | Direct (`scope` claim) | `token_verifier.py:156-234` |
|
||||
| Opaque | Introspection | 🔄 Medium (HTTP) | Direct (`scope` field) | `token_verifier.py:236-328` |
|
||||
| Any | Userinfo | 🐌 Slowest (HTTP + inference) | Inferred (from claims) | `token_verifier.py:330-386` |
|
||||
|
||||
**Configuration** (`nextcloud_mcp_server/app.py:391-399`):
|
||||
```python
|
||||
token_verifier = NextcloudTokenVerifier(
|
||||
nextcloud_host=nextcloud_host,
|
||||
userinfo_uri=userinfo_uri,
|
||||
jwks_uri=jwks_uri, # Enables JWT verification
|
||||
issuer=jwt_validation_issuer, # For JWT issuer validation
|
||||
introspection_uri=introspection_uri, # Enables introspection for opaque tokens
|
||||
client_id=client_id, # Required for introspection auth
|
||||
client_secret=client_secret, # Required for introspection auth
|
||||
)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -565,26 +706,26 @@ docker compose exec mcp-oauth-jwt env | grep NEXTCLOUD_OIDC
|
||||
# Ensure no NEXTCLOUD_USERNAME or NEXTCLOUD_PASSWORD set
|
||||
```
|
||||
|
||||
### Issue: Dynamic Client Registration Doesn't Set Scopes
|
||||
### Verifying DCR Scope Configuration
|
||||
|
||||
**Symptom:** Client registered via DCR has empty `allowed_scopes`
|
||||
DCR **now properly sets `allowed_scopes`** when the `scope` parameter is provided during registration.
|
||||
|
||||
**Cause:** OIDC app's registration endpoint doesn't populate `allowed_scopes` from request
|
||||
**To verify DCR scopes are working:**
|
||||
|
||||
**Workaround:** Use pre-configured clients instead of DCR for JWT tokens:
|
||||
```bash
|
||||
# Create client manually via occ
|
||||
docker compose exec app php occ oidc:create \
|
||||
--token_type=jwt \
|
||||
--allowed_scopes="openid profile email nc:read nc:write" \
|
||||
"Client Name" \
|
||||
"http://callback"
|
||||
# Check the registered client's allowed_scopes via database
|
||||
docker compose exec db mariadb -u nextcloud -ppassword nextcloud \
|
||||
-e "SELECT name, allowed_scopes FROM oc_oauth2_clients WHERE name LIKE 'DCR-%' ORDER BY id DESC LIMIT 1;"
|
||||
|
||||
# Configure MCP server to use these credentials
|
||||
export NEXTCLOUD_OIDC_CLIENT_ID="..."
|
||||
export NEXTCLOUD_OIDC_CLIENT_SECRET="..."
|
||||
# Should show your requested scopes (e.g., "openid profile email nc:read nc:write")
|
||||
```
|
||||
|
||||
**If scopes are missing:**
|
||||
1. Ensure `NEXTCLOUD_OIDC_SCOPES` environment variable is set correctly
|
||||
2. Check MCP server startup logs for the scopes being requested
|
||||
3. Verify DCR is enabled in Nextcloud OIDC app settings
|
||||
4. Delete `.nextcloud_oauth_client.json` and restart to force re-registration
|
||||
|
||||
### Issue: Token Type Case Sensitivity
|
||||
|
||||
**Symptom:** JWT tokens not generated even though `token_type=JWT` set
|
||||
@@ -716,9 +857,7 @@ WARNING Missing required scopes: nc:write
|
||||
### Known Limitations
|
||||
|
||||
1. **No Fine-Grained Scopes** - Only coarse `nc:read` and `nc:write` (not per-app scopes)
|
||||
2. **DCR Doesn't Set Allowed Scopes** - Must use pre-configured clients for JWT
|
||||
3. **No Introspection Endpoint** - OIDC app lacks RFC 7662 introspection
|
||||
4. **No Refresh Token Support** - Tokens must be reacquired when expired
|
||||
2. **No Refresh Token Support** - Tokens must be reacquired when expired
|
||||
|
||||
### Future Enhancements
|
||||
|
||||
@@ -727,7 +866,6 @@ WARNING Missing required scopes: nc:write
|
||||
- Resource-level filtering (apply to MCP resources, not just tools)
|
||||
- Automatic scope discovery from decorated tools
|
||||
- Admin UI for scope management
|
||||
- Token introspection endpoint (RFC 7662)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user