refactor(config)!: derive enable_login_flow from mode, remove ENABLE_LOGIN_FLOW env var

Once OAUTH_SINGLE_AUDIENCE was renamed to LOGIN_FLOW and the validation
gate ensured the only meaningful configuration was
`MCP_DEPLOYMENT_MODE=login_flow + ENABLE_LOGIN_FLOW=true`, the two
controls became redundant. Setting the mode is sufficient; the
ENABLE_LOGIN_FLOW env var doesn't add information.

This commit makes the deployment mode the single source of truth for
the Login Flow v2 toggle:

- `nextcloud_mcp_server/config.py`: drop the `ENABLE_LOGIN_FLOW`
  dynaconf env-var alias. The `enable_login_flow` field stays as an
  internal attribute so the 6 runtime call sites (app.py x4,
  context.py, auth/scope_authorization.py) keep working unchanged.
  Updated field docstring to flag it as derived.
- `nextcloud_mcp_server/config_validators.py`:
  - Drop `enable_login_flow` from `MODE_REQUIREMENTS[LOGIN_FLOW].required`.
  - Drop the validation gate that required ENABLE_LOGIN_FLOW=true for
    LOGIN_FLOW mode (no longer possible to misconfigure — the flag is
    derived, not user input).
  - Add `_sync_derived_flags()` helper called at every return path of
    `detect_auth_mode` to set `settings.enable_login_flow` from the
    resolved mode.
- `tests/unit/test_config_validators.py`: drop `enable_login_flow=True`
  from happy-path fixtures (no longer needed — detection sets it).
  Repurpose `test_login_flow_requires_enable_login_flow_flag` into
  `test_login_flow_mode_auto_derives_enable_login_flow_flag` which
  asserts the new auto-derivation behaviour for both LOGIN_FLOW and a
  non-LOGIN_FLOW mode.
- `docker-compose.yml`: remove `ENABLE_LOGIN_FLOW=true` from the
  `mcp-login-flow` and `mcp-keycloak` profiles.
- `env.sample`: remove the ENABLE_LOGIN_FLOW reference; the comment
  on `MCP_DEPLOYMENT_MODE` now notes the derived flag.
- `docs/configuration.md`, `docs/authentication.md`,
  `docs/login-flow-v2.md`, `docs/auth-flows.md`,
  `docs/troubleshooting.md`, `docs/ADR-025-*.md`: replace
  ENABLE_LOGIN_FLOW=true examples and references with
  MCP_DEPLOYMENT_MODE=login_flow.

BREAKING CHANGE: `ENABLE_LOGIN_FLOW` is no longer read from the
environment. Anyone who relied on `ENABLE_LOGIN_FLOW=true` to activate
Login Flow v2 should set `MCP_DEPLOYMENT_MODE=login_flow` instead (or
rely on it being the 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 19:45:50 +02:00
co-authored by Claude Opus 4.7
parent c74ef014ee
commit df4994e860
11 changed files with 80 additions and 54 deletions
+8 -3
View File
@@ -458,7 +458,10 @@ class Settings:
# and passes them through to Nextcloud APIs (no storage, stateless)
enable_multi_user_basic_auth: bool = False
# Login Flow v2 settings (ADR-022)
# Login Flow v2 derived flag (ADR-022). Internal — not user-settable.
# Auto-set by detect_auth_mode() when the resolved deployment mode is
# LOGIN_FLOW. Kept as a field for backward compat with the runtime call
# sites that read it (app.py, context.py, scope_authorization.py).
enable_login_flow: bool = False
# Token and webhook storage settings
@@ -853,8 +856,10 @@ def get_settings() -> Settings:
"userinfo_uri": "USERINFO_URI",
# Multi-user BasicAuth pass-through mode
"enable_multi_user_basic_auth": "ENABLE_MULTI_USER_BASIC_AUTH",
# Login Flow v2 settings (ADR-022)
"enable_login_flow": "ENABLE_LOGIN_FLOW",
# 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.
# Token and webhook storage settings
"token_encryption_key": "TOKEN_ENCRYPTION_KEY",
"token_storage_db": "TOKEN_STORAGE_DB",
+27 -13
View File
@@ -114,7 +114,7 @@ MODE_REQUIREMENTS: dict[AuthMode, ModeRequirements] = {
"Optional background sync using app passwords stored via Astrolabe.",
),
AuthMode.LOGIN_FLOW: ModeRequirements(
required=["nextcloud_host", "enable_login_flow"],
required=["nextcloud_host"],
optional=[
# OAuth credentials (uses DCR if not provided)
"oidc_client_id",
@@ -201,24 +201,42 @@ def detect_auth_mode(settings: Settings) -> AuthMode:
explicit_mode = mode_map[mode_str]
logger.info(f"Using explicit deployment mode: {explicit_mode.value}")
_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
# Check for single-user BasicAuth (explicit credentials)
if settings.nextcloud_username and settings.nextcloud_password:
_sync_derived_flags(settings, AuthMode.SINGLE_USER_BASIC)
return AuthMode.SINGLE_USER_BASIC
# Default: Login Flow v2 multi-user mode (browser-based app-password flow).
# The un-augmented OAuth bearer pass-through it replaced required unmerged
# Nextcloud user_oidc patches (see ADR-022); selecting LOGIN_FLOW without
# ENABLE_LOGIN_FLOW=true fails validation below.
# Nextcloud user_oidc patches (see ADR-022).
_sync_derived_flags(settings, AuthMode.LOGIN_FLOW)
return AuthMode.LOGIN_FLOW
def _sync_derived_flags(settings: Settings, mode: AuthMode) -> None:
"""Derive internal feature flags from the resolved deployment mode.
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
removed in the ADR-022 follow-up (PR #787).
"""
settings.enable_login_flow = mode == AuthMode.LOGIN_FLOW
def validate_configuration(settings: Settings) -> tuple[AuthMode, list[str]]:
"""Validate configuration for detected mode.
@@ -308,16 +326,12 @@ def validate_configuration(settings: Settings) -> tuple[AuthMode, list[str]]:
)
if mode == AuthMode.LOGIN_FLOW:
# ADR-022: LOGIN_FLOW requires the Login Flow v2 layer. The un-augmented
# OAuth bearer pass-through (formerly OAUTH_SINGLE_AUDIENCE without
# ENABLE_LOGIN_FLOW) needed unmerged Nextcloud user_oidc patches and
# is no longer supported.
if not settings.enable_login_flow:
errors.append(
f"[{mode.value}] ENABLE_LOGIN_FLOW=true is required for "
"login_flow mode. The un-augmented OAuth path is no longer "
"supported — see ADR-022."
)
# ADR-022 follow-up: the un-augmented OAuth bearer pass-through (the
# old OAUTH_SINGLE_AUDIENCE without ENABLE_LOGIN_FLOW) needed unmerged
# Nextcloud user_oidc patches and is no longer supported. The
# `enable_login_flow` flag is now derived from the resolved mode by
# `_sync_derived_flags`, so users only configure the mode — no
# separate ENABLE_LOGIN_FLOW env var is needed.
# If OAuth credentials not provided, DCR must be available
# (This is a runtime check, not a config check, so we just warn)