The docs claimed scopes are mcp:-prefixed (mcp:notes.read, mcp:notes.write) and that the notes.* pair "covers all Nextcloud apps". Both are false. Per @require_scopes decorators across nextcloud_mcp_server/server/, scopes are unprefixed and per-app: notes.read/write, talk.read/write, files.read/write, calendar.read/write, contacts.read/write, deck.read/write, news.read, tables.read/write, cookbook.read/write, todo.read/write, collectives.read/write, sharing.write, semantic.read, plus standard OIDC scopes. Changes: - login-flow-v2.md: replace the false 2-row "covers all apps" scope table with the real per-app reference (links to scope_authorization.discover_all_scopes() as authoritative source); strip mcp: prefix from intro paragraph, sequence diagrams, @require_scopes example, WWW-Authenticate header example. Also fix sticky-session keying advice per reviewer: route on user identity (sub claim) rather than the raw bearer token, since tokens rotate on refresh. - auth-flows.md: clarify "Astrolabe (hosted UI) → MCP" matrix column header; strip mcp: from sequence diagram and key characteristics bullet; correct "issued by MCP server" to "issued by configured IdP" on the Login Flow v2 token. - authentication.md: strip mcp: from the high-level diagram and scope-enforcement prose; cross-link to the scope reference. - configuration.md: add NEXTCLOUD_OIDC_CLIENT_ID, NEXTCLOUD_OIDC_CLIENT_SECRET, and OIDC_DISCOVERY_URL to the Login Flow v2 vars table — these were undocumented in the table after the round-2 multi-IdP fix. - running.md: drop deprecated `version: '3.8'` from compose snippets (Compose v2 ignores it and emits warnings). - testing-oidc-consent.md: fix sample authorize URL and consent description to use real scope names instead of mcp:-prefixed ones (the manual test as written would have failed with invalid_scope). - CLAUDE.md: replace dead links to deleted oauth-architecture.md, oauth-setup.md, and audience-validation-setup.md with login-flow-v2.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
255 lines
13 KiB
Markdown
255 lines
13 KiB
Markdown
# Authentication Flows by Deployment Mode
|
|
|
|
This document provides a unified reference for the auth flows in each supported deployment mode. For configuration details, see [Authentication](authentication.md). For Login Flow v2 architecture and setup, see [Login Flow v2](login-flow-v2.md).
|
|
|
|
## Quick Reference Matrix
|
|
|
|
| Mode | Client → MCP → NC | Background Sync | Astrolabe (hosted UI) → MCP |
|
|
|------|-------------------|-----------------|------------------------------|
|
|
| [Single-User BasicAuth](#1-single-user-basicauth) | Embedded credentials | Same credentials | N/A |
|
|
| [Multi-User BasicAuth](#2-multi-user-basicauth) | Header pass-through | Stored app password (optional) | OAuth Bearer token |
|
|
| [Login Flow v2](#3-login-flow-v2) | OAuth → MCP, app pwd → NC | Stored app password | OAuth Bearer token |
|
|
|
|
## Communication Patterns
|
|
|
|
This document covers three distinct communication patterns:
|
|
|
|
1. **MCP Client → MCP Server → Nextcloud**: Interactive tool calls initiated by users through MCP clients (Claude Desktop, claude.ai, custom clients).
|
|
2. **MCP Server → Nextcloud**: Background operations like vector sync that run without user interaction.
|
|
3. **Astrolabe → MCP Server**: Astrolabe app backend communication for settings UI and unified search.
|
|
|
|
---
|
|
|
|
## Deployment Modes
|
|
|
|
### 1. Single-User BasicAuth
|
|
|
|
**Use Case:** Personal Nextcloud instance, local development, single-user deployments.
|
|
|
|
#### MCP Client → MCP Server → Nextcloud
|
|
|
|
```
|
|
MCP Client MCP Server Nextcloud
|
|
│ │ │
|
|
│── MCP Request ─────────────▶│ │
|
|
│ (no auth required) │ │
|
|
│ │── HTTP + BasicAuth ───────▶│
|
|
│ │ Authorization: Basic │
|
|
│ │ (embedded credentials) │
|
|
│ │◀── API Response ───────────│
|
|
│◀── Tool Result ─────────────│ │
|
|
```
|
|
|
|
**Key characteristics:**
|
|
- Credentials embedded in server configuration (`NEXTCLOUD_USERNAME`, `NEXTCLOUD_PASSWORD`)
|
|
- Single shared `NextcloudClient` created at startup
|
|
- No MCP-level authentication required (server trusts local clients)
|
|
- All requests use the same Nextcloud user
|
|
|
|
**Implementation:** `context.py` — returns the shared client from lifespan context
|
|
|
|
#### Background Sync
|
|
|
|
Uses the same embedded credentials as interactive requests. The background job accesses Nextcloud with the configured username/password.
|
|
|
|
#### Astrolabe Integration
|
|
|
|
Not applicable — Astrolabe is only used in multi-user deployments where users need personal settings and per-user state.
|
|
|
|
---
|
|
|
|
### 2. Multi-User BasicAuth
|
|
|
|
**Use Case:** Internal deployment where users provide their own Nextcloud credentials via HTTP headers.
|
|
|
|
#### MCP Client → MCP Server → Nextcloud
|
|
|
|
```
|
|
MCP Client MCP Server Nextcloud
|
|
│ │ │
|
|
│── MCP Request ─────────────▶│ │
|
|
│ Authorization: Basic │ │
|
|
│ (user credentials) │ │
|
|
│ │── BasicAuthMiddleware ────▶│
|
|
│ │ Extracts credentials │
|
|
│ │ │
|
|
│ │── HTTP + BasicAuth ───────▶│
|
|
│ │ (pass-through) │
|
|
│ │◀── API Response ───────────│
|
|
│◀── Tool Result ─────────────│ │
|
|
```
|
|
|
|
**Key characteristics:**
|
|
- `BasicAuthMiddleware` extracts credentials from the `Authorization: Basic` header
|
|
- Credentials passed through to Nextcloud (not stored)
|
|
- Client created per-request from extracted credentials
|
|
- Stateless — no credential storage between requests
|
|
|
|
#### Background Sync (Optional)
|
|
|
|
If users provision an app password (via Astrolabe or `nc_auth_provision_access`), the server can run background jobs on their behalf:
|
|
|
|
```
|
|
Astrolabe MCP Server Nextcloud
|
|
│ │ │
|
|
│── Store app password ──────▶│ │
|
|
│ (via management API) │ │
|
|
│ │ [Encrypt + persist locally]│
|
|
│ │ (SQLite, Fernet) │
|
|
│◀── Confirmation ────────────│ │
|
|
│ │ │
|
|
│ [Background job] │ │
|
|
│ │── Retrieve app password ──▶│
|
|
│ │── HTTP + BasicAuth ───────▶│
|
|
│ │◀── API Response ───────────│
|
|
```
|
|
|
|
**Requirements:** `TOKEN_ENCRYPTION_KEY`, `TOKEN_STORAGE_DB`.
|
|
|
|
#### Astrolabe → MCP Server
|
|
|
|
```
|
|
Astrolabe MCP Server Nextcloud OIDC
|
|
│ │ │
|
|
│── OAuth Flow ──────────────▶│◀── Token from IdP ────────▶│
|
|
│ (user initiates) │ │
|
|
│ │ │
|
|
│── Bearer Token ────────────▶│ │
|
|
│ (management API calls) │ │
|
|
│ │── Validate via JWKS ──────▶│
|
|
│ │ (or introspection) │
|
|
│◀── API Response ────────────│ │
|
|
```
|
|
|
|
**Key characteristics:**
|
|
- Astrolabe has its own OAuth client registered with the IdP (Nextcloud OIDC by default; Keycloak / Cognito / etc. when configured via `OIDC_DISCOVERY_URL`)
|
|
- Tokens are validated by the MCP server using the IdP's JWKS (Nextcloud OIDC's JWKS by default; whichever IdP is configured otherwise)
|
|
- Authorization check: `token.sub == requested_resource_owner`
|
|
- The same JWKS-based validation path applies under [Login Flow v2](#3-login-flow-v2) — the MCP server is an OIDC relying party of the configured IdP in both modes; Login Flow v2 only changes the MCP→Nextcloud credential leg (per-user app passwords).
|
|
|
|
---
|
|
|
|
### 3. Login Flow v2
|
|
|
|
**Use Case:** Hosted multi-user deployments, OAuth-based MCP clients (claude.ai, Astrolabe Cloud), production. Recommended for any setup where MCP clients shouldn't handle Nextcloud credentials directly.
|
|
|
|
This mode replaces the previously-supported "OAuth Single-Audience" and "OAuth Token Exchange" modes, both of which required upstream Nextcloud patches that were never merged. See [ADR-022](ADR-022-deployment-mode-consolidation.md) for the rationale.
|
|
|
|
#### MCP Client → MCP Server → Nextcloud (steady state)
|
|
|
|
```
|
|
MCP Client MCP Server Nextcloud
|
|
│ │ │
|
|
│── Bearer Token ────────────▶│ │
|
|
│ (issued by configured IdP,│ │
|
|
│ per-app scopes) │ │
|
|
│ │── Validate scopes ─────────│
|
|
│ │ (@require_scopes) │
|
|
│ │ │
|
|
│ │── Lookup user's │
|
|
│ │ stored app password │
|
|
│ │ │
|
|
│ │── HTTP + BasicAuth ───────▶│
|
|
│ │ Authorization: Basic │
|
|
│ │ (per-user app password) │
|
|
│ │◀── API Response ───────────│
|
|
│◀── Tool Result ─────────────│ │
|
|
```
|
|
|
|
**Key characteristics:**
|
|
- MCP client authenticates to MCP server via OAuth 2.1 + PKCE
|
|
- MCP server is an **OIDC relying party of a configurable IdP** (Nextcloud OIDC by default; Keycloak, AWS Cognito, etc. via `OIDC_DISCOVERY_URL`) + an OAuth facade for MCP clients. RFC 7591 DCR is used to register the MCP-client side; the server's own RP credentials come from `NEXTCLOUD_OIDC_CLIENT_ID/SECRET` (generic OIDC creds), with DCR fallback. Tokens are signed by the chosen IdP and validated against that IdP's JWKS.
|
|
- Per-app scopes (e.g. `notes.read`, `talk.read`, `files.write`) gate tool access — see [Login Flow v2 → Scope Reference](login-flow-v2.md#scope-reference) for the full list
|
|
- Per-user app password obtained via Login Flow v2 (Nextcloud-specific protocol, used regardless of which IdP authenticated the client) and stored encrypted in SQLite
|
|
- App passwords appear in Nextcloud's **Settings → Security → Devices & Sessions** and are user-revocable
|
|
|
|
#### First-Use Provisioning (one-time per user)
|
|
|
|
```
|
|
MCP Client MCP Server Nextcloud
|
|
│ │ │
|
|
│── Bearer Token + request ──▶│ │
|
|
│ │ No stored app password │
|
|
│ │ │
|
|
│◀── Elicit URL or 401 ───────│ │
|
|
│ "Visit <login-url>" │ │
|
|
│ │── POST /index.php/login/v2▶│
|
|
│ │◀── login_url, poll_token ──│
|
|
│ │ │
|
|
│ User opens login_url in browser, authenticates, "Grant"│
|
|
│ ──────────────────────────────────────────────────────▶│
|
|
│ │ │
|
|
│ │── Poll endpoint (bg) ─────▶│
|
|
│ │◀── loginName, appPassword ─│
|
|
│ │ │
|
|
│ │── Encrypt + store │
|
|
│ │ in tokens.db │
|
|
│ │ │
|
|
│── Retry request ───────────▶│── Basic Auth as above ────▶│
|
|
```
|
|
|
|
#### Background Sync
|
|
|
|
Uses the same per-user app password retrieved from encrypted storage. No token refresh needed — Nextcloud app passwords don't expire (until the user revokes them).
|
|
|
|
```
|
|
MCP Server Nextcloud
|
|
│ │
|
|
[Background job starts] │ │
|
|
│── Retrieve app password ──▶│
|
|
│ (per user, from SQLite) │
|
|
│ │
|
|
│── HTTP + BasicAuth ───────▶│
|
|
│◀── API Response ───────────│
|
|
```
|
|
|
|
#### Astrolabe → MCP Server
|
|
|
|
Same as Multi-User BasicAuth — see [Astrolabe → MCP Server](#astrolabe--mcp-server) above.
|
|
|
|
---
|
|
|
|
## Configuration Quick Reference
|
|
|
|
### Single-User BasicAuth
|
|
```bash
|
|
NEXTCLOUD_HOST=https://nextcloud.example.com
|
|
NEXTCLOUD_USERNAME=admin
|
|
NEXTCLOUD_PASSWORD=<app-password>
|
|
```
|
|
|
|
### Multi-User BasicAuth
|
|
```bash
|
|
NEXTCLOUD_HOST=https://nextcloud.example.com
|
|
ENABLE_MULTI_USER_BASIC_AUTH=true
|
|
|
|
# Optional: app-password storage for background sync
|
|
TOKEN_ENCRYPTION_KEY=<fernet-key>
|
|
TOKEN_STORAGE_DB=/app/data/tokens.db
|
|
```
|
|
|
|
### Login Flow v2
|
|
```bash
|
|
NEXTCLOUD_HOST=https://nextcloud.example.com
|
|
ENABLE_LOGIN_FLOW=true
|
|
|
|
# Required for app-password storage
|
|
TOKEN_ENCRYPTION_KEY=<fernet-key>
|
|
TOKEN_STORAGE_DB=/app/data/tokens.db
|
|
|
|
# Public URLs (for browser redirects)
|
|
NEXTCLOUD_MCP_SERVER_URL=https://mcp.example.com
|
|
NEXTCLOUD_PUBLIC_ISSUER_URL=https://nextcloud.example.com
|
|
```
|
|
|
|
See [Login Flow v2](login-flow-v2.md) for full setup, scope reference, and troubleshooting.
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [Authentication](authentication.md) — mode comparison and selection
|
|
- [Login Flow v2](login-flow-v2.md) — multi-user setup details
|
|
- [Configuration](configuration.md) — environment variable reference
|
|
- [ADR-022](ADR-022-deployment-mode-consolidation.md) — design rationale for mode consolidation
|