@@ -29,9 +29,6 @@ import passport from 'passport';
2929import { MultiSamlStrategy } from '@node-saml/passport-saml' ;
3030import * as oidcClient from 'openid-client' ;
3131
32- function normalizeIssuerUrl ( raw : string ) : string {
33- return `https://${ raw . replace ( / ^ h t t p s ? : \/ \/ / , '' ) . replace ( / \/ $ / , '' ) } ` ;
34- }
3532import {
3633 makeLoginIncorrectPasswordError ,
3734 makeLoginSsoRequiredError ,
@@ -102,6 +99,38 @@ async function getCPUUsage() {
10299 return 1 - ( endIdle - startIdle ) / ( endTotal - startTotal ) ;
103100}
104101
102+ function normalizeIssuerUrl ( raw : string ) : string {
103+ return `https://${ raw . replace ( / ^ h t t p s ? : \/ \/ / , '' ) . replace ( / \/ $ / , '' ) } ` ;
104+ }
105+
106+ async function discoverOidcConfig (
107+ issuerUrl : string ,
108+ clientId : string ,
109+ clientSecret : string ,
110+ ) {
111+ const config = await oidcClient . discovery (
112+ new URL ( issuerUrl ) ,
113+ clientId ,
114+ clientSecret ,
115+ ) ;
116+
117+ const supported = config . serverMetadata ( ) . token_endpoint_auth_methods_supported ;
118+
119+ // Per OIDC spec, if token_endpoint_auth_methods_supported is absent the
120+ // default is ["client_secret_basic"]. If the server explicitly lists methods
121+ // and does NOT include client_secret_post, re-discover with ClientSecretBasic.
122+ if ( supported && ! supported . includes ( 'client_secret_post' ) ) {
123+ return oidcClient . discovery (
124+ new URL ( issuerUrl ) ,
125+ clientId ,
126+ clientSecret ,
127+ oidcClient . ClientSecretBasic ( clientSecret ) ,
128+ ) ;
129+ }
130+
131+ return config ;
132+ }
133+
105134// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
106135const env = process . env . NODE_ENV || 'development' ;
107136const sessionStore = connectPgSimple ( session ) ;
@@ -307,12 +336,7 @@ export default async function makeApiServer(deps: Dependencies) {
307336 const issuerUrl = normalizeIssuerUrl ( oidcSettings . issuer_url ) ;
308337
309338 const { API_BASE_URL } = process . env ;
310- const config = await oidcClient . discovery (
311- new URL ( issuerUrl ) ,
312- oidcSettings . client_id ,
313- oidcSettings . client_secret ,
314- oidcClient . ClientSecretBasic ( oidcSettings . client_secret ) ,
315- ) ;
339+ const config = await discoverOidcConfig ( issuerUrl , oidcSettings . client_id , oidcSettings . client_secret ) ;
316340
317341 // Reconstruct callback URL with code/state from IdP redirect.
318342 // authorizationCodeGrant needs: base = registered redirect_uri, query = code+state from IdP.
@@ -372,12 +396,7 @@ export default async function makeApiServer(deps: Dependencies) {
372396 const callbackUrl = deps . SSOService . getSSOOidcCallbackUrl ( ) ;
373397 const issuerUrl = normalizeIssuerUrl ( oidcSettings . issuer_url ) ;
374398
375- const config = await oidcClient . discovery (
376- new URL ( issuerUrl ) ,
377- oidcSettings . client_id ,
378- oidcSettings . client_secret ,
379- oidcClient . ClientSecretBasic ( oidcSettings . client_secret ) ,
380- ) ;
399+ const config = await discoverOidcConfig ( issuerUrl , oidcSettings . client_id , oidcSettings . client_secret ) ;
381400
382401 const codeVerifier = oidcClient . randomPKCECodeVerifier ( ) ;
383402 const codeChallenge = await oidcClient . calculatePKCECodeChallenge ( codeVerifier ) ;
0 commit comments