auth: preserve issuer formatting for OIDC

This commit is contained in:
Arunavo Ray
2025-10-26 07:49:42 +05:30
parent a9dd646573
commit e41b4ffc56
5 changed files with 52 additions and 31 deletions

View File

@@ -24,6 +24,7 @@ describe("normalizeOidcProviderConfig", () => {
expect(result.oidcConfig.userInfoEndpoint).toBe("https://auth.example.com/userinfo");
expect(result.oidcConfig.scopes).toEqual(["openid", "email"]);
expect(result.oidcConfig.pkce).toBe(false);
expect(result.oidcConfig.discoveryEndpoint).toBe("https://auth.example.com/.well-known/openid-configuration");
});
it("derives missing fields from discovery", async () => {
@@ -46,6 +47,24 @@ describe("normalizeOidcProviderConfig", () => {
expect(result.oidcConfig.jwksEndpoint).toBe("https://auth.example.com/jwks");
expect(result.oidcConfig.userInfoEndpoint).toBe("https://auth.example.com/userinfo");
expect(result.oidcConfig.scopes).toEqual(["openid", "email", "profile"]);
expect(result.oidcConfig.discoveryEndpoint).toBe("https://auth.example.com/.well-known/openid-configuration");
});
it("preserves trailing slash issuers when building discovery endpoints", async () => {
const trailingIssuer = "https://auth.example.com/application/o/example/";
const requestedUrls: string[] = [];
const fetchMock: typeof fetch = async (url) => {
requestedUrls.push(typeof url === "string" ? url : url.url);
return new Response(JSON.stringify({
authorization_endpoint: "https://auth.example.com/application/o/example/auth",
token_endpoint: "https://auth.example.com/application/o/example/token",
}));
};
const result = await normalizeOidcProviderConfig(trailingIssuer, {}, fetchMock);
expect(requestedUrls[0]).toBe("https://auth.example.com/application/o/example/.well-known/openid-configuration");
expect(result.oidcConfig.discoveryEndpoint).toBe("https://auth.example.com/application/o/example/.well-known/openid-configuration");
});
it("throws for invalid issuer URL", async () => {

View File

@@ -131,18 +131,21 @@ export async function normalizeOidcProviderConfig(
throw new OidcConfigError("Issuer is required");
}
let normalizedIssuer: string;
const trimmedIssuer = issuer.trim();
try {
const issuerUrl = new URL(issuer.trim());
normalizedIssuer = issuerUrl.toString().replace(/\/$/, "");
// Validate issuer but keep caller-provided formatting so we don't break provider expectations
new URL(trimmedIssuer);
} catch {
throw new OidcConfigError(`Invalid issuer URL: ${issuer}`);
}
const issuerForDiscovery = trimmedIssuer.replace(/\/$/, "");
const discoveryEndpoint = cleanUrl(
rawConfig.discoveryEndpoint,
"discovery endpoint",
) ?? `${normalizedIssuer}/.well-known/openid-configuration`;
) ?? `${issuerForDiscovery}/.well-known/openid-configuration`;
const authorizationEndpoint = cleanUrl(rawConfig.authorizationEndpoint, "authorization endpoint");
const tokenEndpoint = cleanUrl(rawConfig.tokenEndpoint, "token endpoint");