diff --git a/.changeset/sso-callback-urls.md b/.changeset/sso-callback-urls.md index 68886c161b..eb2391d70d 100644 --- a/.changeset/sso-callback-urls.md +++ b/.changeset/sso-callback-urls.md @@ -2,4 +2,4 @@ "@better-auth/sso": patch --- -Fix SSO discovery and SAML callback URLs for custom base paths, and prevent SAML logout self-redirects. +Preserve registered OIDC and SAML callback URLs and provider IDs when composing URLs. Report invalid discovery issuers as configuration errors and prevent SAML logout self-redirects under custom auth base paths. diff --git a/packages/sso/src/oidc/discovery.test.ts b/packages/sso/src/oidc/discovery.test.ts index 46f549b2f1..abddb31e22 100644 --- a/packages/sso/src/oidc/discovery.test.ts +++ b/packages/sso/src/oidc/discovery.test.ts @@ -62,6 +62,14 @@ const createMockDiscoveryDocument = ( describe("OIDC Discovery", () => { describe("computeDiscoveryUrl", () => { + it.each([ + "urn:example:id", + "not-a-url", + "https://idp.example.com/a\\b", + ])("reports an invalid issuer as a discovery error: %s", (issuer) => { + expect(() => computeDiscoveryUrl(issuer)).toThrow(DiscoveryError); + }); + it("should compute discovery URL from issuer without trailing slash", () => { const url = computeDiscoveryUrl("https://idp.example.com"); expect(url).toBe( @@ -90,9 +98,9 @@ describe("OIDC Discovery", () => { ); }); - it("does not leave a double slash after a trailing issuer path", () => { + it("preserves repeated slashes in the configured issuer path", () => { expect(computeDiscoveryUrl("https://idp.example.com/tenant/v1//")).toBe( - "https://idp.example.com/tenant/v1/.well-known/openid-configuration", + "https://idp.example.com/tenant/v1//.well-known/openid-configuration", ); }); }); diff --git a/packages/sso/src/oidc/discovery.ts b/packages/sso/src/oidc/discovery.ts index f334d13c80..3c782e9426 100644 --- a/packages/sso/src/oidc/discovery.ts +++ b/packages/sso/src/oidc/discovery.ts @@ -122,7 +122,16 @@ export async function discoverOIDCConfig( * Handles trailing slashes correctly. */ export function computeDiscoveryUrl(issuer: string): string { - return appendURLPath(issuer, "/.well-known/openid-configuration"); + try { + return appendURLPath(issuer, "/.well-known/openid-configuration"); + } catch (error) { + if (!(error instanceof TypeError)) throw error; + throw new DiscoveryError( + "discovery_invalid_url", + `The issuer URL "${issuer}" cannot be used for discovery.`, + { url: issuer }, + ); + } } /**