diff --git a/packages/backend-core/src/auth/auth.ts b/packages/backend-core/src/auth/auth.ts index e31bc81eed..8de8904f3a 100644 --- a/packages/backend-core/src/auth/auth.ts +++ b/packages/backend-core/src/auth/auth.ts @@ -19,6 +19,7 @@ import { GoogleInnerConfig, OIDCInnerConfig, PlatformLogoutOpts, + SessionInfo, SSOProviderType, } from "@budibase/types" import * as events from "../events" @@ -44,7 +45,6 @@ export const buildAuthMiddleware = authenticated export const buildTenancyMiddleware = tenancy export const buildCsrfMiddleware = csrf export const passport = _passport -export const jwt = require("jsonwebtoken") // Strategies _passport.use(new LocalStrategy(local.options, local.authenticate)) @@ -191,10 +191,10 @@ export async function platformLogout(opts: PlatformLogoutOpts) { if (!ctx) throw new Error("Koa context must be supplied to logout.") - const currentSession = getCookie(ctx, Cookie.Auth) + const currentSession = getCookie(ctx, Cookie.Auth) let sessions = await getSessionsForUser(userId) - if (keepActiveSession) { + if (currentSession && keepActiveSession) { sessions = sessions.filter( session => session.sessionId !== currentSession.sessionId ) diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts index 8bd6591d05..3e82498bdd 100644 --- a/packages/backend-core/src/middleware/authenticated.ts +++ b/packages/backend-core/src/middleware/authenticated.ts @@ -13,7 +13,7 @@ import { getGlobalDB, doInTenant } from "../context" import { decrypt } from "../security/encryption" import * as identity from "../context/identity" import env from "../environment" -import { Ctx, EndpointMatcher } from "@budibase/types" +import { Ctx, EndpointMatcher, SessionInfo } from "@budibase/types" import { InvalidAPIKeyError, ErrorCode } from "../errors" const ONE_MINUTE = env.SESSION_UPDATE_PERIOD @@ -98,7 +98,9 @@ export default function ( // check the actual user is authenticated first, try header or cookie let headerToken = ctx.request.headers[Header.TOKEN] - const authCookie = getCookie(ctx, Cookie.Auth) || openJwt(headerToken) + const authCookie = + getCookie(ctx, Cookie.Auth) || + openJwt(headerToken) let apiKey = ctx.request.headers[Header.API_KEY] if (!apiKey && ctx.request.headers[Header.AUTHORIZATION]) { diff --git a/packages/backend-core/src/middleware/passport/datasource/google.ts b/packages/backend-core/src/middleware/passport/datasource/google.ts index ae6b3b4913..7f768f1623 100644 --- a/packages/backend-core/src/middleware/passport/datasource/google.ts +++ b/packages/backend-core/src/middleware/passport/datasource/google.ts @@ -58,7 +58,14 @@ export async function postAuth( const platformUrl = await configs.getPlatformUrl({ tenantAware: false }) let callbackUrl = `${platformUrl}/api/global/auth/datasource/google/callback` - const authStateCookie = utils.getCookie(ctx, Cookie.DatasourceAuth) + const authStateCookie = utils.getCookie<{ appId: string }>( + ctx, + Cookie.DatasourceAuth + ) + + if (!authStateCookie) { + throw new Error("Unable to fetch datasource auth cookie") + } return passport.authenticate( new GoogleStrategy( diff --git a/packages/backend-core/src/utils/utils.ts b/packages/backend-core/src/utils/utils.ts index b10d9ebdc0..429978fc97 100644 --- a/packages/backend-core/src/utils/utils.ts +++ b/packages/backend-core/src/utils/utils.ts @@ -11,8 +11,7 @@ import { TenantResolutionStrategy, } from "@budibase/types" import type { SetOption } from "cookies" - -const jwt = require("jsonwebtoken") +import jwt, { Secret } from "jsonwebtoken" const APP_PREFIX = DocumentType.APP + SEPARATOR const PROD_APP_PREFIX = "/app/" @@ -60,10 +59,7 @@ export function isServingApp(ctx: Ctx) { return true } // prod app - if (ctx.path.startsWith(PROD_APP_PREFIX)) { - return true - } - return false + return ctx.path.startsWith(PROD_APP_PREFIX) } export function isServingBuilder(ctx: Ctx): boolean { @@ -138,16 +134,16 @@ function parseAppIdFromUrl(url?: string) { * opens the contents of the specified encrypted JWT. * @return the contents of the token. */ -export function openJwt(token: string) { +export function openJwt(token?: string): T | undefined { if (!token) { - return token + return undefined } try { - return jwt.verify(token, env.JWT_SECRET) + return jwt.verify(token, env.JWT_SECRET as Secret) as T } catch (e) { if (env.JWT_SECRET_FALLBACK) { // fallback to enable rotation - return jwt.verify(token, env.JWT_SECRET_FALLBACK) + return jwt.verify(token, env.JWT_SECRET_FALLBACK) as T } else { throw e } @@ -159,13 +155,9 @@ export function isValidInternalAPIKey(apiKey: string) { return true } // fallback to enable rotation - if ( - env.INTERNAL_API_KEY_FALLBACK && - env.INTERNAL_API_KEY_FALLBACK === apiKey - ) { - return true - } - return false + return !!( + env.INTERNAL_API_KEY_FALLBACK && env.INTERNAL_API_KEY_FALLBACK === apiKey + ) } /** @@ -173,14 +165,14 @@ export function isValidInternalAPIKey(apiKey: string) { * @param ctx The request which is to be manipulated. * @param name The name of the cookie to get. */ -export function getCookie(ctx: Ctx, name: string) { +export function getCookie(ctx: Ctx, name: string) { const cookie = ctx.cookies.get(name) if (!cookie) { - return cookie + return undefined } - return openJwt(cookie) + return openJwt(cookie) as T } /** @@ -197,7 +189,7 @@ export function setCookie( opts = { sign: true } ) { if (value && opts && opts.sign) { - value = jwt.sign(value, env.JWT_SECRET) + value = jwt.sign(value, env.JWT_SECRET as Secret) } const config: SetOption = { diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte index e492b6be46..c05306c679 100644 --- a/packages/builder/src/components/start/ExportAppModal.svelte +++ b/packages/builder/src/components/start/ExportAppModal.svelte @@ -46,7 +46,7 @@ if (!$validation.valid) { return keepOpen } - exportApp(password) + await exportApp(password) }, isValid: $validation.valid, }, diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index b7886ccea4..afaad64723 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -56,6 +56,7 @@ import { import API from "./api" import { cloneDeep } from "lodash" +import jwt, { Secret } from "jsonwebtoken" mocks.licenses.init(pro) @@ -391,7 +392,7 @@ class TestConfiguration { sessionId: "sessionid", tenantId: this.getTenantId(), } - const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET) + const authToken = jwt.sign(authObj, coreEnv.JWT_SECRET as Secret) // returning necessary request headers await cache.user.invalidateUser(userId) @@ -412,7 +413,7 @@ class TestConfiguration { sessionId: "sessionid", tenantId, } - const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET) + const authToken = jwt.sign(authObj, coreEnv.JWT_SECRET as Secret) const headers: any = { Accept: "application/json", diff --git a/packages/types/src/api/web/global/index.ts b/packages/types/src/api/web/global/index.ts index efcb6dc39c..e6e2a78feb 100644 --- a/packages/types/src/api/web/global/index.ts +++ b/packages/types/src/api/web/global/index.ts @@ -4,3 +4,4 @@ export * from "./events" export * from "./configs" export * from "./scim" export * from "./license" +export * from "./sessions" diff --git a/packages/types/src/api/web/global/sessions.ts b/packages/types/src/api/web/global/sessions.ts new file mode 100644 index 0000000000..a6b94a3d24 --- /dev/null +++ b/packages/types/src/api/web/global/sessions.ts @@ -0,0 +1,4 @@ +export interface SessionInfo { + sessionId: string + userId: string +} diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 289f31079a..c43d1b9d13 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -35,6 +35,7 @@ import { ConfigType, } from "@budibase/types" import API from "./api" +import jwt, { Secret } from "jsonwebtoken" class TestConfiguration { server: any @@ -209,7 +210,7 @@ class TestConfiguration { sessionId: "sessionid", tenantId: user.tenantId, } - const authCookie = auth.jwt.sign(authToken, coreEnv.JWT_SECRET) + const authCookie = jwt.sign(authToken, coreEnv.JWT_SECRET as Secret) return { Accept: "application/json", ...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]), @@ -327,7 +328,7 @@ class TestConfiguration { // CONFIGS - OIDC getOIDConfigCookie(configId: string) { - const token = auth.jwt.sign(configId, coreEnv.JWT_SECRET) + const token = jwt.sign(configId, coreEnv.JWT_SECRET as Secret) return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]]) }