Skip to content

fix: OTP-Login lauffähig machen (verify-otp/send-otp konsistent + RLS) - #10

Merged
Pascal-DCG merged 9 commits into
mainfrom
claude/fix-otp-auth
Jul 11, 2026
Merged

fix: OTP-Login lauffähig machen (verify-otp/send-otp konsistent + RLS)#10
Pascal-DCG merged 9 commits into
mainfrom
claude/fix-otp-auth

Conversation

@Pascal-DCG

Copy link
Copy Markdown
Owner

Problem

Der echte Login (Telegram-OTP) funktionierte produktiv nicht — alle Supabase-Tabellen waren leer (0 Registrierungen), und der Browser zeigte verify-otp 400 (Bad Request).

Root Cause: Die beiden Edge Functions waren mit zwei unterschiedlichen Designs geschrieben:

  • send-otp (Pascals Version) speichert die Telegram request_id in der Tabelle otp_requests und gibt nur { ok: true } zurück — ohne request_id.
  • verify-otp verlangte die request_id aber zwingend im Request-Body.

Das Frontend übergab also undefinedverify-otp antwortete mit 400 "Request-ID sind erforderlich". Registrierung unmöglich.

Fix (deployed)

  • verify-otp (v4 deployed): Schlägt die request_id aus otp_requests nach, wenn sie nicht im Body kommt (funktioniert mit dem bereits live gemergten Frontend, kein Frontend-Deploy nötig). User wird per E-Mail-Alias <phone>@phone.kommit.app gefunden/angelegt, Session robust über properties.hashed_token gemintet, verbrauchte OTP-Zeile wird gelöscht.
  • send-otp (v5 deployed): Gibt request_id zusätzlich zurück (Defense-in-Depth), self-contained.
  • Beide Functions: Inline-CORS, keine fragilen relativen Imports.

Migration 00002 (⚠️ noch anzuwenden)

Enthält die otp_requests-Definition (bisher nur manuell in Prod angelegt, nicht im Repo) + RLS-Härtung:

  • otp_requests: RLS an, keine Policy (nur Service-Role-Edge-Functions greifen zu)
  • listings_archive: RLS an + SELECT-Policy für Admins (Admin-Archiv-Ansicht bleibt funktionsfähig)

Die MCP-Migration konnte in dieser Session nicht durchgeführt werden (Approval-Flow blockiert). Bitte im Supabase-Dashboard (SQL Editor) oder via supabase db push anwenden — schließt die von Supabase gemeldete kritische RLS-Lücke.

Test plan

  • Voraussetzung: TELEGRAM_GATEWAY_TOKEN als Supabase-Secret gesetzt
  • Login mit echter Nummer → Code kommt via Telegram
  • Code eingeben → Session wird erstellt, Profil-Setup erscheint (neuer User)
  • Eintrag profiles/auth.users wird angelegt
  • Erneuter Login mit gleicher Nummer → direkt in die Liste (kein Profil-Setup)
  • Migration 00002 angewendet, RLS-Advisory verschwindet

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb


Generated by Claude Code

Root Cause: send-otp speicherte die Telegram request_id in otp_requests und
gab nur { ok: true } zurueck, waehrend verify-otp die request_id zwingend im
Body erwartete. Das Frontend uebergab undefined -> 400 "Request-ID erforderlich".
Deshalb war noch keine Registrierung moeglich (alle Tabellen leer).

- verify-otp: request_id aus otp_requests nachschlagen wenn nicht im Body;
  User per E-Mail-Alias (<phone>@phone.kommit.app) finden/anlegen; Session
  robust ueber properties.hashed_token minten; otp_requests nach Erfolg leeren.
- send-otp: gibt request_id zusaetzlich zurueck (Defense-in-Depth), self-contained.
- Beide Functions inline-CORS, keine relativen Imports.
- Migration 00002: otp_requests-Definition nachgezogen + RLS aktiviert
  (otp_requests ohne Policy, listings_archive mit Admin-SELECT-Policy).

