From 29295e6d365c5eb5c2532194033afaf1e2e58ee2 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 21 Sep 2021 12:27:53 +0100 Subject: [PATCH 1/2] Self hosted SSO flow and account deletion --- packages/auth/src/index.js | 2 ++ packages/auth/src/middleware/index.js | 2 ++ packages/auth/src/middleware/passport/google.js | 8 ++++++-- packages/auth/src/security/sessions.js | 4 ++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/auth/src/index.js b/packages/auth/src/index.js index 569456ea10..4aa2c8ab96 100644 --- a/packages/auth/src/index.js +++ b/packages/auth/src/index.js @@ -12,6 +12,7 @@ const { auditLog, tenancy, appTenancy, + authError, } = require("./middleware") const { setDB } = require("./db") const userCache = require("./cache/user") @@ -60,6 +61,7 @@ module.exports = { buildTenancyMiddleware: tenancy, buildAppTenancyMiddleware: appTenancy, auditLog, + authError, }, cache: { user: userCache, diff --git a/packages/auth/src/middleware/index.js b/packages/auth/src/middleware/index.js index 059f20af8b..cf8676a2bc 100644 --- a/packages/auth/src/middleware/index.js +++ b/packages/auth/src/middleware/index.js @@ -2,6 +2,7 @@ const jwt = require("./passport/jwt") const local = require("./passport/local") const google = require("./passport/google") const oidc = require("./passport/oidc") +const { authError } = require("./passport/utils") const authenticated = require("./authenticated") const auditLog = require("./auditLog") const tenancy = require("./tenancy") @@ -16,4 +17,5 @@ module.exports = { auditLog, tenancy, appTenancy, + authError, } diff --git a/packages/auth/src/middleware/passport/google.js b/packages/auth/src/middleware/passport/google.js index 07d6816c0b..cb93844c31 100644 --- a/packages/auth/src/middleware/passport/google.js +++ b/packages/auth/src/middleware/passport/google.js @@ -27,7 +27,11 @@ async function authenticate(accessToken, refreshToken, profile, done) { * from couchDB rather than environment variables, using this factory is necessary for dynamically configuring passport. * @returns Dynamically configured Passport Google Strategy */ -exports.strategyFactory = async function (config, callbackUrl) { +exports.strategyFactory = async function ( + config, + callbackUrl, + verify = authenticate +) { try { const { clientID, clientSecret } = config @@ -43,7 +47,7 @@ exports.strategyFactory = async function (config, callbackUrl) { clientSecret: config.clientSecret, callbackURL: callbackUrl, }, - authenticate + verify ) } catch (err) { console.error(err) diff --git a/packages/auth/src/security/sessions.js b/packages/auth/src/security/sessions.js index 328f74c794..83ca9d9bcd 100644 --- a/packages/auth/src/security/sessions.js +++ b/packages/auth/src/security/sessions.js @@ -30,6 +30,10 @@ exports.invalidateSessions = async (userId, sessionId = null) => { sessions.push({ key: makeSessionID(userId, sessionId) }) } else { sessions = await getSessionsForUser(userId) + sessions.forEach( + session => + (session.key = makeSessionID(session.userId, session.sessionId)) + ) } const client = await redis.getSessionClient() const promises = [] From 9282b08890fba1f0fb9d6a4ef19bbd29f852d1b0 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Tue, 21 Sep 2021 18:20:26 +0100 Subject: [PATCH 2/2] Cloud SSO flow and auto login on verification --- .../src/api/controllers/global/users.js | 28 +++++++++++++------ .../worker/src/api/routes/global/users.js | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index 8f754e2922..c8382d4189 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -31,7 +31,12 @@ async function allUsers() { return response.rows.map(row => row.doc) } -async function saveUser(user, tenantId, hashPassword = true) { +async function saveUser( + user, + tenantId, + hashPassword = true, + requirePassword = true +) { if (!tenantId) { throw "No tenancy specified." } @@ -57,7 +62,7 @@ async function saveUser(user, tenantId, hashPassword = true) { hashedPassword = hashPassword ? await hash(password) : password } else if (dbUser) { hashedPassword = dbUser.password - } else { + } else if (requirePassword) { throw "Password must be specified." } @@ -106,16 +111,21 @@ exports.save = async ctx => { } } +const parseBooleanParam = param => { + if (param && param == "false") { + return false + } else { + return true + } +} + exports.adminUser = async ctx => { const { email, password, tenantId } = ctx.request.body // account portal sends a pre-hashed password - honour param to prevent double hashing - let hashPassword = ctx.request.query.hashPassword - if (hashPassword && hashPassword == "false") { - hashPassword = false - } else { - hashPassword = true - } + const hashPassword = parseBooleanParam(ctx.request.query.hashPassword) + // account portal sends no password for SSO users + const requirePassword = parseBooleanParam(ctx.request.query.requirePassword) if (await doesTenantExist(tenantId)) { ctx.throw(403, "Organisation already exists.") @@ -148,7 +158,7 @@ exports.adminUser = async ctx => { tenantId, } try { - ctx.body = await saveUser(user, tenantId, hashPassword) + ctx.body = await saveUser(user, tenantId, hashPassword, requirePassword) } catch (err) { ctx.throw(err.status || 400, err) } diff --git a/packages/worker/src/api/routes/global/users.js b/packages/worker/src/api/routes/global/users.js index 9af249260d..1a04944a30 100644 --- a/packages/worker/src/api/routes/global/users.js +++ b/packages/worker/src/api/routes/global/users.js @@ -10,7 +10,7 @@ function buildAdminInitValidation() { return joiValidator.body( Joi.object({ email: Joi.string().required(), - password: Joi.string().required(), + password: Joi.string(), tenantId: Joi.string().required(), }) .required()