fix(auth): login-flow provisioning — public login_url + session app passwords
Two fixes surfaced while testing Login Flow v2 provisioning behind a split internal/external host (Docker: server↔Nextcloud over http://app, browser over http://localhost:8080): 1. login_url pointed at the internal host. Nextcloud builds the login URL from the request host, so the browser-facing URL came back as http://app/login/v2/flow/... — unreachable from the user's browser. The poll endpoint was already rewritten to the internal host (correct, the server polls it); now LoginFlowV2Client also rewrites the login_url origin to settings.nextcloud_public_issuer_url when set (passed at all 5 construction sites). When unset, behaviour is unchanged. 2. The app-password format guard rejected raw session tokens. core/ getapppassword returns a long alphanumeric token, not the dashed 25-char Security-settings format, so the dashed-only regex 400'd the one-click opt-in handoff. Relax APP_PASSWORD_PATTERN to `^[a-zA-Z0-9-]{20,256}$`; the authoritative validation is still the BasicAuth check against Nextcloud. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bf35200bab
commit
ae54956f27
@@ -180,6 +180,25 @@ async def test_provision_app_password_invalid_format():
|
||||
assert "Invalid app password format" in response.json()["error"]
|
||||
|
||||
|
||||
def test_app_password_pattern_accepts_dashed_and_raw_tokens():
|
||||
"""The format guard accepts both the dashed Security-settings format and
|
||||
the raw token from the one-click ``core/getapppassword`` flow, and still
|
||||
rejects short / illegal-character input."""
|
||||
from nextcloud_mcp_server.api.passwords import APP_PASSWORD_PATTERN
|
||||
|
||||
# Dashed format a user copies from Security settings.
|
||||
assert APP_PASSWORD_PATTERN.match("abcde-ABCDE-12345-fghij-67890")
|
||||
# Raw 72-char token returned by core/getapppassword (one-click opt-in).
|
||||
assert APP_PASSWORD_PATTERN.match(
|
||||
"kZmgLDQnqQHUAxhRq4d2VssBfjsI0PaHbL4JySWtwJkzVgAf34c0sZshEjZjuj1PLbwrf83q"
|
||||
)
|
||||
# Still rejects obviously-bad input.
|
||||
assert not APP_PASSWORD_PATTERN.match("short")
|
||||
assert not APP_PASSWORD_PATTERN.match("invalid-password") # < 20 chars
|
||||
assert not APP_PASSWORD_PATTERN.match("has spaces not allowed in this token")
|
||||
assert not APP_PASSWORD_PATTERN.match("contains/slash/" + "a" * 20)
|
||||
|
||||
|
||||
async def test_provision_app_password_success(temp_storage, mocker):
|
||||
"""Test successful app password provisioning."""
|
||||
# Mock settings (imported locally in the function)
|
||||
|
||||
Reference in New Issue
Block a user