Rips firebase_auth; phone OTP flow now talks directly to the new backend endpoints, JWT access token lives in memory, refresh token persists via flutter_secure_storage. WebSocket handshakes read the access token from AuthBridge instead of Firebase. Smoke-tested end-to-end against the backend via curl: - otp/request → read stub code from backend log → otp/verify - /api/mitra/auth/me + /api/shared/auth/refresh rotation - logout → post-logout refresh correctly fails REFRESH_INVALID - ACCOUNT_INACTIVE (403) + WRONG_FLOW (400) error paths verified - Debug APK links cleanly - pubspec: drop firebase_auth, add flutter_secure_storage - core/auth/auth_bridge.dart: shared mutable state (access token + refresh callback + in-flight de-dup) as keepAlive provider - core/auth/token_storage.dart: flutter_secure_storage wrapper - core/auth/auth_notifier.dart: bootstrap → refresh; requestOtp + verifyOtp via /api/mitra/auth/*; logout; granular OTP error codes - core/api/api_client.dart: Bearer from bridge + postRaw(skipAuth) for auth endpoints + single-retry 401 refresh - core/chat/*_notifier.dart: WS auth frame reads bridge.accessToken - features/auth/screens/otp_screen.dart: verificationId → otpRequestId - mitra_app/CLAUDE.md: Auth section rewritten (was stale on Firebase) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1.4 KiB
1.4 KiB
Halo Bestie — Mitra App
Flutter mobile application for mental health professionals (mitra/partners).
See root
CLAUDE.mdfor full project context and architectural decisions.
Stack
- Framework: Flutter (iOS + Android)
- Auth: Self-managed (Phase 3.4). Phone OTP only — no Google / Apple. Access token lives in memory on an
AuthBridge; refresh token persists influtter_secure_storage.firebase_authis no longer used;firebase_messagingis kept for FCM push. - API: Calls public Fastify backend (
/api/mitra/and/api/shared/routes).shared.authcovers refresh + logout for both apps.
Key Concepts
- Users are mitra — trained mental health professionals
- Core flow: phone OTP login → set availability → accept sessions → chat with client → receive payment
- Mitra accounts require approval from control center before going live (backend returns
ACCOUNT_INACTIVE403 on OTP verify whenis_active=false)
Conventions
- Never call
/api/client/or/internal/routes from this app - API calls go through
ApiClient; it auto-attaches the JWT fromAuthBridgeand auto-refreshes on 401 - WebSocket handshake (
/api/shared/ws) sends the same access token in the first frame's{type:"auth", token}message - Mitra role is encoded in the JWT claims (
user_type: "mitra") — the backend enforces the role per route; never trust client state alone