fix: SSO Issuer URL formatted incorrectly (#1096)

Co-authored-by: Jiralite <[email protected]>
This commit is contained in:
Jason ZherdevandJiralite authored and GitHub committed 2026-07-01 17:40:00 +01:00
1 parent 34ef4df925
commit 4a518b8e68
2 files changed
+9 -8

No files matched your search

@@ -644,7 +644,7 @@ export class SsoService {
} }
private async getOrCreateJwks(jwksUrl: string): Promise<RemoteJwkSetResolver> { private async getOrCreateJwks(jwksUrl: string): Promise<RemoteJwkSetResolver> {
await this.validatePublicOutboundUrl(jwksUrl, 'jwks_url'); await this.assertPublicOutboundUrl(jwksUrl, 'jwks_url');
const now = Date.now(); const now = Date.now();
const cached = this.jwksCache.get(jwksUrl); const cached = this.jwksCache.get(jwksUrl);
if (cached && now - cached.cachedAt < SsoService.JWKS_CACHE_TTL_MS) { if (cached && now - cached.cachedAt < SsoService.JWKS_CACHE_TTL_MS) {
@@ -893,7 +893,7 @@ export class SsoService {
}; };
} }
private async validatePublicOutboundUrl(rawUrl: string, fieldName: string): Promise<URL> { private async assertPublicOutboundUrl(rawUrl: string, fieldName: string): Promise<string> {
return validateSsoPublicOutboundUrl(rawUrl, fieldName); return validateSsoPublicOutboundUrl(rawUrl, fieldName);
} }
@@ -902,8 +902,7 @@ export class SsoService {
return null; return null;
} }
try { try {
const validUrl = await this.validatePublicOutboundUrl(rawUrl, fieldName); return await this.assertPublicOutboundUrl(rawUrl, fieldName);
return validUrl.toString();
} catch (error) { } catch (error) {
this.logger.warn({fieldName, rawUrl, error}, 'Ignoring SSO URL that failed outbound policy validation'); this.logger.warn({fieldName, rawUrl, error}, 'Ignoring SSO URL that failed outbound policy validation');
return null; return null;
@@ -86,7 +86,7 @@ export function normalizeSsoAllowedEmailDomains(domains: Array<string>): Array<s
return Array.from(normalized); return Array.from(normalized);
} }
export async function validateSsoPublicOutboundUrl(rawUrl: string, fieldName: string): Promise<URL> { export async function validateSsoPublicOutboundUrl(rawUrl: string, fieldName: string): Promise<string> {
let parsedUrl: URL; let parsedUrl: URL;
try { try {
parsedUrl = new URL(rawUrl); parsedUrl = new URL(rawUrl);
@@ -104,7 +104,10 @@ export async function validateSsoPublicOutboundUrl(rawUrl: string, fieldName: st
} catch { } catch {
throw InputValidationError.fromCode(fieldName, ValidationErrorCodes.INVALID_URL_FORMAT); throw InputValidationError.fromCode(fieldName, ValidationErrorCodes.INVALID_URL_FORMAT);
} }
return parsedUrl; // Return the caller's exact input rather than parsedUrl.toString(), which would normalize
// the URL (e.g. appending a trailing slash) and break exact-match comparisons such as the
// OIDC issuer check in jwtVerify. Invalid URLs have already thrown above.
return rawUrl;
} }
async function normalizeOptionalSsoUrl( async function normalizeOptionalSsoUrl(
@@ -116,8 +119,7 @@ async function normalizeOptionalSsoUrl(
if (!normalized || skipValidation) { if (!normalized || skipValidation) {
return normalized; return normalized;
} }
const validUrl = await validateSsoPublicOutboundUrl(normalized, fieldName); return validateSsoPublicOutboundUrl(normalized, fieldName);
return validUrl.toString();
} }
function resolveSsoReadiness(config: SsoConfigValidationInput, isTestProvider: boolean): boolean { function resolveSsoReadiness(config: SsoConfigValidationInput, isTestProvider: boolean): boolean {