Files
Chris CoutinhoandClaude Opus 4.7 282c245da1 refactor(config)!: drop ENABLE_MULTI_USER_BASIC_AUTH env var, fail loud on legacy aliases
Same pattern as the ENABLE_LOGIN_FLOW removal in the previous commit:
the deployment mode (MCP_DEPLOYMENT_MODE) is the single source of truth
for selecting an auth flow. The ENABLE_MULTI_USER_BASIC_AUTH env-var
alias is redundant with `MCP_DEPLOYMENT_MODE=multi_user_basic`.

Unlike the ENABLE_LOGIN_FLOW removal — where silent removal was safe
because Login Flow v2 is the auto-detection default — silent removal
here would be a surprise: a user with only ENABLE_MULTI_USER_BASIC_AUTH=true
in their .env would auto-detect into LOGIN_FLOW after upgrade (wrong
runtime mode). Mitigation: detect_auth_mode now reads os.environ
directly for both legacy aliases and raises ValueError with a one-line
migration message if either is set. Applied retroactively to
ENABLE_LOGIN_FLOW as well — loud is better than silent.

- nextcloud_mcp_server/config.py:
  - Drop the dynaconf env-var alias entry for ENABLE_MULTI_USER_BASIC_AUTH.
  - Update the `enable_multi_user_basic_auth` field docstring to mark it
    as derived / not user-settable.
  - `_is_multi_user_mode()` (early-config helper, runs before Settings
    is built) switched to checking MCP_DEPLOYMENT_MODE directly. Now
    consistent with the canonical detection in detect_auth_mode.
- nextcloud_mcp_server/config_validators.py:
  - Drop the auto-detection branch (`if settings.enable_multi_user_basic_auth`).
    Selection of MULTI_USER_BASIC is now exclusively via the explicit
    MCP_DEPLOYMENT_MODE branch.
  - Add `enable_multi_user_basic_auth` to `_sync_derived_flags` alongside
    `enable_login_flow` — both flags are now derived from the resolved mode.
  - Drop `enable_multi_user_basic_auth` from
    `MODE_REQUIREMENTS[MULTI_USER_BASIC].required` and from the
    `forbidden` lists of SINGLE_USER_BASIC and LOGIN_FLOW (no longer
    user input → no meaningful forbidden check).
  - Add loud-deprecation `ValueError` block at the top of detect_auth_mode
    that errors with a clear migration message when ENABLE_MULTI_USER_BASIC_AUTH
    or ENABLE_LOGIN_FLOW is found in os.environ.
- tests/unit/test_config_validators.py:
  - Switch ~10 fixtures from `enable_multi_user_basic_auth=True` to
    `deployment_mode="multi_user_basic"` (mirrors `enable_login_flow`
    treatment from the previous commit).
  - Switch two `patch.dict(os.environ, {"ENABLE_MULTI_USER_BASIC_AUTH": "true"})`
    blocks to use MCP_DEPLOYMENT_MODE.
  - Rename `test_forbidden_multi_user_basic_auth` to
    `test_forbidden_multi_user_basic_when_credentials_present` — the
    scenario is now an explicit-mode + credentials conflict, not an
    env-var-flag conflict.
  - Add `test_legacy_enable_multi_user_basic_auth_env_var_errors` and
    `test_legacy_enable_login_flow_env_var_errors` to exercise the new
    loud-deprecation ValueError path.
- docker-compose.yml: mcp-multi-user-basic profile switched to
  `MCP_DEPLOYMENT_MODE=multi_user_basic`.
- env.sample: replaced `#ENABLE_MULTI_USER_BASIC_AUTH=true` example with
  `#MCP_DEPLOYMENT_MODE=multi_user_basic`.
- docs/authentication.md, configuration.md, troubleshooting.md,
  auth-flows.md, webhook-management-guide.md,
  configuration-migration-v2.md, ADR-025: replaced env-var examples
  with the canonical MCP_DEPLOYMENT_MODE form.
- docs/ADR-020: marked partly superseded by ADR-022.
- CLAUDE.md: Multi-User BasicAuth section updated to set
  MCP_DEPLOYMENT_MODE.
- nextcloud_mcp_server/vector/oauth_sync.py: module docstring updated.

BREAKING CHANGE: ENABLE_MULTI_USER_BASIC_AUTH is no longer read from
the environment, and setting it now raises a startup ValueError with
a migration message. Replace `ENABLE_MULTI_USER_BASIC_AUTH=true` with
`MCP_DEPLOYMENT_MODE=multi_user_basic`. The same loud-deprecation
check is also applied to the recently-removed ENABLE_LOGIN_FLOW —
replace with `MCP_DEPLOYMENT_MODE=login_flow` (or drop both;
`login_flow` is the auto-detect default when no other auth env vars
are set).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:06:16 +02:00

13 KiB

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. For Login Flow v2 architecture and setup, see Login Flow v2.

Quick Reference Matrix

Mode Client → MCP → NC Background Sync Astrolabe (hosted UI) → MCP
Single-User BasicAuth Embedded credentials Same credentials N/A
Multi-User BasicAuth Header pass-through Stored app password (optional) OAuth Bearer token
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                   OIDC Provider
    │                             │                            │
    │── 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 — 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 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 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 above.


Configuration Quick Reference

Single-User BasicAuth

NEXTCLOUD_HOST=https://nextcloud.example.com
NEXTCLOUD_USERNAME=admin
NEXTCLOUD_PASSWORD=<app-password>

Multi-User BasicAuth

NEXTCLOUD_HOST=https://nextcloud.example.com
MCP_DEPLOYMENT_MODE=multi_user_basic

# Optional: app-password storage for background sync
TOKEN_ENCRYPTION_KEY=<fernet-key>
TOKEN_STORAGE_DB=/app/data/tokens.db

Login Flow v2

NEXTCLOUD_HOST=https://nextcloud.example.com
MCP_DEPLOYMENT_MODE=login_flow

# 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 for full setup, scope reference, and troubleshooting.