Skip to content

Authentication

Auth is Firebase Authentication, deliberately boring: Google sign-in (plus email/password), one context, one profile document. The interesting parts are two specific decisions and one bug.

The flow

diagram100%
rendering diagram…

drag to pan · ctrl / ⌘ + scroll to zoom

AuthContext exposes { user, profile, loading }; a route guard bounces unauthenticated visitors to /login. Everything per-user in Firestore keys off request.auth.uid — the rules are the enforcement, the context is just convenience.

Decision 1: indexedDBLocalPersistence

Firebase's default web persistence would be fine for a normal app. Ediky sets indexedDBLocalPersistence explicitly in firebase.js for one scenario: a student refreshes the page mid-timed-exam. Persistence must survive that refresh so the user is still signed in when the page reloads and the attempt document (with its server startedAt) can be re-read — otherwise a refresh means logout means a burned attempt. The timer-integrity story in the OA Simulator and CP System starts here.

Decision 2: profile doc ≠ auth record

The users/{uid} document mirrors display fields (name, photo, headline, college, targets) so the app never round-trips to the Auth SDK for render data, and so server-side consumers (the reminder Worker) can read what they need over the Firestore REST API without Auth Admin access.

The bug that fixed the design

Mirroring created a staleness class: email was originally written only at profile creation. Older accounts and changed-email accounts had missing/stale values — and the reminder Worker, which reads email from the profile, silently skipped them. Fix: AuthContext now re-syncs email on every sign-in, and a one-time scripts/backfill-emails.mjs patched existing docs. Full post-mortem in Scheduling; the general rule it produced: every mirrored field must name the mechanism that keeps it fresh.

Server-side identity is separate

Pages Functions never see Firebase Auth — they're either stateless graders or proxies. The two Workers authenticate as a service account (RS256 JWT → Google OAuth2 token → Firestore REST), which bypasses security rules entirely; their safety comes from preconditions and claims, documented in Workers. Client identity and server identity never mix.

Settings surface

/settings edits the profile doc (avatar via Cloudinary upload), theme, and the Codeforces handle — validated against cf-profile at save so a typo'd handle fails loudly instead of making CP verification look broken. A past derived-state bug here (edits computed from stale props) is written up in Troubleshooting.

Ediky Workflow — internal engineering documentation.