From bc582a64ecaa0044d8a42d156c4c614e21d9edc6 Mon Sep 17 00:00:00 2001 From: UniDesk Codex Date: Tue, 30 Jun 2026 02:34:31 +0800 Subject: [PATCH] fix: allow opencode iframe session cookies --- deploy/deploy.yaml | 1 + internal/cloud/access-control.ts | 33 ++++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml index 819895ad..2088f0bc 100644 --- a/deploy/deploy.yaml +++ b/deploy/deploy.yaml @@ -738,6 +738,7 @@ lanes: HWLAB_CLOUD_DB_CONTRACT: v03-redacted-presence-only HWLAB_ACCESS_CONTROL_REQUIRED: "1" HWLAB_SESSION_COOKIE_DOMAIN: .pikapython.com + HWLAB_SESSION_COOKIE_SAMESITE: None HWLAB_BOOTSTRAP_ADMIN_ID: usr_v03_admin HWLAB_BOOTSTRAP_ADMIN_USERNAME: admin HWLAB_BOOTSTRAP_ADMIN_DISPLAY_NAME: HWLAB v0.3 Admin diff --git a/internal/cloud/access-control.ts b/internal/cloud/access-control.ts index 802a53a2..12917a24 100644 --- a/internal/cloud/access-control.ts +++ b/internal/cloud/access-control.ts @@ -17,14 +17,14 @@ import { createUserBillingClient } from "./user-billing-client.ts"; const SESSION_COOKIE = "hwlab_session"; const SESSION_MAX_AGE_SECONDS = 60 * 60 * 24; const SESSION_COOKIE_SAMESITE = "Lax"; -function buildSessionCookie(token, maxAge, { secure = false, domain = "" } = {}) { - return `${SESSION_COOKIE}=${encodeURIComponent(token)}; ${sessionCookieAttributes(maxAge, { secure, domain })}`; +function buildSessionCookie(token, maxAge, { secure = false, domain = "", sameSite = SESSION_COOKIE_SAMESITE } = {}) { + return `${SESSION_COOKIE}=${encodeURIComponent(token)}; ${sessionCookieAttributes(maxAge, { secure, domain, sameSite })}`; } -function buildClearSessionCookie({ secure = false, domain = "" } = {}) { - return `${SESSION_COOKIE}=; ${sessionCookieAttributes(0, { secure, domain })}`; +function buildClearSessionCookie({ secure = false, domain = "", sameSite = SESSION_COOKIE_SAMESITE } = {}) { + return `${SESSION_COOKIE}=; ${sessionCookieAttributes(0, { secure, domain, sameSite })}`; } -function sessionCookieAttributes(maxAge, { secure = false, domain = "" } = {}) { - return ["Path=/", "HttpOnly", secure ? "Secure" : "", sessionCookieDomainAttribute(domain), `SameSite=${SESSION_COOKIE_SAMESITE}`, `Max-Age=${maxAge}`] +function sessionCookieAttributes(maxAge, { secure = false, domain = "", sameSite = SESSION_COOKIE_SAMESITE } = {}) { + return ["Path=/", "HttpOnly", secure ? "Secure" : "", sessionCookieDomainAttribute(domain), `SameSite=${normalizeSessionCookieSameSite(sameSite)}`, `Max-Age=${maxAge}`] .filter(Boolean) .join("; "); } @@ -37,6 +37,17 @@ function normalizeSessionCookieDomain(value) { if (!domain) return ""; return /^\.?[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/u.test(domain) ? domain : ""; } +function normalizeSessionCookieSameSite(value) { + const normalized = String(value ?? "").trim().toLowerCase(); + if (normalized === "none") return "None"; + if (normalized === "strict") return "Strict"; + if (normalized === "lax") return "Lax"; + return SESSION_COOKIE_SAMESITE; +} +function sessionCookieSameSiteForResponse(env, secure) { + const sameSite = normalizeSessionCookieSameSite(env.HWLAB_SESSION_COOKIE_SAMESITE); + return sameSite === "None" && !secure ? SESSION_COOKIE_SAMESITE : sameSite; +} const API_KEY_PREFIX = "hwl_live_"; const API_KEY_RANDOM_BYTES = 32; const API_KEY_DEFAULT_NAME = "Default API key"; @@ -2474,17 +2485,19 @@ function parseCookie(value) { return Object.fromEntries(String(value ?? "").spli function setSessionCookie(response, token, maxAge, request, env = process.env) { const secure = shouldUseSecureSessionCookie(request, env); const domain = normalizeSessionCookieDomain(env.HWLAB_SESSION_COOKIE_DOMAIN); + const sameSite = sessionCookieSameSiteForResponse(env, secure); const cookies = domain - ? [buildSessionCookie(token, maxAge, { secure, domain }), buildClearSessionCookie({ secure })] - : [buildSessionCookie(token, maxAge, { secure })]; + ? [buildSessionCookie(token, maxAge, { secure, domain, sameSite }), buildClearSessionCookie({ secure, sameSite })] + : [buildSessionCookie(token, maxAge, { secure, sameSite })]; response.setHeader("set-cookie", cookies); } function clearSessionCookie(response, request, env = process.env) { const secure = shouldUseSecureSessionCookie(request, env); const domain = normalizeSessionCookieDomain(env.HWLAB_SESSION_COOKIE_DOMAIN); + const sameSite = sessionCookieSameSiteForResponse(env, secure); const cookies = domain - ? [buildClearSessionCookie({ secure, domain }), buildClearSessionCookie({ secure })] - : [buildClearSessionCookie({ secure })]; + ? [buildClearSessionCookie({ secure, domain, sameSite }), buildClearSessionCookie({ secure, sameSite })] + : [buildClearSessionCookie({ secure, sameSite })]; response.setHeader("set-cookie", cookies); } function shouldUseSecureSessionCookie(request, env = process.env) {