diff --git a/fluxer_api/src/api/auth/services/SsoService.ts b/fluxer_api/src/api/auth/services/SsoService.ts index 63d5fbb1f..737c8b9e2 100644 --- a/fluxer_api/src/api/auth/services/SsoService.ts +++ b/fluxer_api/src/api/auth/services/SsoService.ts @@ -644,7 +644,7 @@ export class SsoService { } private async getOrCreateJwks(jwksUrl: string): Promise { - await this.validatePublicOutboundUrl(jwksUrl, 'jwks_url'); + await this.assertPublicOutboundUrl(jwksUrl, 'jwks_url'); const now = Date.now(); const cached = this.jwksCache.get(jwksUrl); 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 { + private async assertPublicOutboundUrl(rawUrl: string, fieldName: string): Promise { return validateSsoPublicOutboundUrl(rawUrl, fieldName); } @@ -902,8 +902,7 @@ export class SsoService { return null; } try { - const validUrl = await this.validatePublicOutboundUrl(rawUrl, fieldName); - return validUrl.toString(); + return await this.assertPublicOutboundUrl(rawUrl, fieldName); } catch (error) { this.logger.warn({fieldName, rawUrl, error}, 'Ignoring SSO URL that failed outbound policy validation'); return null; diff --git a/fluxer_api/src/api/instance/SsoConfigValidation.ts b/fluxer_api/src/api/instance/SsoConfigValidation.ts index 935ecdf01..da22780a4 100644 --- a/fluxer_api/src/api/instance/SsoConfigValidation.ts +++ b/fluxer_api/src/api/instance/SsoConfigValidation.ts @@ -86,7 +86,7 @@ export function normalizeSsoAllowedEmailDomains(domains: Array): Array { +export async function validateSsoPublicOutboundUrl(rawUrl: string, fieldName: string): Promise { let parsedUrl: URL; try { parsedUrl = new URL(rawUrl); @@ -104,7 +104,10 @@ export async function validateSsoPublicOutboundUrl(rawUrl: string, fieldName: st } catch { 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( @@ -116,8 +119,7 @@ async function normalizeOptionalSsoUrl( if (!normalized || skipValidation) { return normalized; } - const validUrl = await validateSsoPublicOutboundUrl(normalized, fieldName); - return validUrl.toString(); + return validateSsoPublicOutboundUrl(normalized, fieldName); } function resolveSsoReadiness(config: SsoConfigValidationInput, isTestProvider: boolean): boolean {