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
+28 -15
View File
@@ -453,9 +453,13 @@ class Settings:
# Progressive Consent settings (always enabled - no flag needed)
enable_offline_access: bool = False
# Multi-user BasicAuth pass-through mode (ADR-019 interim solution)
# When enabled, MCP server extracts BasicAuth credentials from request headers
# and passes them through to Nextcloud APIs (no storage, stateless)
# Multi-user BasicAuth pass-through mode (ADR-019 interim solution).
# Internal — not user-settable; the ENABLE_MULTI_USER_BASIC_AUTH env-var
# alias was removed in the ADR-022 follow-up. Auto-set by
# detect_auth_mode() when MCP_DEPLOYMENT_MODE=multi_user_basic. When True,
# the MCP server extracts BasicAuth credentials from request headers and
# passes them through to Nextcloud APIs (no storage, stateless). Kept
# as a field for backward compat with the runtime call sites that read it.
enable_multi_user_basic_auth: bool = False
# Login Flow v2 derived flag (ADR-022). Internal — not user-settable.
@@ -723,20 +727,31 @@ def _get_semantic_search_enabled() -> bool:
def _is_multi_user_mode() -> bool:
"""Detect if this is a multi-user deployment mode.
Runs early in config setup (before Settings is fully built) for
mode-conditional defaults. Must match the canonical detection in
`config_validators.detect_auth_mode`, but works directly against the
raw dynaconf store since Settings doesn't exist yet.
Multi-user modes are:
- Multi-user BasicAuth (ENABLE_MULTI_USER_BASIC_AUTH=true)
- OAuth Single-Audience (no username/password set)
- Multi-user BasicAuth (MCP_DEPLOYMENT_MODE=multi_user_basic)
- Login Flow v2 / default OAuth (MCP_DEPLOYMENT_MODE=login_flow, or no
username/password and no explicit mode)
- OAuth Token Exchange (ENABLE_TOKEN_EXCHANGE=true)
Single-user modes are:
Single-user mode is:
- Single-user BasicAuth (username and password both set)
Returns:
True if multi-user mode detected
"""
# Multi-user BasicAuth explicitly enabled
if _dynaconf.get("ENABLE_MULTI_USER_BASIC_AUTH", False):
# Explicit deployment mode wins. The ENABLE_MULTI_USER_BASIC_AUTH env-var
# alias was removed in the ADR-022 follow-up; selection is now via
# MCP_DEPLOYMENT_MODE.
explicit_mode = str(_dynaconf.get("MCP_DEPLOYMENT_MODE", "") or "").lower().strip()
if explicit_mode in {"multi_user_basic", "login_flow"}:
return True
if explicit_mode == "single_user_basic":
return False
# Token exchange implies OAuth multi-user
if _dynaconf.get("ENABLE_TOKEN_EXCHANGE", False):
@@ -748,7 +763,7 @@ def _is_multi_user_mode() -> bool:
if has_username and has_password:
return False
# Otherwise, assume OAuth multi-user (default when no credentials provided)
# Otherwise, assume multi-user (default when no credentials provided)
return True
@@ -854,12 +869,10 @@ def get_settings() -> Settings:
"jwks_uri": "JWKS_URI",
"introspection_uri": "INTROSPECTION_URI",
"userinfo_uri": "USERINFO_URI",
# Multi-user BasicAuth pass-through mode
"enable_multi_user_basic_auth": "ENABLE_MULTI_USER_BASIC_AUTH",
# NOTE: `enable_login_flow` used to have an `ENABLE_LOGIN_FLOW` env-var
# alias here, but it was removed in the ADR-022 follow-up — the flag
# is now derived from MCP_DEPLOYMENT_MODE=login_flow and set by
# detect_auth_mode() so users only need to configure the mode.
# NOTE: `enable_multi_user_basic_auth` and `enable_login_flow` no
# longer have env-var aliases — both are derived from the resolved
# MCP_DEPLOYMENT_MODE in detect_auth_mode() so users only configure
# the mode (ADR-022 follow-up).
# Token and webhook storage settings
"token_encryption_key": "TOKEN_ENCRYPTION_KEY",
"token_storage_db": "TOKEN_STORAGE_DB",