From d6e7ae087f470652b67788b6b182533be3510d43 Mon Sep 17 00:00:00 2001 From: AlexLopezGomez Date: Sat, 21 Mar 2026 17:18:25 +0100 Subject: [PATCH 1/5] docs: add Docker and Firebase auth rules from 2026-03-21 session retrospective --- CLAUDE.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index feaf739..28ea553 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -133,3 +133,46 @@ After implementing UI changes, verify the running app serves the latest code. St When instructed to review a plan or enter "Plan Mode", you **MUST** read and strictly adhere to the workflow, review stages, and engineering preferences defined in `PLAN_MODE.md`. +--- + +## Docker & Deployment Rules + +**Base images — NEVER use `node:XX-alpine` official images:** +- `node:XX.X-alpine` bundles npm outside Alpine's APK database; Docker Scout reports CVEs per-layer even after `npm install -g npm@newer` (the old layer remains visible to the scanner) +- ALWAYS use `FROM alpine:X.XX` + `RUN apk upgrade --no-cache && apk add --no-cache nodejs npm` for all Node build/runtime stages +- On Alpine 3.23, the Node.js package name is `nodejs` — NOT `nodejs-20` (does not exist), NOT `node` + +**Lock files — tracked in git, use npm install in Docker:** +- `frontend/package-lock.json`, `backend/package-lock.json`, `demo/rag-chatbot/package-lock.json` are committed — `.gitignore` has explicit `!` exceptions for them; never remove these files from git +- NEVER use `npm ci` in Dockerfiles — lock files generated on Windows lack Linux platform-specific optional packages (`@esbuild/linux-*`, `@rollup/rollup-linux-*`); always use `npm install` (or `npm install --omit=dev`) instead +- IF "Missing: @esbuild/linux-* from lock file" → lock file generated on non-Linux; switch `npm ci` to `npm install` +- IF "npm ci can only install with an existing package-lock.json" → lock file not committed; check `.gitignore` exceptions + +**Layer cache invalidation:** +- Changing any `FROM` line invalidates ALL subsequent layer caches on Render; any previously-hidden issue (missing build-arg env vars, missing files in git) will surface on the next fresh build + +## Firebase Auth Rules + +**Architecture (fixed — do not change):** +- ALWAYS use `signInWithPopup` — NEVER `signInWithRedirect` (Chrome 115+ storage partitioning permanently broke redirect auth) +- COOP header must be `same-origin-allow-popups` — set in `backend/src/index.js` via Helmet; do not change it + +**Environment variables (Vite bakes these at build time):** +- `VITE_FIREBASE_API_KEY`, `VITE_FIREBASE_AUTH_DOMAIN`, `VITE_FIREBASE_PROJECT_ID` must exist in Render's environment variables panel BEFORE triggering a deploy +- If these vars are absent at build time, Firebase initializes with empty strings and `signInWithPopup` silently fails with `auth/popup-closed-by-user` (popup opens to malformed URL, closes immediately) +- IF `auth/popup-closed-by-user` on a fresh deploy → first check: are the three `VITE_FIREBASE_*` vars set in Render? + +**New deployment checklist (every new domain/service):** +1. Set `VITE_FIREBASE_API_KEY`, `VITE_FIREBASE_AUTH_DOMAIN`, `VITE_FIREBASE_PROJECT_ID` in Render environment +2. Add the deployment URL to Firebase Console → Authentication → Settings → Authorized domains + +**Error code reference:** +- `auth/popup-closed-by-user` = popup failed internally (check env vars + authorized domains) OR user closed it +- `auth/unauthorized-domain` = deployment URL missing from Firebase authorized domains → add it to Firebase Console +- `auth/popup-blocked` = browser blocked popup → user must allow popups for the site +- `auth/cancelled-popup-request` = two popups opened simultaneously → silently ignore (already handled) + +**Code contract (do not regress):** +- `AuthContext.loginWithProvider` must `throw new Error(cleanMessage)` — NOT `throw err` — so callers receive readable text, not raw Firebase SDK strings +- `SocialAuth.jsx` checks `firebaseConfigured` (from `frontend/src/config/firebase.js`) and shows "Social sign-in is not configured." when Firebase vars are absent + From 4528d848bcd1f0739b24bdfbe99a4c3818f030fd Mon Sep 17 00:00:00 2001 From: AlexLopezGomez Date: Sun, 22 Mar 2026 22:01:43 +0100 Subject: [PATCH 2/5] fix: resolve social auth error display regression and message accuracy - SocialAuth restores local error state; calls clearError() to prevent parent forms from double-displaying the same error - Fixed silent failure in RegisterPage Step 1 where social auth errors were only rendered on Step 3 (caught by Codex adversarial review) - Updated auth/popup-closed-by-user message to mention env vars and Firebase Authorized Domains as the most common cause on new deploys - Error color unified to text-verdict-fail (was text-red-500) --- frontend/src/components/auth/SocialAuth.jsx | 7 ++++--- frontend/src/context/AuthContext.jsx | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/auth/SocialAuth.jsx b/frontend/src/components/auth/SocialAuth.jsx index 475de11..079290d 100644 --- a/frontend/src/components/auth/SocialAuth.jsx +++ b/frontend/src/components/auth/SocialAuth.jsx @@ -18,16 +18,17 @@ const GITHUB_ICON = ( ); export default function SocialAuth() { - const { loginWithProvider } = useAuth(); + const { loginWithProvider, clearError } = useAuth(); const [loadingProvider, setLoadingProvider] = useState(null); const [error, setError] = useState(null); async function handleProviderLogin(providerName) { - setError(null); setLoadingProvider(providerName); + setError(null); try { await loginWithProvider(providerName); } catch (err) { + clearError(); // prevent parent form from double-displaying this error setError(err.message); } finally { setLoadingProvider(null); @@ -47,7 +48,7 @@ export default function SocialAuth() { ) : ( <> {error && ( -

