diff --git a/packages/auth/src/middleware/passport/oidc.js b/packages/auth/src/middleware/passport/oidc.js index e695e085bf..69bf58243b 100644 --- a/packages/auth/src/middleware/passport/oidc.js +++ b/packages/auth/src/middleware/passport/oidc.js @@ -2,24 +2,6 @@ const fetch = require("node-fetch") const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy const { authenticateThirdParty } = require("./third-party-common") -/** - * @param {*} profile The structured profile created by passport using the user info endpoint - * @param {*} jwtClaims The claims returned in the id token - */ -function getEmail(profile, jwtClaims) { - // profile not guaranteed to contain email e.g. github connected azure ad account - if (profile._json.email) { - return profile._json.email - } - - // fallback to id token - if (jwtClaims.email) { - return jwtClaims.email - } - - return null; -} - /** * @param {*} issuer The identity provider base URL * @param {*} sub The user ID @@ -62,25 +44,52 @@ async function authenticate( done) } +/** + * @param {*} profile The structured profile created by passport using the user info endpoint + * @param {*} jwtClaims The claims returned in the id token + */ +function getEmail(profile, jwtClaims) { + // profile not guaranteed to contain email e.g. github connected azure ad account + if (profile._json.email) { + return profile._json.email + } + + // fallback to id token email + if (jwtClaims.email) { + return jwtClaims.email + } + + // fallback to id token preferred username + const username = jwtClaims.preferred_username + if (username && validEmail(username)) { + return username + } + + return null; +} + +function validEmail(value) { + return ( + (value && !!value.match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)) + ) +} + /** * Create an instance of the oidc passport strategy. This wrapper fetches the configuration * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * @returns Dynamically configured Passport OIDC Strategy */ -exports.strategyFactory = async function (callbackUrl) { +exports.strategyFactory = async function (config, callbackUrl) { try { - const configurationUrl = - "https://login.microsoftonline.com/2668c0dd-7ed2-4db3-b387-05b6f9204a70/v2.0/.well-known/openid-configuration" - const clientSecret = "g-ty~2iW.bo.88xj_QI6~hdc-H8mP2Xbnd" - const clientId = "bed2017b-2f53-42a9-8ef9-e58918935e07" + const { clientId, clientSecret, configUrl } = config - if (!clientId || !clientSecret || !callbackUrl || !configurationUrl) { + if (!clientId || !clientSecret || !callbackUrl || !configUrl) { throw new Error( - "Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configurationUrl" + "Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configUrl" ) } - const response = await fetch(configurationUrl) + const response = await fetch(configUrl) if (!response.ok) { throw new Error(`Unexpected response when fetching openid-configuration: ${response.statusText}`) diff --git a/packages/worker/src/api/controllers/admin/auth.js b/packages/worker/src/api/controllers/admin/auth.js index e6d553bc62..d1d8622d52 100644 --- a/packages/worker/src/api/controllers/admin/auth.js +++ b/packages/worker/src/api/controllers/admin/auth.js @@ -133,8 +133,16 @@ exports.googleAuth = async (ctx, next) => { } async function oidcStrategyFactory(ctx) { + const db = new CouchDB(GLOBAL_DB) + + const config = await authPkg.db.getScopedConfig(db, { + type: Configs.OIDC, + group: ctx.query.group, + }) + const callbackUrl = `${ctx.protocol}://${ctx.host}/api/admin/auth/oidc/callback` - return oidc.strategyFactory(callbackUrl) + + return oidc.strategyFactory(config, callbackUrl) } /**