Browse Source

Linting

pull/4023/head
Rory Powell 5 years ago
parent
commit
c16cfc328f
  1. 9
      packages/auth/src/middleware/passport/google.js
  2. 24
      packages/auth/src/middleware/passport/oidc.js
  3. 146
      packages/auth/src/middleware/passport/third-party-common.js
  4. 18
      packages/auth/src/middleware/passport/utils.js
  5. 20
      packages/builder/src/pages/builder/portal/manage/auth/index.svelte
  6. 4
      packages/worker/src/api/controllers/admin/auth.js

9
packages/auth/src/middleware/passport/google.js

@ -11,14 +11,15 @@ async function authenticate(accessToken, refreshToken, profile, done) {
email: profile._json.email, email: profile._json.email,
oauth2: { oauth2: {
accessToken: accessToken, accessToken: accessToken,
refreshToken: refreshToken refreshToken: refreshToken,
} },
} }
return authenticateThirdParty( return authenticateThirdParty(
thirdPartyUser, thirdPartyUser,
true, // require local accounts to exist true, // require local accounts to exist
done) done
)
} }
/** /**

24
packages/auth/src/middleware/passport/oidc.js

@ -12,7 +12,6 @@ const { authenticateThirdParty } = require("./third-party-common")
* @param {*} idToken The id_token - always a JWT * @param {*} idToken The id_token - always a JWT
* @param {*} params The response body from requesting an access_token * @param {*} params The response body from requesting an access_token
* @param {*} done The passport callback: err, user, info * @param {*} done The passport callback: err, user, info
* @returns
*/ */
async function authenticate( async function authenticate(
issuer, issuer,
@ -27,21 +26,22 @@ async function authenticate(
) { ) {
const thirdPartyUser = { const thirdPartyUser = {
// store the issuer info to enable sync in future // store the issuer info to enable sync in future
provider: issuer, provider: issuer,
providerType: "oidc", providerType: "oidc",
userId: profile.id, userId: profile.id,
profile: profile, profile: profile,
email: getEmail(profile, jwtClaims), email: getEmail(profile, jwtClaims),
oauth2: { oauth2: {
accessToken: accessToken, accessToken: accessToken,
refreshToken: refreshToken refreshToken: refreshToken,
} },
} }
return authenticateThirdParty( return authenticateThirdParty(
thirdPartyUser, thirdPartyUser,
false, // don't require local accounts to exist false, // don't require local accounts to exist
done) done
)
} }
/** /**
@ -65,12 +65,15 @@ function getEmail(profile, jwtClaims) {
return username return username
} }
return null; return null
} }
function validEmail(value) { function validEmail(value) {
return ( return (
(value && !!value.match(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/)) value &&
!!value.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
)
) )
} }
@ -92,7 +95,9 @@ exports.strategyFactory = async function (config, callbackUrl) {
const response = await fetch(configUrl) const response = await fetch(configUrl)
if (!response.ok) { if (!response.ok) {
throw new Error(`Unexpected response when fetching openid-configuration: ${response.statusText}`) throw new Error(
`Unexpected response when fetching openid-configuration: ${response.statusText}`
)
} }
const body = await response.json() const body = await response.json()
@ -110,7 +115,6 @@ exports.strategyFactory = async function (config, callbackUrl) {
}, },
authenticate authenticate
) )
} catch (err) { } catch (err) {
console.error(err) console.error(err)
throw new Error("Error constructing OIDC authentication strategy", err) throw new Error("Error constructing OIDC authentication strategy", err)

146
packages/auth/src/middleware/passport/third-party-common.js

@ -9,73 +9,83 @@ const {
const { authError } = require("./utils") const { authError } = require("./utils")
/** /**
* Common authentication logic for third parties. e.g. OAuth, OIDC. * Common authentication logic for third parties. e.g. OAuth, OIDC.
*/ */
exports.authenticateThirdParty = async function ( exports.authenticateThirdParty = async function (
thirdPartyUser, thirdPartyUser,
requireLocalAccount = true, requireLocalAccount = true,
done done
) { ) {
if (!thirdPartyUser.provider) return authError(done, "third party user provider required") if (!thirdPartyUser.provider)
if (!thirdPartyUser.userId) return authError(done, "third party user id required") return authError(done, "third party user provider required")
if (!thirdPartyUser.email) return authError(done, "third party user email required") if (!thirdPartyUser.userId)
return authError(done, "third party user id required")
const db = database.getDB(StaticDatabases.GLOBAL.name) if (!thirdPartyUser.email)
return authError(done, "third party user email required")
let dbUser
const db = database.getDB(StaticDatabases.GLOBAL.name)
// use the third party id
const userId = generateGlobalUserID(thirdPartyUser.userId) let dbUser
try { // use the third party id
dbUser = await db.get(userId) const userId = generateGlobalUserID(thirdPartyUser.userId)
} catch (err) {
// abort when not 404 error try {
if (!err.status || err.status !== 404) { dbUser = await db.get(userId)
return authError(done, "Unexpected error when retrieving existing user", err) } catch (err) {
} // abort when not 404 error
if (!err.status || err.status !== 404) {
// check user already exists by email return authError(
const users = await db.query(`database/${ViewNames.USER_BY_EMAIL}`, { done,
key: thirdPartyUser.email, "Unexpected error when retrieving existing user",
include_docs: true, err
}) )
const userExists = users.rows.length > 0
if (requireLocalAccount && !userExists) {
return authError(done, "Email does not yet exist. You must set up your local budibase account first.")
}
// create the user to save
let user
if (userExists) {
const existing = users.rows[0].doc
user = constructMergedUser(userId, existing, thirdPartyUser)
// remove the local account to avoid conflicts
await db.remove(existing._id, existing._rev)
} else {
user = constructNewUser(userId, thirdPartyUser)
}
// save the user
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, { // check user already exists by email
expiresIn: "1 day", const users = await db.query(`database/${ViewNames.USER_BY_EMAIL}`, {
key: thirdPartyUser.email,
include_docs: true,
}) })
const userExists = users.rows.length > 0
return done(null, dbUser)
if (requireLocalAccount && !userExists) {
return authError(
done,
"Email does not yet exist. You must set up your local budibase account first."
)
}
// create the user to save
let user
if (userExists) {
const existing = users.rows[0].doc
user = constructMergedUser(userId, existing, thirdPartyUser)
// remove the local account to avoid conflicts
await db.remove(existing._id, existing._rev)
} else {
user = constructNewUser(userId, thirdPartyUser)
}
// save the user
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)
} }
/** /**
@ -101,23 +111,23 @@ function constructNewUser(userId, thirdPartyUser) {
provider: thirdPartyUser.provider, provider: thirdPartyUser.provider,
providerType: thirdPartyUser.providerType, providerType: thirdPartyUser.providerType,
email: thirdPartyUser.email, email: thirdPartyUser.email,
roles: {} roles: {},
} }
// persist profile information // persist profile information
// @reviewers: Historically stored at the root level of the user // @reviewers: Historically stored at the root level of the user
// Nest to prevent conflicts with future fields // Nest to prevent conflicts with future fields
// Is this okay to change? // Is this okay to change?
if (thirdPartyUser.profile) { if (thirdPartyUser.profile) {
user.thirdPartyProfile = { user.thirdPartyProfile = {
...thirdPartyUser.profile._json ...thirdPartyUser.profile._json,
} }
} }
// persist oauth tokens for future use // persist oauth tokens for future use
if (thirdPartyUser.oauth2) { if (thirdPartyUser.oauth2) {
user.oauth2 = { user.oauth2 = {
...thirdPartyUser.oauth2 ...thirdPartyUser.oauth2,
} }
} }

18
packages/auth/src/middleware/passport/utils.js

@ -1,14 +1,14 @@
/** /**
* Utility to handle authentication errors. * Utility to handle authentication errors.
* *
* @param {*} done The passport callback. * @param {*} done The passport callback.
* @param {*} message Message that will be returned in the response body * @param {*} message Message that will be returned in the response body
* @param {*} err (Optional) error that will be logged * @param {*} err (Optional) error that will be logged
*/ */
exports.authError = function (done, message, err = null) { exports.authError = function (done, message, err = null) {
return done( return done(
err, err,
null, // never return a user null, // never return a user
{ message: message } { message: message }
) )
} }

