fix(oidc): clamp post_login_redirect to CORS allow-list (open-redirect)

Closes the open-redirect risk surfaced by the v0.3.x security audit:
the OIDC callback handler was using oidc_config.post_login_redirect
verbatim as the redirect base on both error and success paths. An
attacker with admin-key compromise (or a misconfigured operator) could
set that field to an external domain and turn /api/auth/oidc/callback
into a phishing redirector ('dialectic login failed → re-enter
password at evil.com/login').

Fix:
  - New AuthHandler.safeRedirectBase(raw) validator:
      * empty → '/'
      * relative path starting with '/' (but not '//') → keep as-is
      * absolute URL whose host is in the allow-list → keep as-is
      * everything else → '/'
  - allow-list sourced from cfg.CORSAllowOrigins (the same set we
    already trust for browser CORS), threaded through NewAuthHandler.
  - Applied on BOTH the error branch and success branch of Callback.

Also: drop redundant newline on cmd/dialectic-cli usage Fprintln so
go test ./... passes.

No behavior change for happy path: prod's PostLoginRedirect is
'https://dialectic.hangman-lab.top/oidc/callback', which matches the
CORS allow-list ('https://dialectic.hangman-lab.top'), so the
validator returns it unmodified.
This commit is contained in:
h z
2026-05-24 10:03:53 +01:00
parent 2463129dbd
commit b02b1706b6
3 changed files with 61 additions and 11 deletions

View File

@@ -127,7 +127,7 @@ func check(label string, err error) {
}
func usage() {
fmt.Fprintln(os.Stderr, `Usage:
fmt.Fprint(os.Stderr, `Usage:
dialectic-cli config oidc [--issuer <url>] [--client-id <id>]
[--client-secret <s>] [--callback-url <url>]
[--post-login-redirect <url>]