diff --git a/.changeset/sso-oidc-error-url-encoding.md b/.changeset/sso-oidc-error-url-encoding.md new file mode 100644 index 0000000000..844169c6aa --- /dev/null +++ b/.changeset/sso-oidc-error-url-encoding.md @@ -0,0 +1,5 @@ +--- +"@better-auth/sso": patch +--- + +SSO OIDC callback now URL-encodes the `error` query value when redirecting on `handleOAuthUserInfo` errors (e.g. `?error=signup+disabled` instead of `?error=signup disabled`). Multi-word error values previously produced broken URLs with raw whitespace. diff --git a/packages/sso/src/oidc.test.ts b/packages/sso/src/oidc.test.ts index 56376454e9..765d395d8e 100644 --- a/packages/sso/src/oidc.test.ts +++ b/packages/sso/src/oidc.test.ts @@ -588,7 +588,10 @@ describe("SSO disable implicit sign in", async () => { "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest", ); const { callbackURL } = await simulateOAuthFlow(res.url, headers); - expect(callbackURL).toContain("/api/auth/error?error=signup disabled"); + expect(callbackURL).not.toContain("error=signup disabled"); + const url = new URL(callbackURL); + expect(url.pathname).toBe("/api/auth/error"); + expect(url.searchParams.get("error")).toBe("signup disabled"); }); it("should create user with SSO provider when sign ups are disabled but sign up is requested", async () => { diff --git a/packages/sso/src/routes/sso.ts b/packages/sso/src/routes/sso.ts index cf546dc4bb..130f1d845d 100644 --- a/packages/sso/src/routes/sso.ts +++ b/packages/sso/src/routes/sso.ts @@ -1693,7 +1693,10 @@ async function handleOIDCCallback( throw e; } if (linked.error) { - throw ctx.redirect(`${errorURL || callbackURL}?error=${linked.error}`); + const baseURL = errorURL || callbackURL; + const params = new URLSearchParams({ error: linked.error }); + const sep = baseURL.includes("?") ? "&" : "?"; + throw ctx.redirect(`${baseURL}${sep}${params.toString()}`); } const { session, user } = linked.data!;