google auth E2E

This commit is contained in:
Martin McKeaveney 2021-04-21 12:12:22 +01:00
parent 1b54845c5e
commit b61a29f2ff
8 changed files with 155 additions and 35 deletions

View File

@ -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()}`
}
/**

View File

@ -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,

View File

@ -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,
})
// retrieve user ...
// fetchUser().then(user => done(null, user))
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",
})
return done(null, dbUser)
}

View File

@ -46,6 +46,7 @@
<Input outline type="password" on:change bind:value={password} />
<Spacer large />
<Button primary on:click={login}>Login</Button>
<a target="_blank" href="/api/admin/auth/google">Sign In with Google</a>
<Button secondary on:click={createTestUser}>Create Test User</Button>
</form>

View File

@ -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 {

View File

@ -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)
}

View File

@ -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)

View File

@ -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