fix(auth): remove the ?demo=1 PIN bypass
The query string skipped the PIN gate on any URL. The PIN is now the only way in. Docs updated; the login plan notes there is no fixed cutover date while the app is in on-site testing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
46e9e3dbc4
commit
16652bb36c
5 changed files with 9 additions and 12 deletions
|
|
@ -9,8 +9,8 @@ OCR-driven receipting. Includes an LLM chat ("Ask") with a read-only
|
|||
tool layer over the same data, and a read-only MCP server.
|
||||
|
||||
> **Default login PIN is `2468`.** Change `NEXT_PUBLIC_APP_PIN` in
|
||||
> production. Append `?demo=1` to any URL to bypass the PIN during
|
||||
> development.
|
||||
> production. There is no PIN bypass — the old `?demo=1` shortcut was
|
||||
> removed on 2026-09-26.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -127,8 +127,8 @@ held in `sessionStorage` after a client-side string compare.
|
|||
- `AuthGate` (`src/components/auth-gate.tsx`) redirects to `/login` whenever
|
||||
the flag is missing, but does so client-side — every server component
|
||||
below it still runs on the server.
|
||||
- Append `?demo=1` to any dashboard URL to bypass the PIN entirely
|
||||
(`src/hooks/use-auth.tsx:27-30`).
|
||||
- The `?demo=1` PIN bypass was removed on 2026-09-26 — the PIN is the
|
||||
only way in.
|
||||
- There is **no per-user identity anywhere in the application**. Every user
|
||||
who knows the PIN sees every site's contract values, payroll rates, and
|
||||
margins.
|
||||
|
|
|
|||
|
|
@ -173,7 +173,8 @@ The dev server runs on **port 6004** (not 3000 — the user runs other
|
|||
apps on 3000; do not change this back to 3000). The `--webpack` flag
|
||||
is required because `@ducanh2912/next-pwa` is webpack-only.
|
||||
|
||||
Append `?demo=1` to any URL to skip the PIN gate.
|
||||
Enter the PIN (`NEXT_PUBLIC_APP_PIN`, default `2468`) on `/login`. The
|
||||
old `?demo=1` bypass was removed on 2026-09-26.
|
||||
|
||||
### 5.2 Production build
|
||||
|
||||
|
|
@ -221,7 +222,6 @@ breaks. This is left in-tree for context, not as a reference.
|
|||
| Flag | Default | Effect when on | Effect when off |
|
||||
| --- | --- | --- | --- |
|
||||
| `ENABLE_CHAT` | unset (= `false`) | `/api/chat` and `/dashboard/chat` work | Both return 404 / `notFound()` |
|
||||
| `?demo=1` query string | n/a | Skips the PIN gate on the current URL | Normal auth flow |
|
||||
| `NEXT_PUBLIC_DEFAULT_THEME` | `light` | Initial theme before user preference loads | " |
|
||||
| `CHAT_PROVIDER` | `anthropic` | `anthropic` if `ANTHROPIC_API_KEY` is set (or `ant auth login` profile); `minimax` / `openai` if `MINIMAX_API_KEY` is set |
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
|
||||
### Cutover timeline
|
||||
|
||||
> **Update 2026-09-26:** the app is still in on-site testing and production is months away, so there is **no fixed cutover date**. Testers keep the shared PIN (shown on the login page on purpose). Build this when it's needed, keep `AUTH_MODE=pin` until testing is done, and use the dates below only as the shape of the rollout.
|
||||
|
||||
| When | What | Who |
|
||||
| --- | --- | --- |
|
||||
| Sat 26 Sep – Sun 27 Sep | Tasks 1–11 built and merged; deployed with `AUTH_MODE=pin` (no visible change) | Dev |
|
||||
|
|
@ -819,7 +821,7 @@ export const PATCH = withUser(async (_req, { params }: { params: Promise<{ id: s
|
|||
|
||||
- [ ] **Step 1: Mode reaches the client.** `src/app/layout.tsx` (server) reads `authMode()` and passes `mode` into `<AuthProvider mode={mode}>`. `NEXT_PUBLIC_*` must **not** be used for the mode — a runtime env flip must not need a rebuild.
|
||||
- [ ] **Step 2: Login page.** `mode === "pin"` → today's keypad, unchanged. `mode === "users"` → email + password form posting to `/api/auth/login`; on 200 `router.replace(resolveRedirectTarget(location.search))`; show the server's error text; disable the button while submitting; `autoComplete="username"` / `"current-password"` so phones offer to save it.
|
||||
- [ ] **Step 3: Gate.** In users mode `AuthGate` renders children directly (the proxy already redirected), and the `?demo=1` bypass is **ignored** (only honoured in pin mode).
|
||||
- [ ] **Step 3: Gate.** In users mode `AuthGate` renders children directly (the proxy already redirected). (The old `?demo=1` bypass was removed on 2026-09-26 — do not reintroduce it.)
|
||||
- [ ] **Step 4: Sidebar footer.** Users mode: "Signed in as {name} · {ROLE_LABELS[role]}" + **Sign out** (POST `/api/auth/logout`, then `location.href = "/login"`). Pin mode: today's "Lock screen".
|
||||
- [ ] **Step 5: Nav by permission.** Hide groups the role lacks (`workforce.view`, `plant.view`, `financial.view`; `chat.use` for Ask). In Phase 1 a site manager sees everything except Ask and Users.
|
||||
- [ ] **Step 6:** Browser check both modes. Commit — `git commit -m "feat(auth): email login page and sign-out"`
|
||||
|
|
|
|||
|
|
@ -23,11 +23,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||
React.useEffect(() => {
|
||||
try {
|
||||
if (sessionStorage.getItem(PIN_VERIFIED_KEY) === "1") setAuthenticated(true);
|
||||
// Demo bypass: append ?demo=1 to any dashboard URL to skip the PIN screen.
|
||||
if (typeof window !== "undefined" && new URLSearchParams(window.location.search).get("demo") === "1") {
|
||||
setAuthenticated(true);
|
||||
sessionStorage.setItem(PIN_VERIFIED_KEY, "1");
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue