From 7803540399de51785523efebaef69351b8c13844 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 5 Jul 2021 17:16:45 +0100 Subject: [PATCH] Use configuration url to retrieve oidc endpoints The /.well-known/openid-configuration endpoint can be used to retrieve the majority of configuration needed for oidc Additionally refactor the callback url to be generated on the server side as this is a fixed endpoint. Add linting fixes --- packages/auth/src/index.js | 9 ++- packages/auth/src/middleware/passport/oidc.js | 68 ++++++++++++------- .../auth/_components/OIDCButton.svelte | 11 ++- .../worker/src/api/controllers/admin/auth.js | 13 +++- packages/worker/src/index.js | 2 +- 5 files changed, 65 insertions(+), 38 deletions(-) diff --git a/packages/auth/src/index.js b/packages/auth/src/index.js index cb4cb8d550..c56c5c5a05 100644 --- a/packages/auth/src/index.js +++ b/packages/auth/src/index.js @@ -2,7 +2,14 @@ const passport = require("koa-passport") const LocalStrategy = require("passport-local").Strategy const JwtStrategy = require("passport-jwt").Strategy const { StaticDatabases } = require("./db/utils") -const { jwt, local, authenticated, google, oidc, auditLog } = require("./middleware") +const { + jwt, + local, + authenticated, + google, + oidc, + auditLog, +} = require("./middleware") const { setDB, getDB } = require("./db") // Strategies diff --git a/packages/auth/src/middleware/passport/oidc.js b/packages/auth/src/middleware/passport/oidc.js index 09c7e2a05e..78a11784e4 100644 --- a/packages/auth/src/middleware/passport/oidc.js +++ b/packages/auth/src/middleware/passport/oidc.js @@ -1,6 +1,7 @@ const env = require("../../environment") const jwt = require("jsonwebtoken") const database = require("../../db") +const fetch = require("node-fetch") const OIDCStrategy = require("@techpass/passport-openidconnect").Strategy const { StaticDatabases, @@ -9,7 +10,17 @@ const { } = require("../../db/utils") // async function authenticate(token, tokenSecret, profile, done) { -async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refreshToken, idToken, params, done) { +async function authenticate( + issuer, + sub, + profile, + jwtClaims, + accessToken, + refreshToken, + idToken, + params, + done +) { // Check the user exists in the instance DB by email const db = database.getDB(StaticDatabases.GLOBAL.name) @@ -18,7 +29,7 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres const userId = generateGlobalUserID(profile.id) try { - // use the google profile id + // use the OIDC profile id dbUser = await db.get(userId) } catch (err) { const user = { @@ -28,13 +39,13 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres ...profile._json, } - // check if an account with the google email address exists locally + // check if an account with the OIDC email address exists locally const users = await db.query(`database/${ViewNames.USER_BY_EMAIL}`, { key: profile._json.email, include_docs: true, }) - // Google user already exists by email + // OIDC user already exists by email if (users.rows.length > 0) { const existing = users.rows[0].doc @@ -74,36 +85,41 @@ async function authenticate(issuer, sub, profile, jwtClaims, accessToken, refres } /** - * Create an instance of the google passport strategy. This wrapper fetches the configuration + * 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 Google Strategy + * @returns Dynamically configured Passport OIDC Strategy */ -exports.strategyFactory = async function () { +exports.strategyFactory = async function (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, callbackURL } = config - - if (!clientID || !clientSecret || !callbackURL) { + if (!clientId || !clientSecret || !callbackUrl || !configurationUrl) { throw new Error( - "Configuration invalid. Must contain google clientID, clientSecret and callbackURL" + "Configuration invalid. Must contain clientID, clientSecret, callbackUrl and configurationUrl" ) } - */ - return new OIDCStrategy( - { - issuer: "https://base.uri/auth/realms/realm_name", - authorizationURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/auth", - tokenURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/token", - userInfoURL: "https://base.uri/auth/realms/realm_name/protocol/openid-connect/userinfo", - clientID: "my_client_id", - clientSecret: "my_client_secret", - callbackURL: "http://localhost:10000/api/admin/auth/oidc/callback", - scope: "openid profile email", - }, - authenticate - ) + const response = await fetch(configurationUrl) + if (response.ok) { + const body = await response.json() + + return new OIDCStrategy( + { + issuer: body.issuer, + authorizationURL: body.authorization_endpoint, + tokenURL: body.token_endpoint, + userInfoURL: body.userinfo_endpoint, + clientID: clientId, + clientSecret: clientSecret, + callbackURL: callbackUrl, + scope: "profile email", + }, + authenticate + ) + } } catch (err) { console.error(err) throw new Error("Error constructing OIDC authentication strategy", err) diff --git a/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte b/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte index 98f5d3efb9..fbd67a6437 100644 --- a/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte +++ b/packages/builder/src/pages/builder/auth/_components/OIDCButton.svelte @@ -1,15 +1,12 @@ {#if show} - window.open("/api/admin/auth/oidc", "_blank")} - > + window.open("/api/admin/auth/oidc", "_blank")}>

Sign in with OIDC

@@ -25,10 +22,10 @@ padding-top: var(--spacing-xs); padding-bottom: var(--spacing-xs); } - .inner img { + /* .inner img { width: 18px; margin: 3px 10px 3px 3px; - } + } */ .inner p { margin: 0; } diff --git a/packages/worker/src/api/controllers/admin/auth.js b/packages/worker/src/api/controllers/admin/auth.js index 374aa5c47d..3c0f76c575 100644 --- a/packages/worker/src/api/controllers/admin/auth.js +++ b/packages/worker/src/api/controllers/admin/auth.js @@ -131,10 +131,17 @@ exports.googleAuth = async (ctx, next) => { )(ctx, next) } -// Minimal OIDC attempt +async function oidcStrategyFactory(ctx) { + const callbackUrl = `${ctx.protocol}://${ctx.host}/api/admin/auth/oidc/callback` + return oidc.strategyFactory(callbackUrl) +} +/** + * The initial call that OIDC authentication makes to take you to the configured OIDC login screen. + * On a successful login, you will be redirected to the oidcAuth callback route. + */ exports.oidcPreAuth = async (ctx, next) => { - const strategy = await oidc.strategyFactory() + const strategy = await oidcStrategyFactory(ctx) return passport.authenticate(strategy, { scope: ["profile", "email"], @@ -142,7 +149,7 @@ exports.oidcPreAuth = async (ctx, next) => { } exports.oidcAuth = async (ctx, next) => { - const strategy = await oidc.strategyFactory() + const strategy = await oidcStrategyFactory(ctx) return passport.authenticate( strategy, diff --git a/packages/worker/src/index.js b/packages/worker/src/index.js index 4e105a1435..8af1380552 100644 --- a/packages/worker/src/index.js +++ b/packages/worker/src/index.js @@ -14,7 +14,7 @@ const redis = require("./utilities/redis") const app = new Koa() -app.keys = ['secret', 'key']; +app.keys = ["secret", "key"] // set up top level koa middleware app.use(koaBody({ multipart: true }))