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
+25 -12
View File
@@ -9,6 +9,7 @@ See ADR-020 for detailed architecture and deployment mode documentation.
"""
import logging
import os
from dataclasses import dataclass
from enum import Enum
@@ -64,7 +65,6 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = {
"document_chunk_overlap",
],
forbidden=[
"enable_multi_user_basic_auth",
"oidc_client_id",
"oidc_client_secret",
],
@@ -78,7 +78,7 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = {
"Suitable for personal Nextcloud instances and local development.",
),
AuthMode.MULTI_USER_BASIC: ModeRequirements(
required=["nextcloud_host", "enable_multi_user_basic_auth"],
required=["nextcloud_host"],
optional=[
# Background sync with app passwords (via Astrolabe)
"enable_offline_access",
@@ -138,7 +138,6 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = {
forbidden=[
"nextcloud_username",
"nextcloud_password",
"enable_multi_user_basic_auth",
],
conditional={
"enable_offline_access": [
@@ -181,6 +180,21 @@ def detect_auth_mode(settings: Settings) -> AuthMode:
logger = logging.getLogger(__name__)
# ADR-022 follow-up: fail loudly if a caller is still relying on the
# removed env-var aliases. Bypass dynaconf and read os.environ directly
# so the check survives even though the aliases are gone.
for legacy, replacement in (
("ENABLE_MULTI_USER_BASIC_AUTH", "multi_user_basic"),
("ENABLE_LOGIN_FLOW", "login_flow"),
):
if os.getenv(legacy):
raise ValueError(
f"{legacy} is no longer read from the environment. "
f"Set MCP_DEPLOYMENT_MODE={replacement} instead "
"(ADR-022). The deployment mode is the single source of "
"truth for selecting an auth flow."
)
# ADR-021: Check for explicit deployment mode first
if settings.deployment_mode:
mode_str = settings.deployment_mode.lower().strip()
@@ -204,11 +218,11 @@ def detect_auth_mode(settings: Settings) -> AuthMode:
_sync_derived_flags(settings, explicit_mode)
return explicit_mode
# Auto-detection (existing behavior)
# Check for multi-user BasicAuth
if settings.enable_multi_user_basic_auth:
_sync_derived_flags(settings, AuthMode.MULTI_USER_BASIC)
return AuthMode.MULTI_USER_BASIC
# Auto-detection (no explicit deployment_mode).
# MULTI_USER_BASIC is no longer auto-detectable — the ENABLE_MULTI_USER_BASIC_AUTH
# env-var alias was dropped in the ADR-022 follow-up, so the only way to
# opt into that mode is `MCP_DEPLOYMENT_MODE=multi_user_basic` (handled
# above). The legacy env var fails loudly at the top of this function.
# Check for single-user BasicAuth (explicit credentials)
if settings.nextcloud_username and settings.nextcloud_password:
@@ -228,13 +242,12 @@ def _sync_derived_flags(settings: Settings, mode: AuthMode) -> None:
Some runtime call sites (app.py, context.py, auth/scope_authorization.py)
still read individual boolean flags rather than passing the mode around.
Keep those flags in sync with the mode here so the mode is the single
source of truth and users don't have to set redundant env vars.
Specifically: `enable_login_flow` is now derived from
`mode == AuthMode.LOGIN_FLOW`. The ENABLE_LOGIN_FLOW env-var alias was
source of truth and users don't have to set redundant env vars. The
ENABLE_LOGIN_FLOW and ENABLE_MULTI_USER_BASIC_AUTH env-var aliases were
removed in the ADR-022 follow-up (PR #787).
"""
settings.enable_login_flow = mode == AuthMode.LOGIN_FLOW
settings.enable_multi_user_basic_auth = mode == AuthMode.MULTI_USER_BASIC
def validate_configuration(settings: Settings) -> tuple[AuthMode, list[str]]: