diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 32034bd731..90b44c43b7 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -70,12 +70,11 @@ exports.getUserParams = (email = "", otherProps = {}) => { * Generates a new configuration ID. * @returns {string} The new configuration ID which the config doc can be stored under. */ -exports.generateConfigID = (type = "", group = "") => { - group += SEPARATOR +exports.generateConfigID = (type = "", group = "", user = "") => { + // group += SEPARATOR + const scope = [type, group, user].join(SEPARATOR) - return `${ - DocumentTypes.CONFIG - }${SEPARATOR}${type}${SEPARATOR}${group}${newid()}` + return `${DocumentTypes.CONFIG}${SEPARATOR}${scope}${newid()}` } /** diff --git a/packages/auth/src/index.js b/packages/auth/src/index.js index 4c6ece1b62..4ef30c9f02 100644 --- a/packages/auth/src/index.js +++ b/packages/auth/src/index.js @@ -1,10 +1,10 @@ const passport = require("koa-passport") const LocalStrategy = require("passport-local").Strategy const JwtStrategy = require("passport-jwt").Strategy -// const GoogleStrategy = require("passport-google-oauth").Strategy +const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy const database = require("./db") -const { StaticDatabases } = require("./db/utils") -const { jwt, local, authenticated } = require("./middleware") +const { StaticDatabases, DocumentTypes } = require("./db/utils") +const { jwt, local, google, authenticated } = require("./middleware") const { Cookies, UserStatus } = require("./constants") const { hash, compare } = require("./hashing") const { @@ -27,7 +27,7 @@ const { // Strategies passport.use(new LocalStrategy(local.options, local.authenticate)) passport.use(new JwtStrategy(jwt.options, jwt.authenticate)) -// passport.use(new GoogleStrategy(google.options, google.authenticate)) +passport.use(new GoogleStrategy(google.options, google.authenticate)) passport.serializeUser((user, done) => done(null, user)) @@ -50,6 +50,7 @@ module.exports = { passport, Cookies, UserStatus, + DocumentTypes, StaticDatabases, generateUserID, getUserParams, diff --git a/packages/auth/src/middleware/passport/google.js b/packages/auth/src/middleware/passport/google.js index 6246e5e768..9113fba1cf 100644 --- a/packages/auth/src/middleware/passport/google.js +++ b/packages/auth/src/middleware/passport/google.js @@ -1,18 +1,54 @@ const env = require("../../environment") +const jwt = require("jsonwebtoken") +const database = require("../../db") +const { StaticDatabases, generateUserID } = require("../../db/utils") exports.options = { - clientId: env.GOOGLE_CLIENT_ID, + clientID: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, callbackURL: env.GOOGLE_AUTH_CALLBACK_URL, } exports.authenticate = async function(token, tokenSecret, profile, done) { - console.log({ - token, - tokenSecret, - profile, - done, + if (!profile._json.email) return done(null, false, "Email Required.") + + // Check the user exists in the instance DB by email + const db = new database.CouchDB(StaticDatabases.GLOBAL.name) + + let dbUser + const userId = generateUserID(profile._json.email) + + try { + // use the google profile id + dbUser = await db.get(userId) + } catch (err) { + console.error("Google user not found. Creating..") + // create the user + const user = { + _id: userId, + provider: profile.provider, + roles: {}, + builder: { + global: true, + }, + ...profile._json, + } + const response = await db.post(user) + + dbUser = user + dbUser._rev = response.rev + } + + // authenticate + const payload = { + userId: dbUser._id, + builder: dbUser.builder, + email: dbUser.email, + } + + dbUser.token = jwt.sign(payload, env.JWT_SECRET, { + expiresIn: "1 day", }) - // retrieve user ... - // fetchUser().then(user => done(null, user)) + + return done(null, dbUser) } diff --git a/packages/builder/src/components/login/LoginForm.svelte b/packages/builder/src/components/login/LoginForm.svelte index 7e32efb7c5..55d1ee3bf5 100644 --- a/packages/builder/src/components/login/LoginForm.svelte +++ b/packages/builder/src/components/login/LoginForm.svelte @@ -46,6 +46,7 @@ + Sign In with Google diff --git a/packages/worker/src/api/controllers/admin/configs.js b/packages/worker/src/api/controllers/admin/configs.js index e9fc8a3942..10f1d2cf2b 100644 --- a/packages/worker/src/api/controllers/admin/configs.js +++ b/packages/worker/src/api/controllers/admin/configs.js @@ -1,17 +1,47 @@ const CouchDB = require("../../../db") -const { StaticDatabases } = require("@budibase/auth") -const { generateConfigID } = require("@budibase/auth") -const { getConfigParams } = require("@budibase/auth/src/db/utils") +const { StaticDatabases, DocumentTypes } = require("@budibase/auth") +const { generateConfigID, getConfigParams } = require("@budibase/auth") +const { SEPARATOR } = require("@budibase/auth/src/db/utils") +const { Configs } = require("../../../constants") const GLOBAL_DB = StaticDatabases.GLOBAL.name +exports.configStatus = async function(ctx) { + const db = new CouchDB(GLOBAL_DB) + let configured = {} + + // check for super admin user + try { + configured.user = true + } catch (err) { + configured.user = false + } + + // check for SMTP config + try { + const response = await db.allDocs( + getConfigParams(`${DocumentTypes.CONFIG}${SEPARATOR}${Configs.SMTP}`) + ) + console.log(response) + configured.smtp = true + } catch (err) { + configured.smtp = false + } + + ctx.body = configured +} + exports.save = async function(ctx) { const db = new CouchDB(GLOBAL_DB) const configDoc = ctx.request.body // Config does not exist yet if (!configDoc._id) { - configDoc._id = generateConfigID(configDoc.type, configDoc.group) + configDoc._id = generateConfigID( + configDoc.type, + configDoc.group, + configDoc.user + ) } try { diff --git a/packages/worker/src/api/controllers/auth.js b/packages/worker/src/api/controllers/auth.js index 03589ab457..d2aaf552e0 100644 --- a/packages/worker/src/api/controllers/auth.js +++ b/packages/worker/src/api/controllers/auth.js @@ -1,4 +1,38 @@ -const { passport, Cookies, clearCookie } = require("@budibase/auth") +const { + passport, + Cookies, + StaticDatabases, + clearCookie, +} = require("@budibase/auth") +const CouchDB = require("../../db") + +const GLOBAL_DB = StaticDatabases.GLOBAL.name + +async function setToken(ctx) { + return async function(err, user) { + if (err) { + return ctx.throw(403, "Unauthorized") + } + + const expires = new Date() + expires.setDate(expires.getDate() + 1) + + if (!user) { + return ctx.throw(403, "Unauthorized") + } + + ctx.cookies.set(Cookies.Auth, user.token, { + expires, + path: "/", + httpOnly: false, + overwrite: true, + }) + + delete user.token + + ctx.body = { user } + } +} exports.authenticate = async (ctx, next) => { return passport.authenticate("local", async (err, user) => { @@ -31,10 +65,30 @@ exports.logout = async ctx => { ctx.body = { message: "User logged out" } } -exports.googleAuth = async () => { - // return passport.authenticate("google") -} +exports.googleAuth = async (ctx, next) => { + return passport.authenticate( + "google", + { successRedirect: "/", failureRedirect: "/" }, + async (err, user) => { + if (err) { + return ctx.throw(403, "Unauthorized") + } -exports.googleAuth = async () => { - // return passport.authenticate("google") + const expires = new Date() + expires.setDate(expires.getDate() + 1) + + if (!user) { + return ctx.throw(403, "Unauthorized") + } + + ctx.cookies.set(Cookies.Auth, user.token, { + expires, + path: "/", + httpOnly: false, + overwrite: true, + }) + + ctx.redirect("/") + } + )(ctx, next) } diff --git a/packages/worker/src/api/routes/admin/configs.js b/packages/worker/src/api/routes/admin/configs.js index 5b7354bfc3..0399026cf2 100644 --- a/packages/worker/src/api/routes/admin/configs.js +++ b/packages/worker/src/api/routes/admin/configs.js @@ -10,7 +10,7 @@ const router = Router() function buildConfigSaveValidation() { // prettier-ignore return joiValidator.body(Joi.object({ - type: Joi.string().valid(...Object.values(Configs)).required() + type: Joi.string().valid(...Object.values(Configs)).required(), }).required().unknown(true)) } @@ -21,6 +21,7 @@ router authenticated, controller.save ) + .post("/api/admin/config/status", controller.configStatus) .delete("/api/admin/configs/:id", authenticated, controller.destroy) .get("/api/admin/configs", authenticated, controller.fetch) .get("/api/admin/configs/:id", authenticated, controller.find) diff --git a/packages/worker/src/api/routes/auth.js b/packages/worker/src/api/routes/auth.js index deea678c63..ac87ef977a 100644 --- a/packages/worker/src/api/routes/auth.js +++ b/packages/worker/src/api/routes/auth.js @@ -1,19 +1,17 @@ const Router = require("@koa/router") const { passport } = require("@budibase/auth") const authController = require("../controllers/auth") +const context = require("koa/lib/context") const router = Router() router .post("/api/admin/auth", authController.authenticate) - .post("/api/admin/auth/logout", authController.logout) - .get("/api/auth/google", passport.authenticate("google")) .get( - "/api/auth/google/callback", - passport.authenticate("google", { - successRedirect: "/app", - failureRedirect: "/", - }) + "/api/admin/auth/google", + passport.authenticate("google", { scope: ["profile", "email"] }) ) + .get("/api/admin/auth/google/callback", authController.googleAuth) + .post("/api/admin/auth/logout", authController.logout) module.exports = router