UNPKG

@flowfuse/flowfuse

Version:

An open source low-code development platform

127 lines (121 loc) 5.64 kB
const fp = require('fastify-plugin') const { OAuth2Client } = require('google-auth-library') const USED_GOOGLE_JWT = 'platform-google-sso-tokens' const { generateUsernameFromEmail, generatePassword, completeSSOSignIn, completeUserSignup } = require('../../../../lib/userTeam') module.exports = fp(async function (app, opts) { app.caches.createCache(USED_GOOGLE_JWT, { ttl: (1000 * 60 * 60) // 1hr }) const googleJWTCache = app.caches.getCache(USED_GOOGLE_JWT) app.post('/ee/sso/login/callback/google', { config: { allowAnonymous: true } }, async (request, reply) => { const enabled = app.settings.get('platform:sso:google') if (!enabled) { reply.code(404).send({ code: 'not_found', error: 'Not Found' }) return } if (!request.query.code) { reply.code(400).send({ code: 'invalid_request', error: 'Missing code' }) return } const clientId = app.settings.get('platform:sso:google:clientId') if (!clientId) { reply.code(500).send({ code: 'invalid_request', error: 'Google SSO not configured' }) return } if (await googleJWTCache.get(request.query.code)) { reply.code(403).send({ code: 'not_allowed', error: 'Code already used' }) return } // request.user is the JWT provided by the Google SSO plugin // We need to decode and verify it. const googleOAuth2Client = new OAuth2Client(clientId) try { const tokenInfo = await googleOAuth2Client.getTokenInfo(request.query.code) if (tokenInfo.aud !== clientId) { reply.code(400).send({ code: 'invalid_request', error: 'Invalid code' }) return } // Now get the user info googleOAuth2Client.setCredentials({ access_token: request.query.code }) const userinfo = await googleOAuth2Client.request({ url: 'https://www.googleapis.com/oauth2/v3/userinfo' }) const googleUserInfo = userinfo.data await googleJWTCache.set(request.query.code, {}) if (googleUserInfo.hd) { // This is a Google Workspace account - check if there is an SSO provider configured for this domain const providerId = await app.sso.getProviderForEmail(googleUserInfo.email) if (!providerId) { // No SSO provider configured for this domain - redirect to home reply.send({ error: 'SSO not available for this Google Workspace domain' }) } else { // There is an SSO provider configured for this domain - use it reply.send({ url: `/ee/sso/login?u=${encodeURIComponent(googleUserInfo.email)}` }) } return } const user = await app.db.models.User.byUsernameOrEmail(googleUserInfo.email) if (user) { const result = await completeSSOSignIn(app, user) if (result.cookie) { reply.setCookie('sid', result.cookie.value, result.cookie.options) } reply.send({ url: '/' }) } else if (app.settings.get('platform:sso:google:auto-create') === true) { // Create a new user for this email address const userProperties = { name: googleUserInfo.name || googleUserInfo.email.split('@')[0], email: googleUserInfo.email, // Verified email from Google email_verified: true, // Generate a random password password: generatePassword(), // Explicitly don't create an admin user admin: false, sso_enabled: true } // Need to determine the username const username = await generateUsernameFromEmail(app, googleUserInfo.email) userProperties.username = username try { // - Create user in DB const newUser = await app.db.models.User.create(userProperties) // - Common sign-up completion - accepts invites etc if needed await completeUserSignup(app, newUser) // - Common sign-in completion - sets up session const result = await completeSSOSignIn(app, newUser) if (result.cookie) { reply.setCookie('sid', result.cookie.value, result.cookie.options) } reply.send({ url: '/' }) return } catch (err) { app.log.error(`Failed to create user via Google SSO: ${err}`) reply.send({ error: `Failed to create user via Google SSO: ${err}` }) } reply.send({ url: '/' }) } else { reply.send({ url: '/' }) } } catch (err) { app.log.error(`Google SSO failed: ${err}`) reply.code(500).send({ code: 'invalid_request', error: 'Invalid Request' }) } }) }, { name: 'app.ee.routes.sso.provider.google' })