20
packages/builder/src/pages/builder/portal/manage/auth/index.svelte

@ -85,7 +85,7 @@
let fileName = e.target.files[0].name let fileName = e.target.files[0].name
image = e.target.files[0] image = e.target.files[0]
providers.oidc.config["iconName"] = fileName providers.oidc.config["iconName"] = fileName
iconDropdownOptions.unshift({label: fileName, value: fileName}) iconDropdownOptions.unshift({ label: fileName, value: fileName })
} }
const providers = { google, oidc } const providers = { google, oidc }
@ -140,17 +140,17 @@
const configSettings = await res.json() const configSettings = await res.json()
if (configSettings.config) { if (configSettings.config) {
const logoKeys = Object.keys(configSettings.config) const logoKeys = Object.keys(configSettings.config)
logoKeys.map(logoKey => { logoKeys.map(logoKey => {
const logoUrl = configSettings.config[logoKey] const logoUrl = configSettings.config[logoKey]
iconDropdownOptions.unshift({ iconDropdownOptions.unshift({
label: logoKey, label: logoKey,
value: logoKey, value: logoKey,
icon: logoUrl, icon: logoUrl,
})
}) })
}) }
}
const oidcResponse = await api.get(`/api/admin/configs/${ConfigTypes.OIDC}`) const oidcResponse = await api.get(`/api/admin/configs/${ConfigTypes.OIDC}`)
const oidcDoc = await oidcResponse.json() const oidcDoc = await oidcResponse.json()

4
packages/worker/src/api/controllers/admin/auth.js

@ -14,14 +14,14 @@ const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name
function authInternal(ctx, user, err = null, info = null) { function authInternal(ctx, user, err = null, info = null) {
if (err) { if (err) {
console.error("Authentication error", err) console.error("Authentication error", err)
return ctx.throw(403, info? info : "Unauthorized") return ctx.throw(403, info ? info : "Unauthorized")
} }
const expires = new Date() const expires = new Date()
expires.setDate(expires.getDate() + 1) expires.setDate(expires.getDate() + 1)
if (!user) { if (!user) {
return ctx.throw(403, info? info : "Unauthorized") return ctx.throw(403, info ? info : "Unauthorized")
} }
ctx.cookies.set(Cookies.Auth, user.token, { ctx.cookies.set(Cookies.Auth, user.token, {

Loading…
Cancel
Save