SmartStore AI — Phase 5 Implementation Guide
Authentication
What got built
backend/app/auth.py — decode_token(), get_or_create_user(), get_current_user dependency
backend/app/api/me.py — a real protected endpoint demonstrating the dependency
backend/tests/test_auth.py
ios/SmartStoreAI/AuthManager.swift — Firebase sign-in (anonymous), token refresh
ios/SmartStoreAI/APIClient.swift — updated to attach the Bearer token to every request
ios/SmartStoreAI/ChatViewModel.swift — updated to refresh the token before every send()
Key design decisions
decode_token and get_or_create_user are separated from the FastAPI dependency itself. This is what made it possible to test the actual logic that matters — first-login provisioning, duplicate-prevention — against the real database, without needing real Firebase credentials. The Firebase verification call itself is Google's infrastructure; testing that it works is Google's job, not this test suite's.
First-login provisioning is idempotent — verified directly, not assumed. test_get_or_create_user_provisions_new_user calls the function twice with the same firebase_uid and confirms exactly one User row exists afterward, not two. This matters because a token gets verified on every single request — if provisioning weren't idempotent, a user's second message would create a second, orphaned account.
Anonymous sign-in on the iOS side, deliberately, not email/password. Every shopper gets a real, stable Firebase UID immediately, with zero sign-up friction — and because everything downstream keys off the token, not the sign-in method, swapping in email/Google/Apple sign-in later touches only AuthManager, nothing else.
/ask is intentionally NOT yet protected by get_current_user. That wiring happens in Phase 6, paired directly with the RBAC permission check — "must be authenticated" and "is authorized for this specific action" are different concerns that belong together at the route level, not split across two phases of half-finished protection.
Verified test results
tests/test_auth.py::test_decode_token_with_valid_token PASSED
tests/test_auth.py::test_decode_token_with_invalid_token_raises PASSED
tests/test_auth.py::test_get_or_create_user_provisions_new_user PASSED
tests/test_auth.py::test_me_endpoint_without_auth_header_returns_401 PASSED
tests/test_auth.py::test_me_endpoint_with_valid_auth_returns_user PASSED
All 17 tests across Phases 0-5 pass together — confirmed by running the full suite, not just this phase's file in isolation.
Setting up real Firebase (you'll need to do this yourself)
- Create a project at console.firebase.google.com
- Enable Anonymous sign-in under Authentication → Sign-in method
- Download the service account JSON (Project Settings → Service Accounts) → set
FIREBASE_CREDENTIALS_PATHin.env - Add the
GoogleService-Info.plistto your Xcode project, add the Firebase iOS SDK via Swift Package Manager - Initialize Firebase in
SmartStoreAIApp.swiftwithFirebaseApp.configure()before theWindowGroup
None of this can be verified in this sandbox — it requires a real Firebase project, which is the one genuinely external dependency in this entire phase.
What's next
Phase 6 — RBAC & Multi-Store Isolation wires get_current_user into /ask for real, adds the role-permission map, and adds Postgres Row-Level Security as a database-enforced backstop.