- Add emailVerification.sendVerificationEmail config to auth/src/auth.ts
using Resend to send verification emails on sign-up
- Add resend npm package to auth/package.json
- Update auth/.env.example with RESEND_API_KEY and FROM_EMAIL
- Create VerifyEmail.tsx page with token verification flow,
spinner UX, success/Error states, and resend option
- Update Register.tsx to redirect to /verify-email after signup
instead of auto-navigating to dashboard
- Add /verify-email route to App.tsx
- Frontend shows 'check your email' step after registration
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The GET /me/email-in-address endpoint was unreachable because the Gateway
HTTPRoute routes all /auth/* traffic to Better-Auth (port 3001), not the
API service. This change:
- Moves the endpoint from the /auth router to a new /api/v1/me/ router
- Adds EmailInAddressResponse schema and get_email_in_address service method
- Updates Settings.tsx to call /api/v1/me/email-in-address
Fixes CAR-445.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Removed usePriceHistory calls with hardcoded string product IDs (prod1, prod10)
that caused 422 errors against the UUID-expecting API. The Price Trends section
now shows a placeholder until a proper featured-products endpoint is available.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Change frontend to call /alerts (was /price-alerts) and /products/{id}/prices
(was /products/{id}/price-history) to match the backend router mounts.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The try-block getSession() pattern is correct for real auth mode.
The mock-auth catch block (VITE_MOCK_AUTH) still needs to set
the Zustand flag so ProtectedRoute respects the authenticated state.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Race condition between signUp/signIn completion and ProtectedRoute's
useSession() call caused redirect loops — Better-Auth's session cookie
is not immediately visible to useSession() after signUp/signIn resolves.
Fix: call authClient.getSession() explicitly after signUp/signIn to
synchronize before navigating to protected routes. Fall back to error
message if session not confirmed.
Also removes dead setAuthenticated() calls that only work in mock mode.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The axe-core accessibility scan runs against the page before the auth
session resolves, showing DashboardSkeleton instead of real content.
DashboardSkeleton had no h1, causing a false-positive
'page-has-heading-one' violation.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Install Mock Service Worker (MSW) and configure it for vitest.
Write one integration test for usePurchases hook using MSW.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Add formatCurrency, formatDate, and storeSlugs utilities in src/utils/
with 21 vitest unit tests covering standard and edge cases.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Avoids ERR_CONNECTION_REFUSED in deployed environments where
VITE_AUTH_URL is not set at build time. Empty-string fallback
routes auth requests to same origin, which the HTTPRoute forwards
to the auth service.
cc @cpfarhood
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Register sends display_name instead of name
- Register/Login handle TokenResponse (access_token, not token)
- Fetch /auth/me after register/login to populate user object
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Pre-existing test failure from Phase 1 better-auth migration.
Dashboard calls authClient.useSession() which makes an unresolved
async call in test environment. Mock it to return null session
(isPending: false) so the unauthenticated UI renders correctly.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace hand-rolled JWT auth with Better-Auth session-based authentication.
- Scaffold auth/ Node.js service with Better-Auth, bcrypt password compat,
Postgres adapter mapped to existing users table
- Add Alembic migration (002) creating sessions, accounts, verifications
tables and migrating password hashes to accounts table
- Update FastAPI auth dependency to validate sessions via shared DB
(supports both cookie and Bearer token)
- Remove registration/login/refresh endpoints from API gateway (now
handled by Better-Auth service)
- Update frontend to use better-auth/react client with httpOnly cookies
(no tokens in localStorage or memory)
- Rewrite auth store, Login, Register, Dashboard, Settings, ProtectedRoute
to use session-based auth
- Update all tests to create sessions directly in DB instead of JWT tokens
Resolves CAR-27
See plan: CAR-26#document-plan
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- AccountLinking: remove unused _fields param from handleConnect
- Coupons: extract Date.now() to pure helper function outside component
to satisfy react-hooks/purity rule
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Must-fix:
- Exclude JWT token from Zustand persist (partialize) to prevent
localStorage XSS exfiltration — token now lives in memory only
- Wire all pages through TanStack Query hooks (usePurchases, useProduct,
useProducts, usePriceHistory, useCoupons, usePriceAlerts) with proper
loading skeletons and error states
- Add mock interceptor in api.ts (VITE_MOCK_API=true) so mock data flows
through the same fetch path — single flag to switch to live API
Should-fix:
- Wire theme toggle to DOM (dark class on <html>)
- Fix AccountLinking form inputs (controlled with value/onChange)
- Remove unused err in catch blocks (Login, Register)
- Bump remaining min-h-10 touch targets to min-h-12 (48px)
Build: 128KB initial JS, Recharts 498KB lazy chunk. 5/5 tests pass.
Co-Authored-By: Paperclip <noreply@paperclip.ing>