Remove dangerous default values for jwt_secret_key, service_key, and
fernet_key. Add startup validation that raises RuntimeError if these
secrets are not set via environment variables or contain placeholder
values.
Add test fixture to provide explicit test values for these secrets,
ensuring existing tests continue to pass.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Better-Auth sets the session cookie as "token.sessionId". The DB stores
only the token part, so passing the full compound value caused 401s.
Splits on "." for both cookie and Bearer paths.
Tests added for compound cookie, raw token cookie (regression), and
compound Bearer token.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
API config.py now reads CARTSNITCH_DATABASE_URL first, falls back to
DATABASE_URL (which the infra K8s overlay sets for all pods), and finally
falls back to the hardcoded default. Also normalizes plain postgresql://
to postgresql+asyncpg:// for the asyncpg driver.
Fixes CAR-510.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Better-auth v1.5.6 stores raw 32-char tokens in sessions.token, not SHA-256
hashes. The SHA-256 fix from PR #136 causes all authenticated API calls to
return 401 because the UAT sessions table contains raw tokens.
- Remove hashlib from dependencies.py; compare tokens directly
- Remove hashlib from conftest.py; store raw tokens in test DB
- Remove hashlib from test_expired_session_rejected; use raw tokens
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Better-Auth v1.2+ stores SHA-256(raw_token) in the sessions.token
column. The cookie/Bearer header carries the raw token, so the API was
doing a plain-text lookup that would never match a hashed value —
causing all authenticated endpoints to return 401.
- Add hashlib import and hash token in _validate_session_token()
- Update conftest._create_test_user_and_session() to store hashed tokens
- Update test_expired_session_rejected() to store hashed tokens
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Better-auth sets the session cookie with the __Secure- prefix on HTTPS
deployments. The API was only reading the plain cookie name, causing all
authenticated calls to return 401 in dev/UAT/prod environments.
Check __Secure-better-auth.session_token first, fall back to
better-auth.session_token for HTTP local dev compatibility.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Fix email format in AuthService.get_email_in_address to use
receipts+{token}@receipts.cartsnitch.com (was broken: @email.cartsnitch.com)
- Remove dead EmailInAddressResponse class and GET /auth/me/email-in-address
endpoint from auth/routes.py (endpoint moved to routes/user.py)
- Add instructions field to EmailInAddressResponse schema
- Update routes/user.py to include instructions in the response
- Update test URLs from /auth/me/email-in-address to /api/v1/me/email-in-address
Co-authored-by: CartSnitch Engineer Bot <cartnoreply@cartsnitch.com>
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>
- Revert auth/dependencies.py to cookie+Bearer dual auth with str user IDs
- Add GET /auth/me/email-in-address endpoint for receipt email routing
- Update User model: add email_inbound_token, change id/store_id/user_id to str
- Update AuthService and UserResponse to use str user IDs
- Update route count test: 33 -> 34 routes
- Restore e2e test for email-in-address endpoint
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Revert auth/dependencies.py, auth/routes.py, services/auth.py, schemas.py
to Better-Auth session-cookie auth (removed JWT register/login/refresh)
- Preserve GET /auth/me/email-in-address endpoint
- Fix UUIDString TypeDecorator: process_result_value returns uuid.UUID
(not str) so SQLAlchemy 2.0 sentinel tracking matches UUID-to-UUID
- Fix seed_data fixture: look up real user_id from session token via
sessions table; purchases now reference actual user FK
- Update purchase_data fixture to use session-cookie auth
- Update test_auth_endpoints, test_auth_validation to cookie-based tests
- Remove TestRegistrationErrors and TestLoginErrors (no longer applicable)
- Update test_openapi.py expected routes and count
- Update test_error_handler.py to use PATCH /auth/me validation
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Remove TimestampMixin (created_at/updated_at) from Purchase, PurchaseItem,
PriceHistory, Coupon, and ShrinkflationEvent models since their PostgreSQL
tables do not have those columns. This was causing 500 errors on
/api/v1/purchases and /api/v1/purchases/stats.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Better-Auth uses nanoid strings for user IDs, not UUIDs. Changed all
user_id parameter/return types in the API layer from UUID to str,
removed the obsolete UUID import where unused, and updated the
_validate_session_token return type accordingly.
Co-authored-by: CartSnitch Engineer Bot <cartnoreply@cartsnitch.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Better-Auth v1.5.6 stores raw tokens in sessions.token, not SHA-256
hashes. The session cookie is signed (rawToken.hmacSignature), so
strip the HMAC signature suffix before querying the DB.
Fixes 401 errors on all data endpoints caused by the incorrect hash.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Restores sha256 import and token hashing in _validate_session_token.
Regression introduced when PR #95 (cookie name fix) was merged without
the hash fix from PR #93.
QA approved: CAR-324 (Checkout Charlie)
CTO approved: Paperclip (Savannah Savings)
Resolves CAR-323
cc @cpfarhood
Better-Auth automatically prefixes cookie names with __Secure- when serving
over HTTPS. The API gateway now tries __Secure-better-auth.session_token
first (HTTPS/deployed), falling back to better-auth.session_token (HTTP/local dev).
Fixes CAR-321.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Better-Auth v1.5.6+ stores session tokens as SHA-256 hashes in the
sessions table. The raw token from the cookie was being queried directly,
causing all authenticated /api/v1/* requests to return 401.
Fixes CAR-313.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Fixes CAR-161 UAT failure: k8s HTTPRoute forwards /api/* to the API
gateway without path rewriting, so requests arrive at FastAPI as
/api/v1/purchases, /api/v1/products, etc. FastAPI previously mounted
data routers at root, causing 404s on all /api/v1/* calls.
Keep health and auth routers at root (probes hit /health directly;
auth traffic is routed to the auth service via HTTPRoute).
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>