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>
This commit is contained in:
Chris Coutinho
2026-05-12 20:06:16 +02:00
co-authored by Claude Opus 4.7
parent df4994e860
commit 282c245da1
15 changed files with 166 additions and 68 deletions
@@ -1,9 +1,9 @@
# ADR-020: Deployment Modes and Configuration Validation
**Status:** Accepted
**Status:** Accepted — partly superseded by ADR-022 (`oauth_single_audience` renamed to `login_flow`; the `ENABLE_MULTI_USER_BASIC_AUTH` and `ENABLE_LOGIN_FLOW` env-var aliases were removed in favour of `MCP_DEPLOYMENT_MODE` as the single source of truth)
**Date:** 2025-12-20
**Deciders:** Development Team
**Related:** ADR-002 (Vector Sync), ADR-004 (Progressive Consent), ADR-019 (Multi-user BasicAuth)
**Related:** ADR-002 (Vector Sync), ADR-004 (Progressive Consent), ADR-019 (Multi-user BasicAuth), ADR-022 (Deployment Mode Consolidation)
## Context
@@ -38,7 +38,7 @@ The nextcloud-mcp-server configuration system has grown to ~80+ environment vari
|----------|-------------|---------|
| Core Nextcloud | 6 | `NEXTCLOUD_HOST`, `NEXTCLOUD_USERNAME`, `NEXTCLOUD_VERIFY_SSL` |
| OAuth/OIDC | 12 | `OIDC_DISCOVERY_URL`, `NEXTCLOUD_OIDC_CLIENT_ID`, `JWKS_URI` |
| Mode Selection | 2 | `MCP_DEPLOYMENT_MODE`, `ENABLE_MULTI_USER_BASIC_AUTH` |
| Mode Selection | 1 | `MCP_DEPLOYMENT_MODE` |
| Token Storage | 3 | `TOKEN_ENCRYPTION_KEY`, `TOKEN_STORAGE_DB` |
| Semantic Search | 6 | `ENABLE_SEMANTIC_SEARCH`, `VECTOR_SYNC_SCAN_INTERVAL` |
| Qdrant | 4 | `QDRANT_URL`, `QDRANT_LOCATION`, `QDRANT_API_KEY` |
@@ -114,9 +114,10 @@ nextcloud_ca_bundle = "@none"
# mcp_deployment_mode = ""
# === Authentication Toggles ===
enable_multi_user_basic_auth = false
# `enable_login_flow` is derived from MCP_DEPLOYMENT_MODE=login_flow in
# detect_auth_mode (ADR-022 follow-up) — no separate toggle.
# Both `enable_multi_user_basic_auth` and `enable_login_flow` are derived
# from MCP_DEPLOYMENT_MODE in detect_auth_mode (ADR-022 follow-up) — no
# separate toggles. Only ENABLE_TOKEN_EXCHANGE remains as an independent
# flag (separate cleanup).
enable_token_exchange = false
# === Token Storage ===
@@ -199,7 +200,7 @@ nextcloud_mcp_port = 8000
# nextcloud_password = "" (in .secrets.toml)
[multi_user_basic]
enable_multi_user_basic_auth = true
# enable_multi_user_basic_auth is now derived from the mode (ADR-022 follow-up).
token_storage_db = "/app/data/tokens.db"
[login_flow]
@@ -344,10 +345,16 @@ In **Phase 4**, this could migrate to a post-hook:
# Phase 4 target (not implemented in Phases 1-3)
def resolve_dependencies(settings):
"""Auto-enable background operations for semantic search in multi-user modes."""
mode = (settings.get("MCP_DEPLOYMENT_MODE", "") or "").lower().strip()
is_multi_user = (
settings.get("ENABLE_MULTI_USER_BASIC_AUTH", False)
mode in {"multi_user_basic", "login_flow"}
or settings.get("ENABLE_TOKEN_EXCHANGE", False)
or (not settings.get("NEXTCLOUD_USERNAME") and not settings.get("NEXTCLOUD_PASSWORD"))
or (
mode != "single_user_basic"
and not (
settings.get("NEXTCLOUD_USERNAME") and settings.get("NEXTCLOUD_PASSWORD")
)
)
)
if settings.get("ENABLE_SEMANTIC_SEARCH", False) and is_multi_user:
if not settings.get("ENABLE_BACKGROUND_OPERATIONS", False):
+1 -1
View File
@@ -221,7 +221,7 @@ NEXTCLOUD_PASSWORD=<app-password>
### Multi-User BasicAuth
```bash
NEXTCLOUD_HOST=https://nextcloud.example.com
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
# Optional: app-password storage for background sync
TOKEN_ENCRYPTION_KEY=<fernet-key>
+2 -2
View File
@@ -43,7 +43,7 @@ Each MCP client sends its own credentials in an HTTP `Authorization: Basic` head
```bash
NEXTCLOUD_HOST=https://your.nextcloud.example.com
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
```
`NEXTCLOUD_USERNAME` and `NEXTCLOUD_PASSWORD` must NOT be set in this mode.
@@ -74,7 +74,7 @@ The server detects the active mode from environment variables at startup:
| Env vars present | Detected mode |
|------------------|---------------|
| `NEXTCLOUD_USERNAME` + `NEXTCLOUD_PASSWORD` | Single-User (BasicAuth) |
| `ENABLE_MULTI_USER_BASIC_AUTH=true` (no creds) | Multi-User (BasicAuth pass-through) |
| `MCP_DEPLOYMENT_MODE=multi_user_basic` | Multi-User (BasicAuth pass-through) |
| `MCP_DEPLOYMENT_MODE=login_flow` or no auth env vars set | Multi-User (Login Flow v2) |
You can also force a mode via CLI flag:
+3 -3
View File
@@ -188,7 +188,7 @@ NEXTCLOUD_OIDC_CLIENT_SECRET=secret
**Before (v0.57.x):**
```bash
NEXTCLOUD_HOST=https://nextcloud.example.com
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
# Both required - redundant
ENABLE_OFFLINE_ACCESS=true
@@ -205,7 +205,7 @@ NEXTCLOUD_OIDC_CLIENT_SECRET=secret
**After (v0.58.0+ - Simplified):**
```bash
NEXTCLOUD_HOST=https://nextcloud.example.com
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
# Optional: Explicit mode declaration
MCP_DEPLOYMENT_MODE=multi_user_basic
@@ -448,7 +448,7 @@ Server activates `login_flow` mode when you expected `multi_user_basic`
Add explicit mode declaration:
```bash
MCP_DEPLOYMENT_MODE=multi_user_basic
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
```
---
+1 -1
View File
@@ -74,7 +74,7 @@ Each MCP client sends its own Nextcloud credentials in an `Authorization: Basic`
```dotenv
NEXTCLOUD_HOST=https://your.nextcloud.instance.com
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
# Optional: enable per-user app-password storage for background sync
TOKEN_ENCRYPTION_KEY=<fernet-key>
+1 -1
View File
@@ -147,7 +147,7 @@ For multi-user deployment issues — provisioning loops, app-password storage, O
```bash
# To Single-User BasicAuth: set NEXTCLOUD_USERNAME and NEXTCLOUD_PASSWORD
# To Multi-User BasicAuth pass-through: ENABLE_MULTI_USER_BASIC_AUTH=true (no creds)
# To Multi-User BasicAuth pass-through: MCP_DEPLOYMENT_MODE=multi_user_basic (no creds)
# To Login Flow v2: MCP_DEPLOYMENT_MODE=login_flow (no creds; also the default fallback)
```
+1 -1
View File
@@ -77,7 +77,7 @@ php occ webhook_listeners:remove <webhook-id>
**Configuration:**
```bash
NEXTCLOUD_HOST=http://nextcloud.example.com
ENABLE_MULTI_USER_BASIC_AUTH=true
MCP_DEPLOYMENT_MODE=multi_user_basic
ENABLE_BACKGROUND_OPERATIONS=true
TOKEN_ENCRYPTION_KEY=<key>
TOKEN_STORAGE_DB=/app/data/tokens.db