fix: OIDC callback sent wrong scheme in token exchange redirect_uri
Fastify only sees plain HTTP -- TLS terminates at NPM/Cloudflare before reaching this process. Building the callback's currentUrl from req.headers.host with a hardcoded "http://" sent redirect_uri=http://monitor.jerodrigged.com/... during the token exchange, which Authentik rejects (logged as generic "invalid_client" to the client, but its own event log said plainly: "Invalid redirect URI used by provider"). Fixed by reusing the known-correct redirectUri's origin and only taking the query string from the actual request, instead of trying to infer scheme from headers. Also fixes the Playwright OIDC test's selectors (Authentik's password field has no <label> association -- placeholder text, not getByLabel). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,16 @@ export function registerOidcRoutes(
|
||||
return reply.code(400).send({ error: "no OIDC login in progress" });
|
||||
}
|
||||
try {
|
||||
const currentUrl = new URL(req.url, `http://${req.headers.host}`);
|
||||
// Fastify only ever sees plain HTTP here -- TLS terminates at NPM/Cloudflare
|
||||
// before reaching this process (see docs/oidc-setup.md). Building this URL
|
||||
// from req.headers.host with a hardcoded "http://" sent the wrong scheme in
|
||||
// the token exchange's redirect_uri, which Authentik rejects as a mismatch
|
||||
// against the https:// URL registered for this provider. Since redirectUri
|
||||
// is guaranteed correct (it's the exact value used to build the original
|
||||
// authorize request), reuse its origin and only take the query string from
|
||||
// the actual incoming request.
|
||||
const currentUrl = new URL(redirectUri);
|
||||
currentUrl.search = new URL(req.url, "http://placeholder").search;
|
||||
const { username } = await handleCallback(oidcConfig, currentUrl, oidcState, oidcCodeVerifier);
|
||||
req.session.username = username;
|
||||
req.session.oidcState = undefined;
|
||||
|
||||
@@ -20,7 +20,9 @@ test("OIDC login via Authentik stays on public domains throughout", async ({ pag
|
||||
await page.getByPlaceholder(/email or username/i).fill(USERNAME);
|
||||
await page.getByRole("button", { name: /log in|continue|next/i }).click();
|
||||
|
||||
await page.getByLabel(/password/i).fill(PASSWORD);
|
||||
// Authentik's password field has no <label> association -- it's identified
|
||||
// by placeholder text ("Please enter your password"), not an a11y label.
|
||||
await page.getByPlaceholder(/password/i).fill(PASSWORD);
|
||||
await page.getByRole("button", { name: /log in|continue|sign in/i }).click();
|
||||
|
||||
await page.waitForURL(/^https:\/\/monitor\.jerodrigged\.com\//, { timeout: 15_000 });
|
||||
|
||||
Reference in New Issue
Block a user