diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index d8c67a5854..d5424d277b 100644 --- a/packages/auth/src/cache/user.js +++ b/packages/auth/src/cache/user.js @@ -1,22 +1,25 @@ const redis = require("../redis/authRedis") const { - updateTenantId, + getTenantId, lookupTenantId, getGlobalDB, - isTenantIdSet, } = require("../tenancy") const EXPIRY_SECONDS = 3600 -exports.getUser = async userId => { - if (!isTenantIdSet()) { - updateTenantId(await lookupTenantId(userId)) +exports.getUser = async (userId, tenantId = null) => { + if (!tenantId) { + try { + tenantId = getTenantId() + } catch (err) { + tenantId = await lookupTenantId(userId) + } } const client = await redis.getUserClient() // try cache let user = await client.get(userId) if (!user) { - user = await getGlobalDB().get(userId) + user = await getGlobalDB(tenantId).get(userId) client.store(userId, user, EXPIRY_SECONDS) } return user diff --git a/packages/auth/src/middleware/authenticated.js b/packages/auth/src/middleware/authenticated.js index cf9a19e58b..303553212b 100644 --- a/packages/auth/src/middleware/authenticated.js +++ b/packages/auth/src/middleware/authenticated.js @@ -3,7 +3,6 @@ const { getCookie, clearCookie } = require("../utils") const { getUser } = require("../cache/user") const { getSession, updateSessionTTL } = require("../security/sessions") const { buildMatcherRegex, matches } = require("./matchers") -const { isTenantIdSet, updateTenantId } = require("../tenancy") const env = require("../environment") function finalise( @@ -17,6 +16,11 @@ function finalise( ctx.version = version } +/** + * This middleware is tenancy aware, so that it does not depend on other middlewares being used. + * The tenancy modules should not be used here and it should be assumed that the tenancy context + * has not yet been populated. + */ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : [] return async (ctx, next) => { @@ -42,10 +46,7 @@ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { error = "No session found" } else { try { - if (session.tenantId && !isTenantIdSet()) { - updateTenantId(session.tenantId) - } - user = await getUser(userId) + user = await getUser(userId, session.tenantId) delete user.password authenticated = true } catch (err) { diff --git a/packages/auth/src/middleware/matchers.js b/packages/auth/src/middleware/matchers.js index f46f0d781b..a555823136 100644 --- a/packages/auth/src/middleware/matchers.js +++ b/packages/auth/src/middleware/matchers.js @@ -1,6 +1,9 @@ const PARAM_REGEX = /\/:(.*?)(\/.*)?$/g exports.buildMatcherRegex = patterns => { + if (!patterns) { + return [] + } return patterns.map(pattern => { const isObj = typeof pattern === "object" && pattern.route const method = isObj ? pattern.method : "GET" diff --git a/packages/auth/src/middleware/tenancy.js b/packages/auth/src/middleware/tenancy.js index 9242f11c26..b80b9a6763 100644 --- a/packages/auth/src/middleware/tenancy.js +++ b/packages/auth/src/middleware/tenancy.js @@ -1,20 +1,14 @@ -const { createTenancyContext, setTenantId } = require("../tenancy") +const { setTenantId } = require("../tenancy") +const ContextFactory = require("../tenancy/FunctionContext") const { buildMatcherRegex, matches } = require("./matchers") module.exports = (allowQueryStringPatterns, noTenancyPatterns) => { const allowQsOptions = buildMatcherRegex(allowQueryStringPatterns) const noTenancyOptions = buildMatcherRegex(noTenancyPatterns) - return (ctx, next) => { - // always run in context - return createTenancyContext().runAndReturn(() => { - if (matches(ctx, noTenancyOptions)) { - return next() - } - - const allowQs = !!matches(ctx, allowQsOptions) - setTenantId(ctx, { allowQs }) - return next() - }) - } + return ContextFactory.getMiddleware(ctx => { + const allowNoTenant = !!matches(ctx, noTenancyOptions) + const allowQs = !!matches(ctx, allowQsOptions) + setTenantId(ctx, { allowQs, allowNoTenant }) + }) } diff --git a/packages/auth/src/tenancy/FunctionContext.js b/packages/auth/src/tenancy/FunctionContext.js new file mode 100644 index 0000000000..06e0d92f5c --- /dev/null +++ b/packages/auth/src/tenancy/FunctionContext.js @@ -0,0 +1,69 @@ +const cls = require("cls-hooked") +const { newid } = require("../hashing") + +const REQUEST_ID_KEY = "requestId" + +class FunctionContext { + static getMiddleware(updateCtxFn = null) { + const namespace = this.createNamespace() + + return async function(ctx, next) { + await new Promise(namespace.bind(function(resolve, reject) { + // store a contextual request ID that can be used anywhere (audit logs) + namespace.set(REQUEST_ID_KEY, newid()) + namespace.bindEmitter(ctx.req) + namespace.bindEmitter(ctx.res) + + if (updateCtxFn) { + updateCtxFn(ctx) + } + next().then(resolve).catch(reject) + })) + } + } + + static run(callback) { + const namespace = this.createNamespace() + + return namespace.runAndReturn(callback) + } + + static setOnContext(key, value) { + const namespace = this.createNamespace() + namespace.set(key, value) + } + + static getContextStorage() { + if (this._namespace && this._namespace.active) { + const { id, _ns_name, ...contextData } = this._namespace.active + return contextData + } + + return {} + } + + static getFromContext(key) { + const context = this.getContextStorage() + if (context) { + return context[key] + } else { + return null + } + } + + static destroyNamespace() { + if (this._namespace) { + cls.destroyNamespace("session") + this._namespace = null + } + } + + static createNamespace() { + if (!this._namespace) { + this._namespace = cls.createNamespace("session") + } + return this._namespace + } +} + +module.exports = FunctionContext diff --git a/packages/auth/src/tenancy/context.js b/packages/auth/src/tenancy/context.js index 5210700bd0..71fba2a3d5 100644 --- a/packages/auth/src/tenancy/context.js +++ b/packages/auth/src/tenancy/context.js @@ -1,6 +1,6 @@ -const cls = require("cls-hooked") const env = require("../environment") const { Headers } = require("../../constants") +const cls = require("./FunctionContext") exports.DEFAULT_TENANT_ID = "default" @@ -12,66 +12,61 @@ exports.isMultiTenant = () => { return env.MULTI_TENANCY } -// continuation local storage -const CONTEXT_NAME = "tenancy" const TENANT_ID = "tenantId" -exports.createTenancyContext = () => { - return cls.createNamespace(CONTEXT_NAME) -} - -const getTenancyContext = () => { - return cls.getNamespace(CONTEXT_NAME) -} - // used for automations, API endpoints should always be in context already exports.doInTenant = (tenantId, task) => { - const context = getTenancyContext() - return getTenancyContext().runAndReturn(() => { + return cls.run(() => { // set the tenant id - context.set(TENANT_ID, tenantId) + cls.setOnContext(TENANT_ID, tenantId) // invoke the task const result = task() // clear down the tenant id manually for extra safety // this should also happen automatically when the call exits - context.set(TENANT_ID, null) + cls.setOnContext(TENANT_ID, null) return result }) } exports.updateTenantId = tenantId => { - getTenancyContext().set(TENANT_ID, tenantId) + cls.setOnContext(TENANT_ID, tenantId) } -exports.setTenantId = (ctx, opts = { allowQs: false }) => { +exports.setTenantId = (ctx, opts = { allowQs: false, allowNoTenant: false }) => { let tenantId // exit early if not multi-tenant if (!exports.isMultiTenant()) { - getTenancyContext().set(TENANT_ID, this.DEFAULT_TENANT_ID) + cls.setOnContext(TENANT_ID, this.DEFAULT_TENANT_ID) return } - const params = ctx.request.params || {} + const allowQs = opts && opts.allowQs + const allowNoTenant = opts && opts.allowNoTenant const header = ctx.request.headers[Headers.TENANT_ID] - const user = ctx.request.user || {} - tenantId = user.tenantId || params.tenantId || header - if (opts.allowQs && !tenantId) { + const user = ctx.user || {} + if (allowQs) { const query = ctx.request.query || {} tenantId = query.tenantId } + // override query string (if allowed) by user, or header + // URL params cannot be used in a middleware, as they are + // processed later in the chain + tenantId = user.tenantId || header || tenantId - if (!tenantId) { + if (!tenantId && !allowNoTenant) { ctx.throw(403, "Tenant id not set") } - - getTenancyContext().set(TENANT_ID, tenantId) + // check tenant ID just incase no tenant was allowed + if (tenantId) { + cls.setOnContext(TENANT_ID, tenantId) + } } exports.isTenantIdSet = () => { - const tenantId = getTenancyContext().get(TENANT_ID) + const tenantId = cls.getFromContext(TENANT_ID) return !!tenantId } @@ -79,7 +74,7 @@ exports.getTenantId = () => { if (!exports.isMultiTenant()) { return exports.DEFAULT_TENANT_ID } - const tenantId = getTenancyContext().get(TENANT_ID) + const tenantId = cls.getFromContext(TENANT_ID) if (!tenantId) { throw Error("Tenant id not found") } diff --git a/packages/auth/src/tenancy/tenancy.js b/packages/auth/src/tenancy/tenancy.js index 16dedd3ebf..6e18ea7154 100644 --- a/packages/auth/src/tenancy/tenancy.js +++ b/packages/auth/src/tenancy/tenancy.js @@ -1,4 +1,4 @@ -const { getDB } = require("../../db") +const { getDB } = require("../db") const { SEPARATOR, StaticDatabases } = require("../db/constants") const { getTenantId, DEFAULT_TENANT_ID, isMultiTenant } = require("./context") const env = require("../environment") diff --git a/packages/builder/src/pages/builder/portal/manage/email/index.svelte b/packages/builder/src/pages/builder/portal/manage/email/index.svelte index 5161625510..f94d2bcd0f 100644 --- a/packages/builder/src/pages/builder/portal/manage/email/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/email/index.svelte @@ -94,6 +94,9 @@ requireAuth = smtpConfig.config.auth != null // always attach the auth for the forms purpose - // this will be removed later if required + if (!smtpDoc.config) { + smtpDoc.config = {} + } if (!smtpDoc.config.auth) { smtpConfig.config.auth = { type: "login", diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 15c4c9bf7a..d0de611d74 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -38,6 +38,7 @@ const { backupClientLibrary, revertClientLibrary, } = require("../../utilities/fileSystem/clientLibrary") +const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy") const URL_REGEX_SLASH = /\/|\\/g @@ -92,8 +93,9 @@ async function getAppUrlIfNotInUse(ctx) { return url } -async function createInstance(tenantId, template) { - const baseAppId = generateAppID(env.MULTI_TENANCY ? tenantId : null) +async function createInstance(template) { + const tenantId = isMultiTenant() ? getTenantId() : null + const baseAppId = generateAppID(tenantId) const appId = generateDevAppID(baseAppId) const db = new CouchDB(appId) @@ -128,8 +130,7 @@ async function createInstance(tenantId, template) { exports.fetch = async function (ctx) { const dev = ctx.query && ctx.query.status === AppStatus.DEV const all = ctx.query && ctx.query.status === AppStatus.ALL - const tenantId = ctx.user.tenantId - const apps = await getAllApps(CouchDB, { tenantId, dev, all }) + const apps = await getAllApps(CouchDB, { dev, all }) // get the locks for all the dev apps if (dev || all) { @@ -189,7 +190,6 @@ exports.fetchAppPackage = async function (ctx) { } exports.create = async function (ctx) { - const tenantId = ctx.user.tenantId const { useTemplate, templateKey } = ctx.request.body const instanceConfig = { useTemplate, @@ -198,7 +198,7 @@ exports.create = async function (ctx) { if (ctx.request.files && ctx.request.files.templateFile) { instanceConfig.file = ctx.request.files.templateFile } - const instance = await createInstance(tenantId, instanceConfig) + const instance = await createInstance(instanceConfig) const appId = instance._id const url = await getAppUrlIfNotInUse(ctx) @@ -222,7 +222,7 @@ exports.create = async function (ctx) { url: url, template: ctx.request.body.template, instance: instance, - tenantId, + tenantId: getTenantId(), updatedAt: new Date().toISOString(), createdAt: new Date().toISOString(), } diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index 13fb7efd1d..81601eea1a 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -10,6 +10,13 @@ const env = require("../environment") const router = new Router() +const NO_TENANCY_ENDPOINTS = [ + { + route: "/api/analytics", + method: "GET", + }, +] + router .use( compress({ @@ -32,12 +39,13 @@ router }) .use("/health", ctx => (ctx.status = 200)) .use("/version", ctx => (ctx.body = pkg.version)) - .use(buildTenancyMiddleware()) .use( buildAuthMiddleware(null, { publicAllowed: true, }) ) + // nothing in the server should allow query string tenants + .use(buildTenancyMiddleware(null, NO_TENANCY_ENDPOINTS)) .use(currentApp) .use(auditLog) diff --git a/packages/server/src/automations/steps/sendSmtpEmail.js b/packages/server/src/automations/steps/sendSmtpEmail.js index 7b25da801e..764972b402 100644 --- a/packages/server/src/automations/steps/sendSmtpEmail.js +++ b/packages/server/src/automations/steps/sendSmtpEmail.js @@ -46,13 +46,13 @@ module.exports.definition = { }, } -module.exports.run = async function ({ inputs, tenantId }) { +module.exports.run = async function ({ inputs }) { let { to, from, subject, contents } = inputs if (!contents) { contents = "

No content

" } try { - let response = await sendSmtpEmail(tenantId, to, from, subject, contents) + let response = await sendSmtpEmail(to, from, subject, contents) return { success: true, response, diff --git a/packages/server/src/automations/thread.js b/packages/server/src/automations/thread.js index 4dc843538b..aada0ca0ca 100644 --- a/packages/server/src/automations/thread.js +++ b/packages/server/src/automations/thread.js @@ -6,6 +6,7 @@ const { processObject } = require("@budibase/string-templates") const { DEFAULT_TENANT_ID } = require("@budibase/auth").constants const CouchDB = require("../db") const { DocumentTypes } = require("../db/utils") +const { doInTenant } = require("@budibase/auth/tenancy") const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId @@ -56,7 +57,7 @@ class Orchestrator { async execute() { let automation = this._automation - const app = this.getApp() + const app = await this.getApp() for (let step of automation.definition.steps) { let stepFn = await this.getStepFunctionality(step.type, step.stepId) step.inputs = await processObject(step.inputs, this._context) @@ -66,13 +67,15 @@ class Orchestrator { ) // appId is always passed try { - const outputs = await stepFn({ - inputs: step.inputs, - appId: this._appId, - apiKey: automation.apiKey, - emitter: this._emitter, - context: this._context, - tenantId: app.tenantId || DEFAULT_TENANT_ID, + let tenantId = app.tenantId || DEFAULT_TENANT_ID + const outputs = await doInTenant(tenantId, () => { + return stepFn({ + inputs: step.inputs, + appId: this._appId, + apiKey: automation.apiKey, + emitter: this._emitter, + context: this._context, + }) }) if (step.stepId === FILTER_STEP_ID && !outputs.success) { break diff --git a/packages/server/src/middleware/currentapp.js b/packages/server/src/middleware/currentapp.js index 7169a36320..8f2403cc37 100644 --- a/packages/server/src/middleware/currentapp.js +++ b/packages/server/src/middleware/currentapp.js @@ -68,5 +68,6 @@ module.exports = async (ctx, next) => { ) { setCookie(ctx, { appId }, Cookies.CurrentApp) } + return next() } diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index d3e9701b62..2757398586 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -33,7 +33,9 @@ function processUser(appId, user) { } exports.getCachedSelf = async (ctx, appId) => { - const user = await userCache.getUser(ctx.user._id, ctx.user.tenantId) + // this has to be tenant aware, can't depend on the context to find it out + // running some middlewares before the tenancy causes context to break + const user = await userCache.getUser(ctx.user._id) return processUser(appId, user) } diff --git a/packages/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.js index 215a453bfb..066f6e23d4 100644 --- a/packages/server/src/utilities/workerRequests.js +++ b/packages/server/src/utilities/workerRequests.js @@ -4,6 +4,7 @@ const { checkSlashesInUrl } = require("./index") const { getDeployedAppID } = require("@budibase/auth/db") const { updateAppRole, getGlobalUser } = require("./global") const { Headers } = require("@budibase/auth/constants") +const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy") function request(ctx, request) { if (!request.headers) { @@ -11,6 +12,9 @@ function request(ctx, request) { } if (!ctx) { request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY + if (isTenantIdSet()) { + request.headers[Headers.TENANT_ID] = getTenantId() + } } if (request.body && Object.keys(request.body).length > 0) { request.headers["Content-Type"] = "application/json" @@ -29,13 +33,14 @@ function request(ctx, request) { exports.request = request -exports.sendSmtpEmail = async (tenantId, to, from, subject, contents) => { +// have to pass in the tenant ID as this could be coming from an automation +exports.sendSmtpEmail = async (to, from, subject, contents) => { + // tenant ID will be set in header const response = await fetch( checkSlashesInUrl(env.WORKER_URL + `/api/global/email/send`), request(null, { method: "POST", body: { - tenantId, email: to, from, contents, diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index e2284b09a8..0ba4dd7986 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -13,65 +13,9 @@ const { user: userCache } = require("@budibase/auth/cache") const { invalidateSessions } = require("@budibase/auth/sessions") const CouchDB = require("../../../db") const env = require("../../../environment") -const { getGlobalDB, getTenantId } = require("@budibase/auth/tenancy") +const { getGlobalDB, getTenantId, doesTenantExist, tryAddTenant, updateTenantId } = require("@budibase/auth/tenancy") const PLATFORM_INFO_DB = StaticDatabases.PLATFORM_INFO.name -const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants - -async function tryAddTenant(tenantId, userId, email) { - const db = new CouchDB(PLATFORM_INFO_DB) - const getDoc = async id => { - if (!id) { - return null - } - try { - return await db.get(id) - } catch (err) { - return { _id: id } - } - } - let [tenants, userIdDoc, emailDoc] = await Promise.all([ - getDoc(TENANT_DOC), - getDoc(userId), - getDoc(email), - ]) - if (!Array.isArray(tenants.tenantIds)) { - tenants = { - _id: TENANT_DOC, - tenantIds: [], - } - } - let promises = [] - if (userIdDoc) { - userIdDoc.tenantId = tenantId - promises.push(db.put(userIdDoc)) - } - if (emailDoc) { - emailDoc.tenantId = tenantId - promises.push(db.put(emailDoc)) - } - if (tenants.tenantIds.indexOf(tenantId) === -1) { - tenants.tenantIds.push(tenantId) - promises.push(db.put(tenants)) - } - await Promise.all(promises) -} - -async function doesTenantExist(tenantId) { - const db = new CouchDB(PLATFORM_INFO_DB) - let tenants - try { - tenants = await db.get(TENANT_DOC) - } catch (err) { - // if theres an error the doc doesn't exist, no tenants exist - return false - } - return ( - tenants && - Array.isArray(tenants.tenantIds) && - tenants.tenantIds.indexOf(tenantId) !== -1 - ) -} async function allUsers() { const db = getGlobalDB() @@ -87,6 +31,8 @@ async function saveUser(user, tenantId) { if (!tenantId) { throw "No tenancy specified." } + // need to set the context for this request, as specified + updateTenantId(tenantId) // specify the tenancy incase we're making a new admin user (public) const db = getGlobalDB(tenantId) let { email, password, _id } = user @@ -162,7 +108,7 @@ exports.adminUser = async ctx => { ctx.throw(403, "Organisation already exists.") } - const db = getGlobalDB() + const db = getGlobalDB(tenantId) const response = await db.allDocs( getGlobalUserParams(null, { include_docs: true, diff --git a/packages/worker/src/api/index.js b/packages/worker/src/api/index.js index 1e2a12ab1b..844690148f 100644 --- a/packages/worker/src/api/index.js +++ b/packages/worker/src/api/index.js @@ -5,17 +5,6 @@ const { routes } = require("./routes") const { buildAuthMiddleware, auditLog, buildTenancyMiddleware } = require("@budibase/auth").auth -const NO_TENANCY_ENDPOINTS = [ - { - route: "/api/system", - method: "ALL", - }, - { - route: "/api/global/users/self", - method: "GET", - }, -] - const PUBLIC_ENDPOINTS = [ { // this covers all of the POST auth routes @@ -32,10 +21,6 @@ const PUBLIC_ENDPOINTS = [ route: "/api/global/configs/public", method: "GET", }, - { - route: "api/global/flags", - method: "GET", - }, { route: "/api/global/configs/checklist", method: "GET", @@ -48,6 +33,22 @@ const PUBLIC_ENDPOINTS = [ route: "/api/global/users/invite/accept", method: "POST", }, + { + route: "api/system/flags", + method: "GET", + }, +] + +const NO_TENANCY_ENDPOINTS = [ + ...PUBLIC_ENDPOINTS, + { + route: "/api/system", + method: "ALL", + }, + { + route: "/api/global/users/self", + method: "GET", + }, ] const router = new Router() @@ -65,8 +66,8 @@ router }) ) .use("/health", ctx => (ctx.status = 200)) - .use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) .use(buildAuthMiddleware(PUBLIC_ENDPOINTS)) + .use(buildTenancyMiddleware(PUBLIC_ENDPOINTS, NO_TENANCY_ENDPOINTS)) // for now no public access is allowed to worker (bar health check) .use((ctx, next) => { if (!ctx.isAuthenticated && !ctx.publicEndpoint) { diff --git a/packages/worker/src/api/routes/global/auth.js b/packages/worker/src/api/routes/global/auth.js index 34260dee7f..bf9ebfcb7c 100644 --- a/packages/worker/src/api/routes/global/auth.js +++ b/packages/worker/src/api/routes/global/auth.js @@ -2,6 +2,7 @@ const Router = require("@koa/router") const authController = require("../../controllers/global/auth") const joiValidator = require("../../../middleware/joi-validator") const Joi = require("joi") +const { updateTenantId } = require("@budibase/auth/tenancy") const router = Router() @@ -28,34 +29,41 @@ function buildResetUpdateValidation() { }).required().unknown(false)) } +function updateTenant(ctx, next) { + updateTenantId(ctx.params.tenantId) + return next() +} + router .post( "/api/global/auth/:tenantId/login", buildAuthValidation(), + updateTenant, authController.authenticate ) .post( "/api/global/auth/:tenantId/reset", buildResetValidation(), + updateTenant, authController.reset ) .post( "/api/global/auth/:tenantId/reset/update", buildResetUpdateValidation(), + updateTenant, authController.resetUpdate ) .post("/api/global/auth/logout", authController.logout) - .get("/api/global/auth/:tenantId/google", authController.googlePreAuth) - .get("/api/global/auth/:tenantId/google/callback", authController.googleAuth) + .get("/api/global/auth/:tenantId/google", updateTenant, authController.googlePreAuth) + .get("/api/global/auth/:tenantId/google/callback", updateTenant, authController.googleAuth) .get( "/api/global/auth/:tenantId/oidc/configs/:configId", + updateTenant, authController.oidcPreAuth ) - .get("/api/global/auth/:tenantId/oidc/callback", authController.oidcAuth) + .get("/api/global/auth/:tenantId/oidc/callback", updateTenant, authController.oidcAuth) // deprecated - used by the default system before tenancy .get("/api/admin/auth/google/callback", authController.googleAuth) - .get("/api/global/auth/google/callback", authController.googleAuth) .get("/api/admin/auth/oidc/callback", authController.oidcAuth) - .get("/api/global/auth/oidc/callback", authController.oidcAuth) module.exports = router