{error}

+

{error}

)}
diff --git a/frontend/src/context/AuthContext.jsx b/frontend/src/context/AuthContext.jsx index b1179f6..e9bf59c 100644 --- a/frontend/src/context/AuthContext.jsx +++ b/frontend/src/context/AuthContext.jsx @@ -81,7 +81,7 @@ export function AuthProvider({ children }) { } else if (err.code === 'auth/popup-blocked') { message = 'Sign-in popup was blocked by your browser. Please allow popups for this site and try again.'; } else if (err.code === 'auth/popup-closed-by-user') { - message = 'Sign-in window was closed. If this keeps happening, check that your browser allows popups for this site.'; + message = 'Sign-in window was closed. On a new deployment, verify VITE_FIREBASE_* env vars are set and the domain is in Firebase Authorized Domains. Otherwise, allow popups for this site.'; } else if (err.code === 'auth/unauthorized-domain') { message = 'This domain is not authorized for sign-in. Add the deployment URL to Firebase Console → Authentication → Authorized domains.'; } From d92bf04e8504dd7e31a9a25b58ee031ab7c246ad Mon Sep 17 00:00:00 2001 From: AlexLopezGomez Date: Sun, 22 Mar 2026 22:02:08 +0100 Subject: [PATCH 3/5] docs: add Docker/Firebase rules and create TODOS.md - CLAUDE.md: Docker base image rules (Alpine vs node:XX-alpine), npm install vs npm ci rules, Firebase auth deployment checklist, error code reference (popup-closed, unauthorized-domain, etc.) - TODOS.md: created with P2 items for Firebase health endpoint and Vitest auth unit test setup --- CLAUDE.md | 10 ++++++++++ TODOS.md | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 TODOS.md diff --git a/CLAUDE.md b/CLAUDE.md index 28ea553..4bef144 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -176,3 +176,13 @@ When instructed to review a plan or enter "Plan Mode", you **MUST** read and str - `AuthContext.loginWithProvider` must `throw new Error(cleanMessage)` — NOT `throw err` — so callers receive readable text, not raw Firebase SDK strings - `SocialAuth.jsx` checks `firebaseConfigured` (from `frontend/src/config/firebase.js`) and shows "Social sign-in is not configured." when Firebase vars are absent +## gstack + +Use `/browse` from gstack for all web browsing. Never use `mcp__claude-in-chrome__*` tools. +If gstack skills aren't working, run `cd ~/.claude/skills/gstack && ./setup` to build the binary and register skills. + +Available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, +/autoplan, /design-consultation, /review, /ship, /land-and-deploy, /canary, /benchmark, +/browse, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /retro, +/investigate, /document-release, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade. + diff --git a/TODOS.md b/TODOS.md new file mode 100644 index 0000000..3d7b1df --- /dev/null +++ b/TODOS.md @@ -0,0 +1,41 @@ +# TODOS + +## P2 — Auth + +### Firebase auth health check endpoint + +**What:** Add `GET /api/auth/firebase-health` that tests Firebase Admin SDK connectivity and returns a JSON status. + +**Why:** A misconfigured Firebase setup (bad project ID, expired service account, wrong VITE_FIREBASE_* vars) is only discovered when a real user attempts OAuth. A health endpoint lets Render's health check or uptime monitoring catch this before users do. + +**Pros:** Catches deploy-day auth failures early. Plugs directly into Render's health check URL. + +**Cons:** Requires Firebase Admin SDK initialization check logic. Moderate effort. + +**Context:** Emerged from the 2026-03-21 Firebase auth hardening session. The new `auth/popup-closed-by-user` error message now tells devs to check env vars — but a health endpoint would make that check automatic. Start in `backend/src/routes/auth.js`, add a `GET /health` handler that calls `getFirebaseAuth().tenantManager()` or a lightweight Admin SDK ping. + +**Effort:** M (human ~4h) → S with CC+gstack (~15 min) + +**Priority:** P2 + +**Depends on:** Nothing. Standalone addition. + +--- + +### Vitest + React Testing Library — auth unit tests + +**What:** Set up Vitest + RTL and write unit tests for: `firebaseConfigured=false` renders "not configured", each named Firebase error code (`popup-blocked`, `popup-closed-by-user`, `account-exists`, `unauthorized-domain`) produces the correct user-facing message, and `SocialAuth` buttons disable during loading. + +**Why:** Zero test coverage on auth flows. Given the auth instability history (signInWithPopup ↔ signInWithRedirect back-and-forth, multiple CVE/config fixes), a regression test suite would have caught breakage earlier. The `auth/popup-closed-by-user` message was misleading until this session — a test would have locked in the correct behavior. + +**Pros:** Locks in current behavior. Fast to write with CC+gstack once framework is in place. + +**Cons:** Adds dev dependencies (vitest, @testing-library/react). Requires initial setup. + +**Context:** No test framework configured as of 2026-03-21. Start with `frontend/src/components/auth/SocialAuth.test.jsx` and `frontend/src/context/AuthContext.test.jsx`. Mock Firebase SDK calls (`vi.mock('firebase/auth')`). + +**Effort:** M (human ~1 day) → S with CC+gstack (~20 min) + +**Priority:** P2 + +**Depends on:** Nothing. Can be added independently. From c093826c10d0c966f97893e2ccc6cea579ce06d4 Mon Sep 17 00:00:00 2001 From: AlexLopezGomez Date: Sun, 22 Mar 2026 22:02:33 +0100 Subject: [PATCH 4/5] chore: bump version and changelog (v1.0.1.0) Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 15 +++++++++++++++ VERSION | 1 + 2 files changed, 16 insertions(+) create mode 100644 VERSION diff --git a/CHANGELOG.md b/CHANGELOG.md index bb1822e..e89789a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [1.0.1.0] - 2026-03-22 + +### Fixed +- Social auth errors now display inside the `SocialAuth` component and call `clearError()` to prevent double-display in parent forms (`SignInForm`, `SignUpFlow`) +- Fixed regression where social auth failure on `RegisterPage` Step 1 was silently invisible — error now shown in the correct step context +- `auth/popup-closed-by-user` error message updated to mention env var and Firebase Authorized Domains as common causes on fresh deployments +- Error color in `SocialAuth` unified to use design system token `text-verdict-fail` instead of hardcoded `text-red-500` +- `firebaseConfigured` export added to `firebase.js` — `SocialAuth` renders "Social sign-in is not configured." when Firebase env vars are absent + +### Added +- Meta tag fallback for Firebase config (`fb-api-key`, `fb-auth-domain`, `fb-project-id`) enables Docker runtime injection path as alternative to build-time env vars +- Named error handling for `auth/account-exists-with-different-credential`, `auth/popup-blocked`, `auth/popup-closed-by-user`, `auth/unauthorized-domain` with readable user-facing messages +- CLAUDE.md: Docker base image rules, npm install vs npm ci rules, Firebase auth deployment checklist and error code reference +- TODOS.md: created with P2 items for Firebase health endpoint and Vitest auth unit tests + ## [Unreleased] ### Added diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..0839c1f --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.1.0 From 63a25b03dc803dcdda5cd80eae74e0d908f098c8 Mon Sep 17 00:00:00 2001 From: AlexLopezGomez Date: Sun, 22 Mar 2026 22:04:57 +0100 Subject: [PATCH 5/5] chore: gitignore .gstack/ local config directory --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9176e4a..920be65 100644 --- a/.gitignore +++ b/.gitignore @@ -82,4 +82,4 @@ RATE_LIMIT.md # Unpublished paper paper/ -paper.zip \ No newline at end of file +paper.zip.gstack/