Deployed: verify-otp v4, send-otp v5.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
kommit Ready Ready Preview, Comment Jul 11, 2026 3:43pm

supabase-js verpackt Function-Fehler in "Edge Function returned a non-2xx
status code" und verbirgt den echten Grund. edgeErrorMessage() liest den
Fehlertext aus dem Response-Body (error.context) aus, sodass send-otp/
verify-otp-Fehler (z.B. "Kein Code angefordert", "Telegram-Token fehlt")
sichtbar werden statt der generischen Meldung.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
Postgres-Log zeigte 42501 "permission denied for table otp_requests":
Die manuell angelegte Tabelle hatte der service_role keine Rechte gewaehrt,
weshalb send-otp/verify-otp beim Zugriff scheiterten und die Tabelle leer blieb.
Migration 00002 gewaehrt jetzt SELECT/INSERT/UPDATE/DELETE an service_role
und sperrt anon/authenticated aus.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
Postgres-Log zeigte 42P01 "relation profiles does not exist" im Trigger
handle_new_user beim Anlegen des auth-Users. Die Funktion lief als
supabase_auth_admin (search_path ohne public) und referenzierte profiles
unqualifiziert. Fix: public.profiles + SET search_path = ''.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
Nach erfolgreichem verify-otp blieb der Login auf "Wird geprueft..." haengen.
Ursache: Der onAuthStateChange-Callback war async und rief direkt
await fetchProfile() (supabase.from) auf. Der Callback haelt den internen
Auth-Lock, den supabase.from zum Anhaengen des Tokens braucht -> Deadlock,
setSession() wurde nie fertig.

Fix: Callback synchron machen, Session sofort setzen (isAuthenticated wird
true), Profil per setTimeout(0) ausserhalb des Locks nachladen.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
Bei frischem Login (SIGNED_IN/INITIAL_SESSION) bleibt loading true, bis das
Profil geladen ist. Sonst koennte ein neuer User mit noch unbekanntem
isNewUser kurz zur Liste navigiert werden statt zum Profil-Setup. Bei
Token-Refresh wird loading nicht angefasst (kein Spinner-Flackern).

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
Nach dem otp_requests-Grant kam 42501 "permission denied for table profiles":
der Rolle authenticated fehlten die table-level Grants auf profiles (und
analog listings/push_subscriptions/listings_archive). Postgres prueft
Tabellenrechte vor RLS, daher scheiterte der Zugriff trotz vorhandener
Policies. Migration 00004 setzt die Grants fuer authenticated + service_role
und ergaenzt ALTER DEFAULT PRIVILEGES fuer kuenftige Tabellen.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
updateProfile schrieb bisher nur in die DB, ohne den lokalen Profil-State im
Auth-Context zu aendern -> der "Telefonnummer anzeigen"-Toggle sprang optisch
nicht um. updateProfile liegt jetzt in useAuth und aktualisiert den State
optimistisch (demo-faehig, mit Rollback bei Fehler). Vorbereitet fuer
avatar_url/avatar_color.

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
- profiles.avatar_url (Migration 00005) + Storage-Bucket "avatars" mit
  Policies (public read, User schreibt nur eigenen Ordner)
- 8 gebuendelte illustrierte Avatare (public/avatars/) + lib/avatars.ts
- Avatar-Komponente rendert Bild bei avatar_url, sonst Initialen
- AvatarPicker (Bottom-Sheet): Foto hochladen/aufnehmen (Canvas-Resize auf
  256px, Upload nach Storage bzw. DataURL im Demo), Galerie-Auswahl, Entfernen
- Kamera-Button auf dem Profil-Avatar oeffnet den Picker
- Avatare ueberall angezeigt (Header, Liste, Detail); Demo-Profile mit Presets
- eslint: supabase/functions (Deno) vom Frontend-Lint ausgeschlossen

Setup: Migration 00005 im Dashboard ausfuehren (Spalte + Bucket + Policies).

https://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
@Pascal-DCG
Pascal-DCG merged commit 9022f41 into main Jul 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants