From 5ef141eb17b66bd420bc931f07ec80f22533c26b Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 3 Nov 2026 01:16:40 -0800 Subject: [PATCH] fix(caddy): tell Audiobookshelf its real scheme so the OIDC callback validates ABS checks every OIDC callback against the origin it believes it is served from: getRequestOrigin() is x-forwarded-proto://Host (dist-server/server/utils/requestUtils.js, read in the pinned image digest 2026-11-03). The post-auth http://:8017 listener is fed over plain http, so Caddy's own X-Forwarded-Proto is http, ABS derives http://abs.franksdank.cloud, and answers its own https callback with 400 {"message":"Invalid callback URL - must be same-origin"} Measured at the app with the callback held fixed: Host abs.franksdank.cloud alone reproduces the 400 (as does every other Host, including the bare LAN IP), and adding `X-Forwarded-Proto: https` turns it into a 302 to /application/o/authorize/ carrying the registered redirect_uri. The edge for a guarded name is TLS-only, so https is the truthful value, not a spoof; the LAN door needs nothing because there browser and server already agree on http, which is why only the guarded path was broken. --- dankclaw/Caddyfile | 11 ++++++++++- serverAppConfigs-docs/Dankserver/Audiobookshelf.md | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/dankclaw/Caddyfile b/dankclaw/Caddyfile index 2243ca6..9d85fcb 100644 --- a/dankclaw/Caddyfile +++ b/dankclaw/Caddyfile @@ -429,7 +429,16 @@ http://:8017 { # convenience hop, taken post-auth so nothing reaches the app # pre-authentication. redir / /audiobookshelf/ permanent - reverse_proxy 192.168.0.235:8000 + # Audiobookshelf validates every OIDC callback against the origin it + # believes it is served from — getRequestOrigin() is + # x-forwarded-proto://Host — and a mismatch is a bodiless 400 + # "Invalid callback URL - must be same-origin" before the IdP is ever + # contacted (measured 2026-11-03). This listener is fed over plain http + # by the outpost, so Caddy's own X-Forwarded-Proto here says http; the + # edge for this name is TLS-only, so https is the truthful value. + reverse_proxy 192.168.0.235:8000 { + header_up X-Forwarded-Proto https + } } @prowlarr host prowlarr.franksdank.cloud diff --git a/serverAppConfigs-docs/Dankserver/Audiobookshelf.md b/serverAppConfigs-docs/Dankserver/Audiobookshelf.md index 0e397df..f6479b3 100644 --- a/serverAppConfigs-docs/Dankserver/Audiobookshelf.md +++ b/serverAppConfigs-docs/Dankserver/Audiobookshelf.md @@ -140,6 +140,14 @@ Both halves — authentik's provider row and ABS's settings — are asserted end second wall appears after the fix ships (measured 2026-11-02: the browser reached authentik, got `code`+`state`, and ABS still bounced to `/login?error=undefined`). The script sets `authorization_code` + `refresh_token`; a blueprint re-import reverts the row silently. +- **The guarded door has to tell the truth about its scheme.** ABS validates every OIDC + `callback` against the origin it believes it is served from — `getRequestOrigin()` is + `x-forwarded-proto://Host` (`server/utils/requestUtils.js`, read in the pinned digest + 2026-11-03) — so behind a TLS edge that reaches the container over plain http it computes + `http://abs.franksdank.cloud` and answers its own https callback with a 400 "Invalid callback + URL - must be same-origin" before the IdP is ever contacted. The Caddy `:8017` route pins + `header_up X-Forwarded-Proto https` for exactly that reason ([[Caddy]] *Guard a new vhost*); + the LAN door needs nothing, because there browser and server agree on `http`. - **The claim mapping is not the whole `property_mappings` list.** A provider holding only the custom mapping grants a token scoped shorter than the scopes ABS requests (`openid profile email abs_groups`): the code is issued and exchanged normally, and ABS's