From c3f60448ad94d8fca0c37c39f822063078c0f65c Mon Sep 17 00:00:00 2001 From: Sienna Meridian Satterwhite Date: Mon, 6 Apr 2026 21:23:19 +0100 Subject: [PATCH] feat: configurable OAuth scopes and cookie security via env vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add three environment variables for deployments with external identity providers (Keycloak, Authentik, Ory Hydra, etc.): - OAUTH_EXTRA_SCOPES: append additional scopes to the default "openid email profile" (e.g. "offline_access" for refresh tokens) - OAUTH_SCOPES: full override of the requested OAuth scopes - COOKIE_SECURE: override the Secure flag on auth cookies (useful for reverse proxy setups where the internal hop is HTTP) Without these, deploying Bulwark with an external OIDC provider that requires `offline_access` for refresh tokens is impossible — sessions die on every page refresh because no refresh token is issued. All three are backwards-compatible: unset = identical to current behavior. --- lib/oauth/cookie-config.ts | 5 ++++- lib/oauth/tokens.ts | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/oauth/cookie-config.ts b/lib/oauth/cookie-config.ts index 3ef4b9b4..5e111a42 100644 --- a/lib/oauth/cookie-config.ts +++ b/lib/oauth/cookie-config.ts @@ -1,9 +1,12 @@ const COOKIE_SAME_SITE = (process.env.COOKIE_SAME_SITE || 'lax') as 'lax' | 'none' | 'strict'; +const COOKIE_SECURE = process.env.COOKIE_SECURE !== undefined + ? process.env.COOKIE_SECURE === 'true' + : (COOKIE_SAME_SITE === 'none' || process.env.NODE_ENV === 'production'); export function getCookieOptions() { return { httpOnly: true, - secure: COOKIE_SAME_SITE === 'none' || process.env.NODE_ENV === 'production', + secure: COOKIE_SECURE, sameSite: COOKIE_SAME_SITE, path: '/', maxAge: 30 * 24 * 60 * 60, diff --git a/lib/oauth/tokens.ts b/lib/oauth/tokens.ts index 5f05e9d3..6545bab0 100644 --- a/lib/oauth/tokens.ts +++ b/lib/oauth/tokens.ts @@ -1,4 +1,6 @@ -export const OAUTH_SCOPES = 'openid email profile'; +const DEFAULT_SCOPES = 'openid email profile'; +const EXTRA_SCOPES = process.env.OAUTH_EXTRA_SCOPES || ''; +export const OAUTH_SCOPES = process.env.OAUTH_SCOPES || (EXTRA_SCOPES ? `${DEFAULT_SCOPES} ${EXTRA_SCOPES}`.trim() : DEFAULT_SCOPES); export const REFRESH_TOKEN_COOKIE = 'jmap_rt'; /** Get the cookie name for a given account slot (0-4). Slot 0 uses the legacy name. */