diff --git a/api/strategies/openidStrategy.js b/api/strategies/openidStrategy.js index 575521d3be..ec22b51743 100644 --- a/api/strategies/openidStrategy.js +++ b/api/strategies/openidStrategy.js @@ -99,6 +99,7 @@ class CustomOpenIDStrategy extends OpenIDStrategy { const hostAndProtocol = process.env.DOMAIN_SERVER; return new URL(`${hostAndProtocol}${req.originalUrl ?? req.url}`); } + authorizationRequestParams(req, options) { const params = super.authorizationRequestParams(req, options); if (options?.state && !params.has('state')) { @@ -112,6 +113,15 @@ class CustomOpenIDStrategy extends OpenIDStrategy { ); } + /** Generate nonce for federated providers that require it */ + const shouldGenerateNonce = isEnabled(process.env.OPENID_GENERATE_NONCE); + if (shouldGenerateNonce && !params.has('nonce') && this._sessionKey) { + const crypto = require('crypto'); + const nonce = crypto.randomBytes(16).toString('hex'); + params.set('nonce', nonce); + logger.debug('[openidStrategy] Generated nonce for federated provider:', nonce); + } + return params; } } @@ -276,12 +286,20 @@ function convertToUsername(input, defaultValue = '') { */ async function setupOpenId() { try { + const shouldGenerateNonce = isEnabled(process.env.OPENID_GENERATE_NONCE); + /** @type {ClientMetadata} */ const clientMetadata = { client_id: process.env.OPENID_CLIENT_ID, client_secret: process.env.OPENID_CLIENT_SECRET, }; + if (shouldGenerateNonce) { + clientMetadata.response_types = ['code']; + clientMetadata.grant_types = ['authorization_code']; + clientMetadata.token_endpoint_auth_method = 'client_secret_post'; + } + /** @type {Configuration} */ openidConfig = await client.discovery( new URL(process.env.OPENID_ISSUER), @@ -297,11 +315,19 @@ async function setupOpenId() { const requiredRoleParameterPath = process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH; const requiredRoleTokenKind = process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND; const usePKCE = isEnabled(process.env.OPENID_USE_PKCE); + logger.info(`[openidStrategy] OpenID authentication configuration`, { + generateNonce: shouldGenerateNonce, + reason: shouldGenerateNonce + ? 'OPENID_GENERATE_NONCE=true - Will generate nonce and use explicit metadata for federated providers' + : 'OPENID_GENERATE_NONCE=false - Standard flow without explicit nonce or metadata', + }); + const openidLogin = new CustomOpenIDStrategy( { config: openidConfig, scope: process.env.OPENID_SCOPE, callbackURL: process.env.DOMAIN_SERVER + process.env.OPENID_CALLBACK_URL, + clockTolerance: process.env.OPENID_CLOCK_TOLERANCE || 300, usePKCE, }, async (tokenset, done) => {