diff --git a/packages/client/src/createApp.js b/packages/client/src/createApp.js index 233abc0f58..f9d4f2bea5 100644 --- a/packages/client/src/createApp.js +++ b/packages/client/src/createApp.js @@ -2,7 +2,7 @@ import { attachChildren } from "./render/attachChildren" import { createTreeNode } from "./render/prepareRenderComponent" import { screenRouter } from "./render/screenRouter" import { createStateManager } from "./state/stateManager" -import { parseAppIdFromCookie } from "./render/getAppId" +import { getAppIdFromPath } from "./render/getAppId" export const createApp = ({ componentLibraries, @@ -38,7 +38,7 @@ export const createApp = ({ window, }) const fallbackPath = window.location.pathname.replace( - parseAppIdFromCookie(window.document.cookie), + getAppIdFromPath(), "" ) routeTo(currentUrl || fallbackPath) diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 6bbe2aee2b..54dbb7af9e 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -1,6 +1,6 @@ import { createApp } from "./createApp" import { builtins, builtinLibName } from "./render/builtinComponents" -import { parseAppIdFromCookie } from "./render/getAppId" +import { getAppIdFromPath } from "./render/getAppId" /** * create a web application from static budibase definition files. @@ -9,7 +9,7 @@ import { parseAppIdFromCookie } from "./render/getAppId" export const loadBudibase = async opts => { const _window = (opts && opts.window) || window // const _localStorage = (opts && opts.localStorage) || localStorage - const appId = parseAppIdFromCookie(_window.document.cookie) + const appId = getAppIdFromPath() const frontendDefinition = _window["##BUDIBASE_FRONTEND_DEFINITION##"] const user = {} diff --git a/packages/client/src/render/getAppId.js b/packages/client/src/render/getAppId.js index 4cbb6692b5..d476eda44c 100644 --- a/packages/client/src/render/getAppId.js +++ b/packages/client/src/render/getAppId.js @@ -1,14 +1,3 @@ -export const parseAppIdFromCookie = docCookie => { - const cookie = - docCookie.split(";").find(c => c.trim().startsWith("budibase:token")) || - docCookie.split(";").find(c => c.trim().startsWith("builder:token")) - - if (!cookie) return location.pathname.replace(/\//g, "") - - const base64Token = cookie.substring(lengthOfKey) - - const user = JSON.parse(atob(base64Token.split(".")[1])) - return user.appId +export const getAppIdFromPath = () => { + return location.pathname.split("/")[1] } - -const lengthOfKey = "budibase:token=".length diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js index 0c045097ed..32a12c167a 100644 --- a/packages/client/src/render/screenRouter.js +++ b/packages/client/src/render/screenRouter.js @@ -1,6 +1,6 @@ import regexparam from "regexparam" import appStore from "../state/store" -import { parseAppIdFromCookie } from "./getAppId" +import { getAppIdFromPath } from "./getAppId" export const screenRouter = ({ screens, onScreenSelected, window }) => { function sanitize(url) { @@ -27,7 +27,7 @@ export const screenRouter = ({ screens, onScreenSelected, window }) => { const makeRootedPath = url => { if (isRunningLocally()) { - const appId = parseAppIdFromCookie(window.document.cookie) + const appId = getAppIdFromPath() if (url) { url = sanitize(url) if (!url.startsWith("/")) { diff --git a/packages/client/tests/testAppDef.js b/packages/client/tests/testAppDef.js index c583d91545..90dbd2717d 100644 --- a/packages/client/tests/testAppDef.js +++ b/packages/client/tests/testAppDef.js @@ -9,7 +9,7 @@ export const load = async (page, screens, url, host = "test.com") => { const cookieJar = new jsdom.CookieJar() const cookie = `${btoa("{}")}.${btoa('{"appId":"TEST_APP_ID"}')}.signature` cookieJar.setCookie( - `budibase:token=${cookie};domain=${host};path=/`, + `budibase:local:TEST_APP_ID=${cookie};domain=${host};path=/`, fullUrl, { looseMode: false, diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 0cdaa25a85..2c7e43d2cc 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -4,6 +4,7 @@ const bcrypt = require("../../utilities/bcrypt") const env = require("../../environment") const { getAPIKey } = require("../../utilities/usageQuota") const { generateUserID } = require("../../db/utils") +const { getCookieName } = require("../../utilities") exports.authenticate = async ctx => { const appId = ctx.user.appId @@ -48,16 +49,18 @@ exports.authenticate = async ctx => { const expires = new Date() expires.setDate(expires.getDate() + 1) - ctx.cookies.set(`budibase:token`, token, { + ctx.cookies.set(getCookieName(appId), token, { expires, path: "/", httpOnly: false, overwrite: true, }) + delete dbUser.password ctx.body = { token, ...dbUser, + appId, } } else { ctx.throw(401, "Invalid credentials.") diff --git a/packages/server/src/middleware/authenticated.js b/packages/server/src/middleware/authenticated.js index 74f133cbde..a4bd347b75 100644 --- a/packages/server/src/middleware/authenticated.js +++ b/packages/server/src/middleware/authenticated.js @@ -9,6 +9,7 @@ const { } = require("../utilities/accessLevels") const env = require("../environment") const { AuthTypes } = require("../constants") +const { getAppId, getCookieName } = require("../utilities") module.exports = async (ctx, next) => { if (ctx.path === "/_builder") { @@ -16,8 +17,10 @@ module.exports = async (ctx, next) => { return } - const appToken = ctx.cookies.get("budibase:token") - const builderToken = ctx.cookies.get("builder:token") + const appId = getAppId(ctx) + + const appToken = ctx.cookies.get(getCookieName(appId)) + const builderToken = ctx.cookies.get(getCookieName()) let token // if running locally in the builder itself @@ -31,16 +34,6 @@ module.exports = async (ctx, next) => { if (!token) { ctx.auth.authenticated = false - - let appId = env.CLOUD ? ctx.subdomains[1] : ctx.params.appId - - // if appId can't be determined from path param or subdomain - if (!appId && ctx.request.headers.referer) { - const url = new URL(ctx.request.headers.referer) - // remove leading and trailing slashes from appId - appId = url.pathname.replace(/\//g, "") - } - ctx.user = { appId, } diff --git a/packages/server/src/utilities/builder/setBuilderToken.js b/packages/server/src/utilities/builder/setBuilderToken.js index 811f273d40..f77bfe49a4 100644 --- a/packages/server/src/utilities/builder/setBuilderToken.js +++ b/packages/server/src/utilities/builder/setBuilderToken.js @@ -2,6 +2,9 @@ const { BUILDER_LEVEL_ID } = require("../accessLevels") const env = require("../../environment") const CouchDB = require("../../db") const jwt = require("jsonwebtoken") +const { DocumentTypes, SEPARATOR } = require("../../db/utils") +const { getCookieName } = require("../index") +const APP_PREFIX = DocumentTypes.APP + SEPARATOR module.exports = async (ctx, appId, version) => { const builderUser = { @@ -20,15 +23,18 @@ module.exports = async (ctx, appId, version) => { const expiry = new Date() expiry.setDate(expiry.getDate() + 30) // set the builder token - ctx.cookies.set("builder:token", token, { + ctx.cookies.set(getCookieName(), token, { expires: expiry, httpOnly: false, overwrite: true, }) // need to clear all app tokens or else unable to use the app in the builder let allDbNames = await CouchDB.allDbs() - allDbNames.map(dbName => {}) - ctx.cookies.set(`budibase:${appId}`, "", { - overwrite: true, + allDbNames.map(dbName => { + if (dbName.startsWith(APP_PREFIX)) { + ctx.cookies.set(getCookieName(dbName), "", { + overwrite: true, + }) + } }) } diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index 4fdc9f8b49..4c324cbfdf 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -11,6 +11,32 @@ exports.isDev = () => { ) } +/** + * Given a request tries to find the appId, which can be located in various places + * @param {object} ctx The main request body to look through. + * @returns {string|undefined} If an appId was found it will be returned. + */ exports.getAppId = ctx => { - + let appId = env.CLOUD ? ctx.subdomains[1] : ctx.params.appId + // look in body if can't find it in subdomain + if (!appId && ctx.request.body && ctx.request.body.appId) { + appId = ctx.request.body.appId + } + // if appId can't be determined from path param or subdomain + if (!appId && ctx.request.headers.referer) { + const url = new URL(ctx.request.headers.referer) + // remove leading and trailing slashes from appId + appId = url.pathname.replace(/\//g, "") + } + return appId +} + +/** + * Get the name of the cookie which is to be updated/retrieved + * @param {string|undefined|null} appId OPTIONAL can specify the specific app if previewing etc + * @returns {string} The name of the token trying to find + */ +exports.getCookieName = (appId = null) => { + let mid = env.CLOUD ? "cloud" : "local" + return `budibase:${mid}:${appId ? appId : "builder"}` }