docs: correct OIDC architecture framing for Login Flow v2
The previous round of review feedback rested on a misunderstanding — that the MCP server is "the OAuth issuer" under Login Flow v2 and that NEXTCLOUD_OIDC_CLIENT_ID/SECRET are external-IdP-only. Code says otherwise (app.py:619/625/703-717, unified_verifier.py:72): - The MCP server is an OIDC relying party of Nextcloud OIDC. Tokens are signed by Nextcloud and validated against Nextcloud's JWKS in all modes — the server has no private signing keys. - Static NEXTCLOUD_OIDC_CLIENT_ID/SECRET are the preferred way to register the MCP server as that relying party; RFC 7591 DCR is a fallback when both are unset. - Login Flow v2 layers per-user app-password acquisition on top — it governs the MCP→Nextcloud data leg, not the relying-party setup. This commit reverts the inaccuracies introduced by35c115eand reframes the original `login-flow-v2.md` to match what the code does: - login-flow-v2.md: revise "How It Works" to describe the MCP server as an OIDC RP + OAuth facade (not a standalone issuer); rename "OAuth Issuer Endpoints" → "OAuth Endpoints" with a note that those endpoints front Nextcloud OIDC; add NEXTCLOUD_OIDC_CLIENT_ID/SECRET to the required env vars with DCR documented as fallback. - running.md: restore the static-creds Docker example (deleted in35c115eon the wrong reasoning that it was tied to the retired direct-OAuth-to-Nextcloud flow); rewrite the OAuth Mode section intro to describe the actual relying-party + facade architecture. - configuration.md: fix Best Practices "For Production" to mention static creds as preferred / DCR as fallback; restore the .oauth Docker volume alongside data so DCR-registered MCP-client state and the encrypted app-password DB both persist. - auth-flows.md: drop the note added in35c115ethat wrongly claimed the MCP server validates Bearer tokens against its own JWKS under Login Flow v2 — it validates against Nextcloud's JWKS in all modes; reword the Login Flow v2 "Key characteristics" bullet that called the MCP server "the OAuth authorization server". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
35c115ead6
commit
319e82774e
+17
-9
@@ -11,13 +11,15 @@ Two authentication legs, each with a different mechanism:
|
||||
```
|
||||
┌─────────────────┐ OAuth/OIDC ┌──────────────────┐ App password ┌─────────────────┐
|
||||
│ MCP Client │ ───────────────> │ MCP Server │ ────────────────> │ Nextcloud │
|
||||
│ (Claude, etc.) │ (mcp:* scopes) │ (OAuth issuer + │ (Basic Auth) │ (NC 16+) │
|
||||
│ (Claude, etc.) │ (mcp:* scopes) │ (OIDC RP of NC, │ (Basic Auth) │ (NC 16+, │
|
||||
│ │ │ OAuth facade, │ │ OIDC issuer) │
|
||||
│ │ │ app-pwd holder) │ │ │
|
||||
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
- **MCP client → MCP server**: OAuth 2.1 with PKCE. The MCP server is the authorization server (or proxies an external IdP). Tokens carry `mcp:*` scopes that gate which tools the user can call.
|
||||
- **MCP server → Nextcloud**: Per-user **app password** obtained via Nextcloud's native [Login Flow v2](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2). Sent as HTTP Basic Auth.
|
||||
- **MCP client → MCP server**: OAuth 2.1 with PKCE. The MCP server is **not** a standalone OAuth issuer — it acts as an OIDC relying party of Nextcloud OIDC and exposes an OAuth facade in front of it. Tokens are signed by Nextcloud's OIDC provider, validated by the MCP server against Nextcloud's JWKS, and carry `mcp:*` scopes that gate which tools the user can call.
|
||||
- **MCP server → Nextcloud (auth leg)**: The MCP server registers itself with Nextcloud's OIDC provider via static `NEXTCLOUD_OIDC_CLIENT_ID`/`SECRET` (preferred) or RFC 7591 DCR (fallback). This relationship is used for OIDC discovery, JWKS retrieval, and token validation — *not* for proxying client tokens to Nextcloud APIs.
|
||||
- **MCP server → Nextcloud (data leg)**: Per-user **app password** obtained via Nextcloud's native [Login Flow v2](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2). Sent as HTTP Basic Auth.
|
||||
|
||||
App passwords appear in **Settings → Security → Devices & Sessions** in Nextcloud and can be revoked by the user at any time.
|
||||
|
||||
@@ -35,14 +37,20 @@ Scope enforcement happens at the MCP server layer (defense-in-depth). See [Scope
|
||||
# Nextcloud connection
|
||||
NEXTCLOUD_HOST=https://your.nextcloud.example.com
|
||||
|
||||
# Enable Login Flow v2
|
||||
# OIDC client credentials for the MCP server's relying-party relationship with Nextcloud OIDC.
|
||||
# Preferred path: register a client in Nextcloud admin → OIDC and set these. If both are unset
|
||||
# and Nextcloud advertises a `registration_endpoint`, the server falls back to RFC 7591 DCR.
|
||||
NEXTCLOUD_OIDC_CLIENT_ID=<your-client-id>
|
||||
NEXTCLOUD_OIDC_CLIENT_SECRET=<your-client-secret>
|
||||
|
||||
# Enable Login Flow v2 (per-user app-password provisioning for the MCP→Nextcloud data leg)
|
||||
ENABLE_LOGIN_FLOW=true
|
||||
|
||||
# App-password storage (required for persistence across restarts)
|
||||
TOKEN_STORAGE_DB=/app/data/tokens.db
|
||||
TOKEN_ENCRYPTION_KEY=<fernet-key> # see "Generating an encryption key" below
|
||||
|
||||
# Public OAuth issuer URL (the URL clients will be redirected to for browser flows)
|
||||
# Public URLs (for browser redirects)
|
||||
NEXTCLOUD_MCP_SERVER_URL=https://mcp.example.com
|
||||
NEXTCLOUD_PUBLIC_ISSUER_URL=https://your.nextcloud.example.com # Public URL of Nextcloud
|
||||
```
|
||||
@@ -186,15 +194,15 @@ Clients can use this header to trigger **step-up authorization** — re-running
|
||||
|
||||
Implementation: [`nextcloud_mcp_server/auth/scope_authorization.py`](../nextcloud_mcp_server/auth/scope_authorization.py).
|
||||
|
||||
## OAuth Issuer Endpoints
|
||||
## OAuth Endpoints
|
||||
|
||||
When `--oauth` is enabled, the MCP server exposes OAuth 2.1 endpoints:
|
||||
When `--oauth` is enabled, the MCP server exposes OAuth 2.1 endpoints. **These endpoints front Nextcloud OIDC** — discovery metadata, token issuance, and JWKS still come from Nextcloud; the MCP server is not a standalone OAuth issuer.
|
||||
|
||||
| Endpoint | RFC | Purpose |
|
||||
|----------|-----|---------|
|
||||
| `GET /.well-known/oauth-authorization-server` | RFC 8414 | Server metadata |
|
||||
| `GET /.well-known/oauth-authorization-server` | RFC 8414 | Server metadata (advertises Nextcloud OIDC as the upstream issuer) |
|
||||
| `GET /.well-known/oauth-protected-resource/mcp` | RFC 9728 | PRM — advertises supported scopes (dynamically discovered from `@require_scopes`) |
|
||||
| `POST /register` | RFC 7591 | Dynamic Client Registration |
|
||||
| `POST /register` | RFC 7591 | Dynamic Client Registration (for MCP clients; see also `NEXTCLOUD_OIDC_CLIENT_ID/SECRET` for the MCP server's own RP credentials) |
|
||||
| `PUT/DELETE /register/{client_id}` | RFC 7592 | Client management with registration token |
|
||||
| `GET /authorize` | RFC 6749 | Authorization endpoint (PKCE required, S256) |
|
||||
| `POST /token` | RFC 6749 | Token endpoint |
|
||||
|
||||
Reference in New Issue
Block a user