Store OIDC config in cookie instead of URL

This commit is contained in:
Rory Powell 2021-07-15 16:20:31 +01:00
parent 239e39e5ed
commit 33b352c3ef
7 changed files with 35 additions and 21 deletions

View File

@ -6,6 +6,7 @@ exports.UserStatus = {
exports.Cookies = { exports.Cookies = {
CurrentApp: "budibase:currentapp", CurrentApp: "budibase:currentapp",
Auth: "budibase:auth", Auth: "budibase:auth",
OIDC_CONFIG: "budibase:oidc:config",
} }
exports.GlobalRoles = { exports.GlobalRoles = {

View File

@ -4,7 +4,7 @@
import Auth0Logo from "assets/auth0-logo.png" import Auth0Logo from "assets/auth0-logo.png"
import MicrosoftLogo from "assets/microsoft-logo.png" import MicrosoftLogo from "assets/microsoft-logo.png"
import { admin, oidc } from "stores/portal" import { oidc } from "stores/portal"
import { onMount } from "svelte" import { onMount } from "svelte"
let show = false let show = false
@ -27,7 +27,8 @@
{#if show} {#if show}
<ActionButton <ActionButton
on:click={() => window.open(`/api/admin/auth/oidc/${$oidc.uuid}`, "_blank")} on:click={() =>
window.open(`/api/admin/auth/oidc/configs/${$oidc.uuid}`, "_blank")}
> >
<div class="inner"> <div class="inner">
<img {src} alt="oidc icon" /> <img {src} alt="oidc icon" />

View File

@ -79,10 +79,6 @@
providers.oidc?.config?.configs[0].clientID && providers.oidc?.config?.configs[0].clientID &&
providers.oidc?.config?.configs[0].clientSecret providers.oidc?.config?.configs[0].clientSecret
$: oidcCallback = providers.oidc?.config.configs[0].uuid
? `/api/admin/auth/oidc/callback/${providers.oidc?.config.configs[0].uuid}`
: ""
async function uploadLogo(file) { async function uploadLogo(file) {
let data = new FormData() let data = new FormData()
data.append("file", file) data.append("file", file)
@ -240,7 +236,7 @@
{/each} {/each}
<div class="form-row"> <div class="form-row">
<Label size="L">Callback URL</Label> <Label size="L">Callback URL</Label>
<Input readonly bind:value={oidcCallback} /> <Input readonly placeholder="/api/admin/auth/oidc/callback" />
</div> </div>
<br /> <br />
<Body size="S"> <Body size="S">

View File

@ -4,7 +4,8 @@ const { oidc } = require("@budibase/auth/src/middleware")
const { Configs, EmailTemplatePurpose } = require("../../../constants") const { Configs, EmailTemplatePurpose } = require("../../../constants")
const CouchDB = require("../../../db") const CouchDB = require("../../../db")
const { sendEmail, isEmailConfigured } = require("../../../utilities/email") const { sendEmail, isEmailConfigured } = require("../../../utilities/email")
const { clearCookie, getGlobalUserByEmail, hash } = authPkg.utils const { setCookie, getCookie, clearCookie, getGlobalUserByEmail, hash } =
authPkg.utils
const { Cookies } = authPkg.constants const { Cookies } = authPkg.constants
const { passport } = authPkg.auth const { passport } = authPkg.auth
const { checkResetPasswordCode } = require("../../../utilities/redis") const { checkResetPasswordCode } = require("../../../utilities/redis")
@ -133,9 +134,7 @@ exports.googleAuth = async (ctx, next) => {
)(ctx, next) )(ctx, next)
} }
async function oidcStrategyFactory(ctx) { async function oidcStrategyFactory(ctx, configId) {
const { configId } = ctx.params
const db = new CouchDB(GLOBAL_DB) const db = new CouchDB(GLOBAL_DB)
const config = await authPkg.db.getScopedConfig(db, { const config = await authPkg.db.getScopedConfig(db, {
@ -145,7 +144,7 @@ async function oidcStrategyFactory(ctx) {
const chosenConfig = config.configs.filter(c => c.uuid === configId)[0] const chosenConfig = config.configs.filter(c => c.uuid === configId)[0]
const callbackUrl = `${ctx.protocol}://${ctx.host}/api/admin/auth/oidc/callback/${configId}` const callbackUrl = `${ctx.protocol}://${ctx.host}/api/admin/auth/oidc/callback`
return oidc.strategyFactory(chosenConfig, callbackUrl) return oidc.strategyFactory(chosenConfig, callbackUrl)
} }
@ -155,7 +154,10 @@ async function oidcStrategyFactory(ctx) {
* On a successful login, you will be redirected to the oidcAuth callback route. * On a successful login, you will be redirected to the oidcAuth callback route.
*/ */
exports.oidcPreAuth = async (ctx, next) => { exports.oidcPreAuth = async (ctx, next) => {
const strategy = await oidcStrategyFactory(ctx) const { configId } = ctx.params
const strategy = await oidcStrategyFactory(ctx, configId)
setCookie(ctx, configId, Cookies.OIDC_CONFIG)
return passport.authenticate(strategy, { return passport.authenticate(strategy, {
// required 'openid' scope is added by oidc strategy factory // required 'openid' scope is added by oidc strategy factory
@ -164,7 +166,8 @@ exports.oidcPreAuth = async (ctx, next) => {
} }
exports.oidcAuth = async (ctx, next) => { exports.oidcAuth = async (ctx, next) => {
const strategy = await oidcStrategyFactory(ctx) const configId = getCookie(ctx, Cookies.OIDC_CONFIG)
const strategy = await oidcStrategyFactory(ctx, configId)
return passport.authenticate( return passport.authenticate(
strategy, strategy,

View File

@ -39,7 +39,7 @@ router
.post("/api/admin/auth/logout", authController.logout) .post("/api/admin/auth/logout", authController.logout)
.get("/api/admin/auth/google", authController.googlePreAuth) .get("/api/admin/auth/google", authController.googlePreAuth)
.get("/api/admin/auth/google/callback", authController.googleAuth) .get("/api/admin/auth/google/callback", authController.googleAuth)
.get("/api/admin/auth/oidc/:configId", authController.oidcPreAuth) .get("/api/admin/auth/oidc/configs/:configId", authController.oidcPreAuth)
.get("/api/admin/auth/oidc/callback/:configId", authController.oidcAuth) .get("/api/admin/auth/oidc/callback", authController.oidcAuth)
module.exports = router module.exports = router

View File

@ -1,4 +1,5 @@
const setup = require("./utilities") const setup = require("./utilities")
const { Cookies } = require("@budibase/auth").constants
jest.mock("nodemailer") jest.mock("nodemailer")
const sendMailMock = setup.emailMock() const sendMailMock = setup.emailMock()
@ -74,13 +75,13 @@ describe("/api/admin/auth", () => {
afterEach(() => { afterEach(() => {
expect(strategyFactory).toBeCalledWith( expect(strategyFactory).toBeCalledWith(
chosenConfig, chosenConfig,
`http://127.0.0.1:4003/api/admin/auth/oidc/callback/${configId}` // calculated url `http://127.0.0.1:4003/api/admin/auth/oidc/callback` // calculated url
) )
}) })
describe("/api/admin/auth/oidc", () => { describe("/api/admin/auth/oidc/configs", () => {
it("should load strategy and delegate to passport", async () => { it("should load strategy and delegate to passport", async () => {
await request.get(`/api/admin/auth/oidc/${configId}`) await request.get(`/api/admin/auth/oidc/configs/${configId}`)
expect(passportSpy).toBeCalledWith(mockStrategyReturn, { expect(passportSpy).toBeCalledWith(mockStrategyReturn, {
scope: ["profile", "email"], scope: ["profile", "email"],
@ -91,7 +92,8 @@ describe("/api/admin/auth", () => {
describe("/api/admin/auth/oidc/callback", () => { describe("/api/admin/auth/oidc/callback", () => {
it("should load strategy and delegate to passport", async () => { it("should load strategy and delegate to passport", async () => {
await request.get(`/api/admin/auth/oidc/callback/${configId}`) await request.get(`/api/admin/auth/oidc/callback`)
.set(config.getOIDConfigCookie(configId))
expect(passportSpy).toBeCalledWith(mockStrategyReturn, { expect(passportSpy).toBeCalledWith(mockStrategyReturn, {
successRedirect: "/", failureRedirect: "/error" successRedirect: "/", failureRedirect: "/error"

View File

@ -68,6 +68,12 @@ class TestConfiguration {
} }
} }
cookieHeader(cookies) {
return {
Cookie: [cookies],
}
}
defaultHeaders() { defaultHeaders() {
const user = { const user = {
_id: "us_uuid1", _id: "us_uuid1",
@ -77,7 +83,7 @@ class TestConfiguration {
const authToken = jwt.sign(user, env.JWT_SECRET) const authToken = jwt.sign(user, env.JWT_SECRET)
return { return {
Accept: "application/json", Accept: "application/json",
Cookie: [`${Cookies.Auth}=${authToken}`], ...this.cookieHeader([`${Cookies.Auth}=${authToken}`]),
} }
} }
@ -156,6 +162,11 @@ class TestConfiguration {
) )
} }
getOIDConfigCookie(configId) {
const token = jwt.sign(configId, env.JWT_SECRET)
return this.cookieHeader([[`${Cookies.OIDC_CONFIG}=${token}`]])
}
async saveOIDCConfig() { async saveOIDCConfig() {
await this.deleteConfig(Configs.OIDC) await this.deleteConfig(Configs.OIDC)
const config = { const config = {