From 2509116b28f260242334da39ed986b90bd25dccd Mon Sep 17 00:00:00 2001 From: Richard Henkenjohann Date: Wed, 11 Mar 2026 11:43:55 +0100 Subject: [PATCH] Fix Keycloak OIDC flow (#27716) Some OIDC providers like Keycloak (v18+) include an `iss` (issuer) parameter in the authentication response for security (as per RFC 9207). The oidc route was explicitly extracting only `code` and `state`, thus losing the `iss` parameter and other potential parameters like `session_state`. When Jackson passed the incomplete set of parameters to `openid-client`, the library failed validation with the error `invalid response encountered` because it expected the `iss` parameter to be present if the server supports it. Fixes #22238. Co-authored-by: Sahitya Chandra --- apps/web/app/api/auth/oidc/route.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/web/app/api/auth/oidc/route.ts b/apps/web/app/api/auth/oidc/route.ts index 693ae9a203..30799e3c70 100644 --- a/apps/web/app/api/auth/oidc/route.ts +++ b/apps/web/app/api/auth/oidc/route.ts @@ -9,20 +9,18 @@ import logger from "@calcom/lib/logger"; // This is the callback endpoint for the OIDC provider // A team must set this endpoint in the OIDC provider's configuration async function handler(req: NextRequest) { - const log = logger.getSubLogger({ prefix: ["[ODIC auth]"] }); + const log = logger.getSubLogger({ prefix: ["[OIDC auth]"] }); const { searchParams } = req.nextUrl; - const code = searchParams.get("code"); - const state = searchParams.get("state"); - const tenant = searchParams.get("tenant"); + const params = Object.fromEntries(searchParams.entries()); - if (!code || !state) { + if (!params.code || !params.state) { return NextResponse.json({ message: "Code and state are required" }, { status: 400 }); } const { oauthController } = await jackson(); try { - const { redirect_url } = await oauthController.oidcAuthzResponse({ code, state }); + const { redirect_url } = await oauthController.oidcAuthzResponse(params); if (!redirect_url) { throw new HttpError({ @@ -33,7 +31,7 @@ async function handler(req: NextRequest) { return NextResponse.redirect(redirect_url, 302); } catch (err) { - log.error(`Error authorizing tenant ${tenant}: ${err}`); + log.error(`Error authorizing tenant ${params.tenant}: ${err}`); const { message, statusCode = 500 } = err as HttpError; return NextResponse.json({ message }, { status: statusCode });