AUTH-CONTRACT — How Cetking login really works
Written 5 Oct 2026 from the actual code and database, not from plans. Updated 9 Oct 2026, 20:00 IST. Every AI tool and developer reads this before touching login. If code and this file disagree, the code wins and this file gets corrected.
Status at a glance (9 Oct 2026, 20:00 IST)
Universe login is LIVE for everyone (opened 6 Oct 2026, 08:27 IST, test week before the first rollout): https://cetking-platform.vercel.app/login
| Piece | Status |
|---|---|
| Join for Free | Login tabs, mobile OTP | ✅ Live — Ravneet confirmed Join free works on his phone |
| New students (permanent Cetking ID, 0 coins) | ✅ Live (switch ON) |
| Existing 414 students | ✅ Already linked — no migration needed |
| Google: Connect Gmail after OTP, Continue with Google (Login tab, first) | ✅ Live — phone test still pending |
| Google first, mobile second (unknown Gmail → mobile box → OTP → Gmail connected) | 🔵 Built 9 Oct 2026 on branch login-google-then-mobile, waiting for "go live" |
| Rav's chat login: mobile OTP only (no tabs, no Google) | 🔵 Same branch |
| Terms & Privacy line, home "Mobile Number | Join for Free" bar, profile Log out | ✅ Live |
| 1,000 coins at 100% profile | ⏳ Waiting for Ravneet's decisions |
| Open to everyone | ✅ 6 Oct 2026 — Ravneet removed CK_IDENTITY_PILOT_PHONES in Vercel (put it back to limit login to pilot numbers again) |
| Rolling login (returning students never re-login) | ✅ Live 6 Oct 2026 (PR #144). See SESSION-CARRYOVER.md |
| Carry cetking.com logins into the new app (no re-login when cetking.com moves) | 📝 Plan written — SESSION-CARRYOVER.md, step 2 |
| Google One Tap ("Continue as ") | 🔮 Future scope |
| Timetable | ✅ Back on the live site: https://cetking-platform.vercel.app/timetable |
PRs merged 5 Oct 2026: #134 (tabs + this doc), #135 (Google), #136 (Google first + profile logout),
#137 (Terms line), #138 (home bar, "Join for Free", Google logo).
Database migrations (Cetking Learn): ck_identity_universe_registration, ck_identity_google_signin.
The one rule
One person, one permanent Cetking ID, one coin wallet — on every site. Mobile OTP is the only way IN the first time. Gmail is only a faster way BACK. (Ravneet, 9 Oct 2026)
Mobile OTP / Google → Cetking identity (Cetking Learn database) → permanent person + student ID
→ Universe, Arena, attendance, TCY, coins, future apps
A website is only a front door (gateway). Front doors can be added or removed. The identity behind them never moves and is never copied.
Never: create a second user table, a second wallet, merge accounts by email or name, let a Gmail open an account that has no verified mobile, or award coins from anything the browser sends.
Note for anyone counting sign-ups: Supabase Auth (auth.users) gets a row every time someone taps
Google, even when Cetking refuses them. Those rows are attempts, not students. Real joins are
ck_identity.audit_log event registered; real students are public.students.
Where identity lives
- Database: Supabase project Cetking Learn (
suqcijtpfeaystltekfn). - Permanent person:
ck_leads.people. Student record:public.students. Coins:public.ck_coin_ledger. - Student ↔ person link:
ck_leads.person_links(namespacearena:suqcijtpfeaystltekfn:students). - The Universe's own project (Cetking One AI,
jfbauorxtmgvagwoyabr) holds no identity. - Check on 5 Oct 2026: 437 students = 414 real (all linked, no duplicate numbers, all numbers in
+91…format) + 23 test accounts. No migration was needed for existing students.
Front door 1 — cetking.com (WordPress, built by ChatGPT, live)
- Plugin
plugins/cetking-one(branchchatgpt/cetking-one-welcome-campaign, PRs #114–#116, #118, #119; still draft/unmerged). - Addresses:
cetking.com/wp-json/cetking-one/v1/+otp/verify,session/exchange,session/refresh,session/logout,me,devices,devices/revoke,google/start,google/status,profile,profile/save,enquiries,session/migrate. - Steps: MSG91 OTP in the browser → server re-checks the proof with MSG91 (
verifyAccessToken) → short-lived login ticket → device session. - Session = two random secrets in locked cookies (not readable by JavaScript, cetking.com only). No JWT. The database stores only scrambled (hashed) copies.
- All database work goes through
ck_one_auth,ck_one_google,ck_one_profile. - Deliberately refuses requests from other websites and only runs on exactly
https://cetking.com. So other sites cannot borrow it from the browser. Do not loosen this. - Sites are switched on per row in
ck_one_private.scopes. On 5 Oct 2026 there is one row:https://cetking.com(login on, registration on, welcome coins on). - Welcome coins:
ck_one_private.award_welcome+ck_one_private.welcome_grants, keyed by person, so 1,000 coins once per person across every site. Today cetking.com gives them at registration and at Google linking. - Old Arena login bridge (
legacy_until) ends 10 Oct 2026, ~04:20 IST. After that, old Arena logins must sign in again with OTP. To confirm with ChatGPT that this is intended. - Version note: branch code says 0.6.4; recorded live version was 0.6.2. Confirm before relying on it.
Front door 2 — Cetking Universe (Next.js on Vercel)
- Code:
cetking-one/lib/identity/*,cetking-one/app/api/auth/*(otp/verify,me,logout,google/start,google/callback),cetking-one/app/api/profile/*, screencetking-one/components/auth/OtpLogin.tsx, page/login, home quick-join barcetking-one/components/auth/QuickJoin.tsx, profile logoutcetking-one/components/profile/ProfileLogout.tsx. - OTP: MSG91 in the browser → Universe server re-checks the proof with MSG91 → session cookie
__Host-ck_session(random token; database stores only its hash). - Database functions (Cetking Learn, service role only):
ck_identity_login,ck_identity_me,ck_identity_logout,ck_identity_profile,ck_identity_profile_save,ck_identity_rate_hit,ck_identity_google, helperck_identity.ensure_student. - Settings (Vercel environment variables, Production only, server only; never in chat or code):
CK_IDENTITY_ENABLED,CK_IDENTITY_SUPABASE_URL,CK_IDENTITY_SUPABASE_SERVICE_KEY,CK_IDENTITY_SECRET,MSG91_AUTH_KEY,CK_IDENTITY_PILOT_PHONES(pilot numbers only),CK_IDENTITY_GOOGLE_ENABLED(trueshows the Google buttons). Browser:NEXT_PUBLIC_MSG91_WIDGET_ID,NEXT_PUBLIC_MSG91_TOKEN_AUTH. Because these are Production-only, preview (blue) builds cannot show login; login is tested on the live site, limited to the pilot numbers. https://vercel.com/cet-king-one/cetking-platform/settings/environment-variables
Current switches (6 Oct 2026, 08:27 IST) — open to everyone
CK_IDENTITY_PILOT_PHONESremoved → every number can log in; unknown numbers join free (new Cetking ID, 0 coins). To go back to pilot-only, add it again with the pilot numbers and redeploy.- New-student joining ON (
ck_identity.settings.allow_registration = true). - Google sign-in ON (
CK_IDENTITY_GOOGLE_ENABLED=true; Supabase redirect URL added:https://cetking-platform.vercel.app/api/auth/google/callback?ck_state=*). https://supabase.com/dashboard/project/suqcijtpfeaystltekfn/auth/url-configuration
New students
- Switch:
ck_identity.settings.allow_registration(one row). Off = new numbers get "not registered". - On: a verified new number gets a
public.studentsrow (signup_tool = 'Cetking Universe'), a person inck_leads.peopleand the link — throughck_identity.ensure_student, which uses the same locks and conflict checks as cetking.com registration, so the two sites can never create two students for one number. 0 coins. - Always on (fix): an existing student whose person link was missing is linked at login instead of being blocked.
- Dry-run tested (all undone) and confirmed by Ravneet on his phone (Join free works).
- Every new join also gets a lead card (
ck_leads.lead_cases) for the counsellors.
Google sign-in
- Same rules and same table as cetking.com (
ck_one_private.google_identities), so a Gmail connected on one site works on both. - Connect: only from a session opened by mobile OTP in the last 10 minutes on that browser.
Identity read from Supabase Auth's verified Google record (
auth.identities), never a typed email. One Google ↔ one person; conflicts refused. - Login: a connected Gmail opens a normal Universe session (
login_method = 'google') and goes straight back to the page the student started from. - Google first, mobile second (9 Oct 2026): a Gmail that is not connected yet is no longer
turned away. The callback keeps that verified Google account in a signed, httpOnly cookie
__Host-ck_google_pending(this browser only, 10 minutes; holds the Supabase Auth user id and a masked hint likera•••@gmail.com) and opens/login?mode=login&google=google_pending&next=…with the mobile box ready (title "One last step", the masked Gmail and a "Not you?" link). When the mobile OTP succeeds on that browser,/api/auth/otp/verify(bodylinkGoogle: true) connects that Gmail to the account the OTP opened, through the sameck_identity_googlelinkcheck (OTP session under 10 minutes old, conflicts refused), clears the cookie, and the student goes on tonext.- New person → OTP creates the account → Gmail connected.
- Existing student who never connected Gmail → OTP opens their existing account → Gmail connected.
- The screen never says which of the two it was.
- Never connected on a shared computer, or when the student tapped "Not you?".
/api/auth/meshows a signed-out visitor only the masked hint, never the id.- Tests:
tests/identity/google-pending.test.ts.
- Flow:
/api/auth/google/start→ Supabase/auth/v1/authorize(PKCE, signed httpOnly flow cookie) → Google →/api/auth/google/callback→ code exchange + user check →ck_identity_google. The final address carries only a short result code. - Dry-run tested 5 Oct (all undone): connect after OTP ✓; Google login → same person and student ✓; connect from a Google session ✗; connect 10+ min after OTP ✗; unconnected Gmail login ✗ (now → mobile box).
Still open
- Coins — waiting for Ravneet. New rule wanted: 1,000 coins when the profile is 100% complete
(not on joining). Decisions pending: what counts as 100% (the current 11-item checklist includes
"email or Gmail"), whether cetking.com keeps registration coins, and old students who already got
1,000. Must be awarded inside the database through
award_welcome(once per person), never from the browser. - Sessions are separate per front door (logging in on cetking.com does not log you into the Universe). Plan to carry cetking.com logins over with no re-login: SESSION-CARRYOVER.md.
- Universe session lengths: 7 days without a visit, 12 hours on a shared computer. Since
6 Oct 2026 an active login is renewed once a day (
ck_identity_renew), so the old hard 30-day end no longer forces daily users to log in again. Details: SESSION-CARRYOVER.md.
Future scope
- Google One Tap ("Continue as " pop-up showing the student's own Google account, so they just tap it). Needs Google Identity Services on the page and an ID-token sign-in path in place of the current redirect button; about half a day. Must keep the same rules: a Gmail opens an account only once a mobile OTP has been done. We never pre-fill an email ourselves (we don't know who the visitor is before login). Requested by Ravneet 5 Oct 2026; parked as future scope.
Login screen rules (Ravneet, 5 Oct 2026; updated 9 Oct 2026)
- One card, two tabs: Join for Free (default) | Login.
/login?mode=loginopens Login. - Stage 1 for joining is always mobile number → Send OTP → enter OTP. No name, no long form.
- Both tabs run the same login. The server alone decides new vs existing. The screen must never reveal whether a number is already a student (no "account exists" checks before OTP) — this protects student privacy and the opacity rule.
- Mobile OTP always works for everyone (first login and recovery). Google never replaces it.
- Stage 2, right after OTP: a Connect your Gmail card for one-tap login next time.
- Login tab: Continue with Google first (white button with the Google "G"), then "or log in with your mobile number" and the OTP form. A Gmail we don't know yet comes back to the mobile box (no "first time here?" question) and is connected after the OTP.
- Inside Rav's chat (compact card): mobile OTP only — no tabs, no Google button. Number → OTP → "You're in" → the chat carries on (Ravneet, 9 Oct 2026: Google would take the student off the chat page mid-conversation, and every chat login should give a verified phone).
- Under the card: "By continuing, you agree to Cetking's Terms & Privacy Policy" → https://cetking.com/privacy-policy-general-terms-conditions/
- Home page: Mobile Number | Join for Free bar under the bot lineup (signed-out visitors only); it opens the join card with the number pre-filled (handed over in the browser tab, never in the web address).
- Profile page: Log out of this device button at the bottom.
- Signed in: the top bar shows the coins pill, which opens the profile; profile cards belong to Oracle.
- Colours: greys only (the Google "G" keeps Google's own colours).
Order of work (Ravneet, 5 Oct 2026)
Finish login → test → then other features. The timetable code was removed from the live site and
kept on branch parked-timetable (it has a known type error in app/api/timetable/route.ts to fix
before it comes back).
Rejected ideas (and why)
- "Existing number → skip OTP, Google only": locks out everyone who never linked Google.
- "Check if the number exists before sending OTP": leaks who is a Cetking student.
- "Google creates a new account instantly": accounts with no verified mobile, and duplicates.
- "Universe keeps its own users in Cetking One AI": a second identity and wallet.
- "Put mobile OTP above Google on the Login tab" (9 Oct 2026): returning students who connected Gmail would start using OTP again (slower, and an SMS each time). Instead, an unknown Gmail is caught and sent to the mobile box.