diff --git a/CHANGELOG.md b/CHANGELOG.md index a5e7a65c69..7ce63c6689 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the value of the holdings excluded from analysis in the portfolio summary +### Fixed + +- Fixed the discovery of the _OpenID Connect_ (`OIDC`) configuration for issuer URLs with a trailing slash (experimental) + ## 3.72.0 - 2026-09-20 ### Added diff --git a/apps/api/src/app/auth/auth.module.ts b/apps/api/src/app/auth/auth.module.ts index e2a71ca843..879463cc13 100644 --- a/apps/api/src/app/auth/auth.module.ts +++ b/apps/api/src/app/auth/auth.module.ts @@ -22,6 +22,7 @@ import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { GoogleStrategy } from './google.strategy'; import { JwtStrategy } from './jwt.strategy'; +import { getOidcDiscoveryUrl } from './oidc.helper'; import { OidcStrategy } from './oidc.strategy'; @Module({ @@ -94,9 +95,15 @@ import { OidcStrategy } from './oidc.strategy'; // Fetch OIDC configuration from discovery endpoint try { const response = await fetchService.fetch( - `${issuer}/.well-known/openid-configuration` + getOidcDiscoveryUrl(issuer) ); + if (!response.ok) { + throw new Error( + `OIDC discovery request failed with status ${response.status}` + ); + } + const config = (await response.json()) as { authorization_endpoint: string; token_endpoint: string; diff --git a/apps/api/src/app/auth/oidc.helper.spec.ts b/apps/api/src/app/auth/oidc.helper.spec.ts new file mode 100644 index 0000000000..9234f12c95 --- /dev/null +++ b/apps/api/src/app/auth/oidc.helper.spec.ts @@ -0,0 +1,20 @@ +import { getOidcDiscoveryUrl } from './oidc.helper'; + +describe('getOidcDiscoveryUrl', () => { + it.each([ + [ + 'https://auth.example.com', + 'https://auth.example.com/.well-known/openid-configuration' + ], + [ + 'https://auth.example.com/', + 'https://auth.example.com/.well-known/openid-configuration' + ], + [ + 'https://auth.example.com/application/o/ghostfolio/', + 'https://auth.example.com/application/o/ghostfolio/.well-known/openid-configuration' + ] + ])('creates the discovery URL for %s', (issuer, expected) => { + expect(getOidcDiscoveryUrl(issuer)).toBe(expected); + }); +}); diff --git a/apps/api/src/app/auth/oidc.helper.ts b/apps/api/src/app/auth/oidc.helper.ts new file mode 100644 index 0000000000..4e8de829fc --- /dev/null +++ b/apps/api/src/app/auth/oidc.helper.ts @@ -0,0 +1,4 @@ +export function getOidcDiscoveryUrl(issuer: string) { + // Remove trailing slashes + return `${issuer.replace(/\/+$/, '')}/.well-known/openid-configuration`; +}