1
0
Fork 0
mirror of synced 2024-06-30 20:10:54 +12:00
budibase/packages/worker/src/api/controllers/global/auth.ts

288 lines
7.5 KiB
TypeScript
Raw Normal View History

import {
auth as authCore,
constants,
context,
events,
utils as utilsCore,
configs,
} from "@budibase/backend-core"
import {
ConfigType,
User,
Ctx,
LoginRequest,
SSOUser,
PasswordResetRequest,
PasswordResetUpdateRequest,
GoogleInnerConfig,
2023-12-06 07:17:27 +13:00
DatasourceAuthCookie,
} from "@budibase/types"
import env from "../../../environment"
import * as authSdk from "../../../sdk/auth"
import * as userSdk from "../../../sdk/users"
const { Cookie, Header } = constants
const { passport, ssoCallbackUrl, google, oidc } = authCore
const { setCookie, getCookie, clearCookie } = utilsCore
// LOGIN / LOGOUT
2021-07-23 02:26:14 +12:00
async function passportCallback(
ctx: Ctx,
user: User,
err: any = null,
info: { message: string } | null = null
) {
if (err) {
2023-02-16 04:10:02 +13:00
console.error("Authentication error")
console.error(err)
console.trace(err)
2021-07-09 00:12:25 +12:00
return ctx.throw(403, info ? info : "Unauthorized")
}
if (!user) {
2023-02-16 04:10:02 +13:00
console.error("Authentication error - no user provided")
2021-07-09 00:12:25 +12:00
return ctx.throw(403, info ? info : "Unauthorized")
}
2021-04-12 21:47:48 +12:00
const token = await authSdk.loginUser(user)
// set a cookie for browser access
setCookie(ctx, token, Cookie.Auth, { sign: false })
// set the token in a header as well for APIs
ctx.set(Header.TOKEN, token)
}
export const login = async (ctx: Ctx<LoginRequest>, next: any) => {
const email = ctx.request.body.username
const user = await userSdk.db.getUserByEmail(email)
if (user && (await userSdk.db.isPreventPasswordActions(user))) {
ctx.throw(403, "Invalid credentials")
}
return passport.authenticate(
"local",
2022-07-04 23:54:26 +12:00
async (err: any, user: User, info: any) => {
await passportCallback(ctx, user, err, info)
await context.identity.doInUserContext(user, ctx, async () => {
await events.auth.login("local", user.email)
2022-05-24 20:54:36 +12:00
})
ctx.status = 200
}
)(ctx, next)
}
2021-04-11 22:35:55 +12:00
export const logout = async (ctx: any) => {
if (ctx.user && ctx.user._id) {
await authSdk.logout({ ctx, userId: ctx.user._id })
}
ctx.body = { message: "User logged out." }
}
// INIT
export const setInitInfo = (ctx: any) => {
2021-11-05 02:03:18 +13:00
const initInfo = ctx.request.body
setCookie(ctx, initInfo, Cookie.Init)
2021-11-05 02:03:18 +13:00
ctx.status = 200
}
export const getInitInfo = (ctx: any) => {
try {
ctx.body = getCookie(ctx, Cookie.Init) || {}
} catch (err) {
clearCookie(ctx, Cookie.Init)
ctx.body = {}
}
2021-11-05 02:03:18 +13:00
}
// PASSWORD MANAGEMENT
/**
* Reset the user password, used as part of a forgotten password flow.
*/
export const reset = async (ctx: Ctx<PasswordResetRequest>) => {
const { email } = ctx.request.body
await authSdk.reset(email)
ctx.body = {
message: "Please check your email for a reset link.",
}
}
/**
* Perform the user password update if the provided reset code is valid.
*/
export const resetUpdate = async (ctx: Ctx<PasswordResetUpdateRequest>) => {
const { resetCode, password } = ctx.request.body
2021-05-06 02:17:15 +12:00
try {
await authSdk.resetUpdate(resetCode, password)
2021-05-06 02:17:15 +12:00
ctx.body = {
message: "password reset successfully.",
}
2024-01-03 01:02:24 +13:00
} catch (err: any) {
console.warn(err)
// hide any details of the error for security
2024-01-03 01:02:24 +13:00
ctx.throw(400, err.message || "Cannot reset password.")
}
}
// DATASOURCE
2021-04-14 00:56:28 +12:00
export const datasourcePreAuth = async (ctx: any, next: any) => {
2022-01-18 03:52:10 +13:00
const provider = ctx.params.provider
const { middleware } = require(`@budibase/backend-core`)
2022-01-18 03:52:10 +13:00
const handler = middleware.datasource[provider]
2022-01-06 21:08:54 +13:00
setCookie(
ctx,
{
2022-01-18 03:52:10 +13:00
provider,
2022-01-06 21:08:54 +13:00
appId: ctx.query.appId,
},
Cookie.DatasourceAuth
2022-01-06 21:08:54 +13:00
)
2022-01-18 03:52:10 +13:00
return handler.preAuth(passport, ctx, next)
2022-01-06 21:08:54 +13:00
}
export const datasourceAuth = async (ctx: any, next: any) => {
2023-12-06 07:17:27 +13:00
const authStateCookie = getCookie<DatasourceAuthCookie>(
ctx,
Cookie.DatasourceAuth
)
if (!authStateCookie) {
throw new Error("Unable to retrieve datasource authentication cookie")
}
2022-01-18 03:52:10 +13:00
const provider = authStateCookie.provider
const { middleware } = require(`@budibase/backend-core`)
2022-01-18 03:52:10 +13:00
const handler = middleware.datasource[provider]
return handler.postAuth(passport, ctx, next)
2022-01-06 21:08:54 +13:00
}
// GOOGLE SSO
export async function googleCallbackUrl(config?: GoogleInnerConfig) {
return ssoCallbackUrl(ConfigType.GOOGLE, config)
}
2021-04-22 22:45:22 +12:00
/**
* The initial call that google authentication makes to take you to the google login screen.
* On a successful login, you will be redirected to the googleAuth callback route.
*/
export const googlePreAuth = async (ctx: any, next: any) => {
const config = await configs.getGoogleConfig()
if (!config) {
return ctx.throw(400, "Google config not found")
}
let callbackUrl = await googleCallbackUrl(config)
2022-09-24 09:21:51 +12:00
const strategy = await google.strategyFactory(
config,
callbackUrl,
userSdk.db.save
2022-09-24 09:21:51 +12:00
)
2021-04-22 08:08:04 +12:00
return passport.authenticate(strategy, {
scope: ["profile", "email"],
accessType: "offline",
prompt: "consent",
2021-04-22 08:08:04 +12:00
})(ctx, next)
}
2021-04-22 05:40:32 +12:00
export const googleCallback = async (ctx: any, next: any) => {
const config = await configs.getGoogleConfig()
if (!config) {
return ctx.throw(400, "Google config not found")
}
const callbackUrl = await googleCallbackUrl(config)
2022-09-24 09:21:51 +12:00
const strategy = await google.strategyFactory(
config,
callbackUrl,
userSdk.db.save
2022-09-24 09:21:51 +12:00
)
2021-04-22 08:08:04 +12:00
2021-04-21 23:12:22 +12:00
return passport.authenticate(
2021-04-22 08:08:04 +12:00
strategy,
{
successRedirect: env.PASSPORT_GOOGLEAUTH_SUCCESS_REDIRECT,
failureRedirect: env.PASSPORT_GOOGLEAUTH_FAILURE_REDIRECT,
},
async (err: any, user: SSOUser, info: any) => {
await passportCallback(ctx, user, err, info)
await context.identity.doInUserContext(user, ctx, async () => {
await events.auth.login("google-internal", user.email)
2022-05-24 20:54:36 +12:00
})
ctx.redirect(env.PASSPORT_GOOGLEAUTH_SUCCESS_REDIRECT)
2021-04-21 23:12:22 +12:00
}
)(ctx, next)
2021-04-11 22:35:55 +12:00
}
2021-06-28 02:46:04 +12:00
// OIDC SSO
export async function oidcCallbackUrl() {
return ssoCallbackUrl(ConfigType.OIDC)
}
2024-03-21 00:46:39 +13:00
export const oidcStrategyFactory = async (ctx: any) => {
const config = await configs.getOIDCConfig()
if (!config) {
return ctx.throw(400, "OIDC config not found")
}
let callbackUrl = await oidcCallbackUrl()
2022-06-24 01:29:19 +12:00
//Remote Config
const enrichedConfig = await oidc.fetchStrategyConfig(config, callbackUrl)
return oidc.strategyFactory(enrichedConfig, userSdk.db.save)
}
2021-06-28 02:46:04 +12:00
/**
* The initial call that OIDC authentication makes to take you to the configured OIDC login screen.
* On a successful login, you will be redirected to the oidcAuth callback route.
*/
export const oidcPreAuth = async (ctx: Ctx, next: any) => {
const { configId } = ctx.params
if (!configId) {
ctx.throw(400, "OIDC config id is required")
}
2024-03-21 00:46:39 +13:00
const strategy = await oidcStrategyFactory(ctx)
setCookie(ctx, configId, Cookie.OIDC_CONFIG)
2021-06-28 02:46:04 +12:00
const config = await configs.getOIDCConfigById(configId)
if (!config) {
return ctx.throw(400, "OIDC config not found")
}
let authScopes =
config.scopes?.length > 0
? config.scopes
: ["profile", "email", "offline_access"]
2021-06-28 02:46:04 +12:00
return passport.authenticate(strategy, {
// required 'openid' scope is added by oidc strategy factory
scope: authScopes,
2021-06-28 02:46:04 +12:00
})(ctx, next)
}
export const oidcCallback = async (ctx: any, next: any) => {
2024-03-21 00:46:39 +13:00
const strategy = await oidcStrategyFactory(ctx)
2021-06-28 02:46:04 +12:00
return passport.authenticate(
strategy,
{
successRedirect: env.PASSPORT_OIDCAUTH_SUCCESS_REDIRECT,
failureRedirect: env.PASSPORT_OIDCAUTH_FAILURE_REDIRECT,
},
async (err: any, user: SSOUser, info: any) => {
await passportCallback(ctx, user, err, info)
await context.identity.doInUserContext(user, ctx, async () => {
await events.auth.login("oidc", user.email)
2022-05-24 20:54:36 +12:00
})
ctx.redirect(env.PASSPORT_OIDCAUTH_SUCCESS_REDIRECT)
2021-04-21 23:12:22 +12:00
}
)(ctx, next)
2021-04-11 22:35:55 +12:00
}