From 9fb2a3676c5070df943151e398768164b4d87c9a Mon Sep 17 00:00:00 2001 From: mjnong Date: Thu, 13 Aug 2026 09:20:15 +0200 Subject: [PATCH 1/2] docs: correct the consumer list and the token-persistence claim Three claims in AGENTS.md were false against the code, verified by grep: - `cellarnode-mobile-app` was listed as an OTP consumer. It has no dependency, no import and no lockfile entry for @cellarnode/auth. - `cellarnode-admin-dashboard-v2` was listed under "NOT used by". It depends on ^0.14.0 and imports createAuthStore in src/auth/auth-store.ts to hold the local-dev /test/login JWE and attach Authorization: Bearer to outbound requests (Ably authUrl). It is a core-store consumer; only the OTP flow and the React components are unused there. - createAuthStore was documented as persisting to localStorage with an expo-secure-store adapter for mobile. There is no localStorage in src/auth-store.ts and no storage-adapter seam in AuthStoreConfig; the token lives in a module closure and durability comes from the HttpOnly refresh cookie sent via credentials: "include". The corrected list matters beyond tidiness: all four real consumers are Vite, so there is no Metro consumer to anchor a "bundler-agnostic" claim. CEL-1364 declined an import.meta.env gate inside devLogin because the core entry must import under plain Node ESM, and the Scope section now says so explicitly rather than implying a React Native constraint. Also adds AuthError to the README core-export list, which enumerated every other value export from src/index.ts. Re-verified the two PR #15 fixes on main and both still hold: there is no Makefile, and the four documented commands match .github/workflows/ci.yml exactly. Both React export lists match the src barrels. --- AGENTS.md | 23 ++++++++++++++++++++--- README.md | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 03314f6..a499806 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,12 +4,29 @@ Shared OTP-based authentication — token store, API client, React UI components ## Scope -Powers OTP auth in: `producer-dashboard`, `cellarnode-importer-dashboard`, `cellarnode-mobile-app`, `cellarnode-elabel-frontend` (producer-side `/app/*`). +Consumed by four SPAs, every one of them built with Vite: + +- **Full OTP flow + React components** — `producer-dashboard`, + `cellarnode-importer-dashboard`, `cellarnode-elabel-frontend` (producer-side + `/app/*`). +- **Core store only** — `cellarnode-admin-dashboard-v2`. Its production auth is the + GitHub OAuth BFF (CEL-142+, see root AGENTS.md "Authentication / SSO Direction"), + but it does depend on this package: `src/auth/auth-store.ts` imports + `createAuthStore` to hold the local-dev `/test/login` JWE and to attach + `Authorization: Bearer` to outbound requests such as the Ably `authUrl` POST. It + renders nothing from `@cellarnode/auth/react`. **NOT used by:** -- `cellarnode-admin-dashboard-v2` — uses GitHub OAuth BFF (CEL-142+, see root AGENTS.md "Authentication / SSO Direction"). +- `cellarnode-mobile-app` — no dependency, no import, no lockfile entry. It does not + consume this package at all. - `cellarnode-public-site` — marketing-only, no auth. +There is therefore **no React Native / Metro consumer**. When the docs below call the +core entry "bundler-agnostic", the case that must keep working is **plain Node ESM** +(vitest, scripts, any non-Vite importer) — not Metro. That is the whole basis on which +CEL-1364 declined an `import.meta.env` gate inside `devLogin`; do not restate it as a +React Native constraint. + ## Stack - TypeScript 5.x. Framework-agnostic core + React subpath. @@ -44,7 +61,7 @@ The dev-bypass internals (`DevSignInBypass`, `DEV_LOGIN_EMAIL_STORAGE_KEY`, ### Core API -- `createAuthStore({ baseUrl })` — token persistence (localStorage in browser; mobile uses an `expo-secure-store` adapter on the consumer side). Also exposes `devLogin(email)` (CEL-1364) — see "Dev sign-in bypass". +- `createAuthStore({ baseUrl })` — holds the access token in a **module-closure variable, not `localStorage`**; durability across reloads comes from the backend's HttpOnly refresh cookie, which `performRefresh()` sends with `credentials: "include"`. `AuthStoreConfig` is `{ baseUrl, refreshPath?, refreshBuffer? }` — there is no storage-adapter seam. Also exposes `devLogin(email)` (CEL-1364) — see "Dev sign-in bypass". - `createAuthClient({ baseUrl, store, onAuthFailure })` — fetch wrapper, auto-attaches Bearer, calls `onAuthFailure` on 401. - `createAuthApi({ client, store })` — typed login/register/logout helpers. - `validateUserType(userType)` — `"producer" | "importer" | "distributor" | "admin"`. diff --git a/README.md b/README.md index 087f07c..c199e51 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Add this to your CSS file so Tailwind picks up utility classes from the package: ## Exports -- `@cellarnode/auth` — Core: `createAuthStore`, `createAuthClient`, `createAuthApi`, `validateUserType`, `hasEntitlement`, `extractAccessToken`, types (incl. `DevLoginResult`) +- `@cellarnode/auth` — Core: `createAuthStore`, `createAuthClient`, `createAuthApi`, `validateUserType`, `hasEntitlement`, `extractAccessToken`, `AuthError`, types (incl. `DevLoginResult`) - `@cellarnode/auth/react` — React: `LoginForm`, `RegisterForm`, `UnauthorizedPage`, `SquircleShift`, `InputOTP` (+ `Group` / `Slot` / `Separator`) `DevSignInBypass`, `DEV_LOGIN_EMAIL_STORAGE_KEY`, `readDevLoginEmail` and From a24841375d57869429b87befb611125b5a304435 Mon Sep 17 00:00:00 2001 From: mjnong Date: Thu, 13 Aug 2026 09:43:39 +0200 Subject: [PATCH 2/2] docs: correct the admin-v2 createAuthStore claim (reviewer P1) --- AGENTS.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a499806..0384da6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,12 +9,25 @@ Consumed by four SPAs, every one of them built with Vite: - **Full OTP flow + React components** — `producer-dashboard`, `cellarnode-importer-dashboard`, `cellarnode-elabel-frontend` (producer-side `/app/*`). -- **Core store only** — `cellarnode-admin-dashboard-v2`. Its production auth is the - GitHub OAuth BFF (CEL-142+, see root AGENTS.md "Authentication / SSO Direction"), - but it does depend on this package: `src/auth/auth-store.ts` imports - `createAuthStore` to hold the local-dev `/test/login` JWE and to attach - `Authorization: Bearer` to outbound requests such as the Ably `authUrl` POST. It - renders nothing from `@cellarnode/auth/react`. +- **Vestigial core-store import** — `cellarnode-admin-dashboard-v2`. It declares the + dependency and `src/auth/auth-store.ts:22` imports `createAuthStore`, but the + resulting store is effectively unused. All of its auth — production *and* local dev + — flows through the GitHub OAuth BFF's HttpOnly `cellarnode_session` cookie (CEL-142+, + see root AGENTS.md "Authentication / SSO Direction"). Concretely, post-CEL-170: + - nothing in the SPA calls `setAccessToken`, so the store never holds a token. The + dev-only `/test/login` bypass stopped committing the JWE in CEL-170 and now only + branches on the response status (`src/auth/dev-login.ts:24-28`). + - nothing attaches `Authorization: Bearer` from it. The Ably `authUrl` POST to + `/ably-token` is same-origin and rides the cookie; the old `getAccessToken()` + Bearer fallback was deleted as dead code + (`src/components/layout/AuthenticatedShell.tsx:172-181`). + - the single surviving call is `authStore.clearAccessToken()` on logout + (`src/pages/auth/LogoutPage.tsx:32`), which clears an always-empty store. + + So treat admin-v2 as a dependency-of-record, not a behavioral consumer: changes to + store semantics do not affect it, though removing `createAuthStore` or + `clearAccessToken` from the public API would still break its build. It renders + nothing from `@cellarnode/auth/react`. **NOT used by:** - `cellarnode-mobile-app` — no dependency, no import, no lockfile entry. It does not