fix: OTP-Login lauffähig machen (verify-otp/send-otp konsistent + RLS) - #10
Merged
Conversation
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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 Telegramrequest_idin der Tabelleotp_requestsund gibt nur{ ok: true }zurück — ohnerequest_id.verify-otpverlangte dierequest_idaber zwingend im Request-Body.Das Frontend übergab also
undefined→verify-otpantwortete mit 400 "Request-ID sind erforderlich". Registrierung unmöglich.Fix (deployed)
verify-otp(v4 deployed): Schlägt dierequest_idausotp_requestsnach, 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.appgefunden/angelegt, Session robust überproperties.hashed_tokengemintet, verbrauchte OTP-Zeile wird gelöscht.send-otp(v5 deployed): Gibtrequest_idzusätzlich zurück (Defense-in-Depth), self-contained.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 pushanwenden — schließt die von Supabase gemeldete kritische RLS-Lücke.Test plan
TELEGRAM_GATEWAY_TOKENals Supabase-Secret gesetztprofiles/auth.userswird angelegthttps://claude.ai/code/session_01G1v5ZGvnrS5hb6eX2hfxKb
Generated by Claude Code