fix(oidc): redirect callback to /login so the SPA receives oidc_code

After a successful OIDC callback the backend redirected the browser to
`{frontend_url}/?oidc_code=…` (the site root). But the SvelteKit SPA only
reads `oidc_code` on the `/login` route: the root route immediately
`goto`s `/files`, and the layout's auth guard bounces an unauthenticated
visitor to `/login?redirect=…` — both of which drop the `oidc_code` query
param. The exchange step (`POST /api/auth/oidc/exchange`) therefore never
runs, so the user lands back on the login form with no session even though
the IdP round-trip and callback succeeded.

Redirect to `{frontend_url}/login?oidc_code=…` instead — the route that
actually performs the exchange. `/login` is public, so the guard doesn't
interfere; after a successful exchange the page navigates on to the app.

This was masked until now by #510 (the duplicate-callback 403 always fired
first); with that fixed, the callback reaches the frontend and this second
bug surfaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Paul Meier
2026-06-21 15:18:23 -05:00
parent e7b85e56e2
commit d1bbe8ba45
+3 -3
View File
@@ -886,8 +886,8 @@ pub async fn oidc_authorize(
/// Handle the OIDC provider callback. /// Handle the OIDC provider callback.
/// ///
/// Validates the `state` / PKCE / nonce, exchanges the code for tokens, then /// Validates the `state` / PKCE / nonce, exchanges the code for tokens, then
/// redirects the browser to the frontend with a short-lived exchange code /// redirects the browser to the frontend login route with a short-lived
/// (`/?oidc_code=…`). /// exchange code (`/login?oidc_code=…`), which the SPA swaps for a session.
#[utoipa::path( #[utoipa::path(
get, get,
path = "/api/auth/oidc/callback", path = "/api/auth/oidc/callback",
@@ -937,7 +937,7 @@ pub async fn oidc_callback(
// Regular web login, redirect to frontend with exchange code // Regular web login, redirect to frontend with exchange code
let config = auth_app.oidc_config().unwrap(); let config = auth_app.oidc_config().unwrap();
let frontend_url = config.frontend_url.trim_end_matches('/'); let frontend_url = config.frontend_url.trim_end_matches('/');
let redirect_url = format!("{}/?oidc_code={}", frontend_url, exchange_code); let redirect_url = format!("{}/login?oidc_code={}", frontend_url, exchange_code);
tracing::info!("OIDC login successful, redirecting with exchange code"); tracing::info!("OIDC login successful, redirecting with exchange code");
Ok(Redirect::temporary(&redirect_url)) Ok(Redirect::temporary(&redirect_url))
} }