Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit134750bunknown_key

fix(auth+pwa): CSRF tokens on auth forms + bump SW cache to v2

fix(auth+pwa): CSRF tokens on auth forms + bump SW cache to v2

CSRF: register + login + 2FA forms now emit the hidden _csrf field that
the csrfProtect middleware checks. Without it, returning users with a
stale session cookie hit '403 CSRF token invalid' on registration.

Service worker: bump cache name from gluecron-shell-v1 to v2. The old
v1 cache held the pre-redesign HTML shell and was being served to
returning visitors instead of the new design ('1980s look' bug). v2
forces every browser to purge v1 on next visit.

Verified: 1234 pass / 1 unrelated pre-existing fail.
Claude committed on May 3, 2026Parent: 5d82f61
2 files changed+73134750b32beab4c325d0541a90561482bec261e2
2 changed files+7−3
Modifiedsrc/routes/auth.tsx+5−1View fileUnifiedSplit
3939
4040auth.get("/register", (c) => {
4141 const error = c.req.query("error");
42 const csrf = c.get("csrfToken") as string | undefined;
4243 return c.html(
4344 <Layout title="Register">
4445 <div class="auth-container">
4546 <h2>Create account</h2>
4647 {error && <div class="auth-error">{decodeURIComponent(error)}</div>}
47 <Form method="post" action="/register">
48 <Form method="post" action="/register" csrfToken={csrf}>
4849 <FormGroup label="Username" htmlFor="username">
4950 <Input
5051 id="username"
185186 !!ssoCfg.clientId &&
186187 !!ssoCfg.clientSecret;
187188 const ssoLabel = ssoCfg?.providerName || "SSO";
189 const csrf = c.get("csrfToken") as string | undefined;
188190 return c.html(
189191 <Layout title="Sign in">
190192 <div class="auth-container">
193195 <Form
194196 method="post"
195197 action={`/login${redirect ? `?redirect=${encodeURIComponent(redirect)}` : ""}`}
198 csrfToken={csrf}
196199 >
197200 <FormGroup label="Username or email" htmlFor="username">
198201 <Input
382385 method="post"
383386 action={`/login/2fa?redirect=${encodeURIComponent(redirect)}`}
384387 >
388 <input type="hidden" name="_csrf" value={(c.get("csrfToken") as string | undefined) || ""} />
385389 <div class="form-group">
386390 <label for="code">Code</label>
387391 <input
Modifiedsrc/routes/pwa.ts+2−2View fileUnifiedSplit
6464 * - other → pass-through (the static CSS is inlined into the HTML, so there's
6565 * no cross-request asset worth caching for v1)
6666 */
67export const SERVICE_WORKER_SRC = `// gluecron service worker — v1
68const CACHE = 'gluecron-shell-v1';
67export const SERVICE_WORKER_SRC = `// gluecron service worker — v2 (post Editorial-Technical redesign)
68const CACHE = 'gluecron-shell-v2';
6969const SHELL = ['/', '/manifest.webmanifest', '/icon.svg'];
7070
7171self.addEventListener('install', (e) => {
7272