Supabase Authentication Setup¶
Status: Implemented ✅ | Configuration Needed ⚠️
Quick Links:
- Supabase Dashboard: https://supabase.com/dashboard
Immediate Next Steps (Required for Deployment)¶
1. Get Supabase Service Key¶
Why: Backend needs service_role key for creating auth sessions
Steps:
1. Go to https://supabase.com/dashboard
2. Select your project
3. Click Settings (left sidebar) → API
4. Scroll to "Project API keys"
5. Copy the service_role secret key
- ⚠️ Keep this secret! Never commit to git or share publicly
- This key has full database access
2. Add Environment Variables to Railway¶
Add to BOTH Railway environments (dev and production):
# Supabase Authentication
SUPABASE_URL=https://<your-project>.supabase.co
SUPABASE_ANON_KEY=<your-anon-key>
SUPABASE_SERVICE_KEY=<paste-service-role-key-from-step-1>
# iMessage relay authentication (legacy /orchestrator/message)
RELAY_WEBHOOK_SECRET=<generate-random-secret>
How to add to Railway: 1. Go to Railway dashboard 2. Select your project → backend service 3. Click Variables tab 4. Click + New Variable 5. Add each variable above 6. Deploy (Railway will auto-redeploy)
Generate RELAY_WEBHOOK_SECRET:
# Option 1: Use openssl
openssl rand -hex 32
# Option 2: Use Python
python -c "import secrets; print(secrets.token_hex(32))"
3. Update Frontend Authentication¶
The web app (../archety-web) uses HTTP-only cookies (withCredentials: true) plus CSRF protection for mutating requests.
- Session check:
GET /auth/session - CSRF token:
GET /auth/csrf-token - OTP verification:
POST /auth/verify/start,POST /auth/verify/confirm
Avoid storing long-lived auth tokens in localStorage.
4. Update Mac Mini Relay¶
Add authentication to orchestrator requests:
import os
# Get secret from environment variable
RELAY_WEBHOOK_SECRET = os.getenv("RELAY_WEBHOOK_SECRET")
# Add to all requests to /orchestrator/message
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {RELAY_WEBHOOK_SECRET}"
}
response = requests.post(
"https://archety-backend-prod.up.railway.app/orchestrator/message",
json=message_payload,
headers=headers
)
5. Run Database Migrations¶
When deploying to production for the first time:
# Option 1: Via Railway CLI
railway run alembic upgrade head
# Option 2: One-time deploy script
# Add to Railway deployment command (if not auto-running)
alembic upgrade head && uvicorn app.main:app
What this does:
- Creates user_traits table
- Creates trait_profiles table
- Creates onboarding_sessions table
- Renames metadata → meta_data in existing tables
Testing the Authentication¶
Test 1: Phone Verification Flow¶
# 1. Start verification
curl -X POST http://localhost:8000/auth/verify/start \
-H "Content-Type: application/json" \
-d '{"phone": "+15551234567"}'
# 2. Confirm with OTP (check your phone)
curl -X POST http://localhost:8000/auth/verify/confirm \
-H "Content-Type: application/json" \
-d '{
"phone": "+15551234567",
"code": "123456",
"name": "Test User"
}'
# Response includes:
# - access_token (use this!)
# - refresh_token
# - user_id
Test 2: Authenticated Endpoint¶
# Get user profile (requires auth)
curl -X GET http://localhost:8000/user/profile \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE"
# Should return:
# - User profile data
# - Trait profile (if exists)
# - Relationship data
Test 3: Orchestrator Authentication¶
# Test orchestrator endpoint (requires relay secret)
curl -X POST http://localhost:8000/orchestrator/message \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_RELAY_SECRET_HERE" \
-d '{
"chat_guid": "test-123",
"mode": "direct",
"sender": "+15551234567",
"text": "hello sage!",
"timestamp": 1699123456,
"participants": ["+15551234567"]
}'
Current Status¶
✅ Implemented:
- Supabase client wrapper (app/identity/supabase_client.py)
- Authentication dependencies (app/dependencies.py)
- JWT generation after Twilio OTP
- All user endpoints secured
- Orchestrator endpoint secured
- Configuration added to app/config.py
⚠️ Configuration Needed:
- [ ] Add SUPABASE_SERVICE_KEY to Railway
- [ ] Add RELAY_WEBHOOK_SECRET to Railway
- [ ] Update frontend to use access_token
- [ ] Update Mac mini relay with auth header
- [ ] Run database migrations
📝 Future Improvements:
- [ ] Productionize: Separate Supabase projects for dev/prod (before launch)
- [ ] Add token refresh endpoint (POST /auth/refresh)
- [ ] Implement full HMAC signing for orchestrator (upgrade from Bearer token)
- [ ] Add session revocation endpoint
- [ ] Add "remember me" functionality (longer token expiry)
Production Readiness Checklist¶
Before launching to real users:
- Separate Supabase Projects
- Create new production project in Supabase
- Update Railway prod environment with new keys
- Keep dev environment with current keys
-
Test both environments independently
-
Security Hardening
- Enable Supabase Row Level Security (RLS) policies
- Set up Supabase auth rate limiting
- Rotate all secrets (RELAY_WEBHOOK_SECRET, service keys)
- Enable HTTPS-only cookies in production
-
Add webhook signature verification (upgrade to HMAC)
-
Monitoring
- Set up Supabase auth analytics
- Monitor JWT refresh rates
- Alert on auth failure spikes
- Track token expiry issues
Cost & Limits¶
Current Setup (Free Tier): - 50,000 Monthly Active Users (MAU) - 500 MB database storage - 2 GB bandwidth/month - Unlimited API requests
When to Upgrade ($25/month Pro): - Hit 50,000 MAU - Need more than 500 MB storage - Need custom SMTP for emails - Need point-in-time recovery
Scaling Path: - 0-50K users: Free tier ✅ - 50K-100K users: Pro tier (\(25/mo) - 100K+ users: Team tier (\)599/mo) or Enterprise
Troubleshooting¶
Issue: "Supabase not enabled" warning in logs¶
Cause: Environment variables not set
Fix:
1. Add SUPABASE_URL and SUPABASE_SERVICE_KEY to Railway
2. Redeploy backend
3. Check logs for "Supabase client initialized" message
Issue: 401 Unauthorized on /user/profile¶
Cause: Missing or invalid access token
Fix:
1. Verify frontend is sending Authorization: Bearer <token> header
2. Check token hasn't expired (tokens last 1 hour by default)
3. Verify token was obtained from /auth/verify/confirm
4. Check Supabase service key is correct
Issue: "Invalid signature" on /orchestrator/message¶
Cause: Missing or incorrect RELAY_WEBHOOK_SECRET
Fix:
1. Ensure RELAY_WEBHOOK_SECRET is set in Railway
2. Ensure Mac mini relay is sending same secret in Authorization header
3. Format: Authorization: Bearer <secret>
Support & Resources¶
Supabase Documentation: - Auth Guide: https://supabase.com/docs/guides/auth - Python Client: https://supabase.com/docs/reference/python/introduction - JWT Structure: https://supabase.com/docs/guides/auth/jwts
Project Files:
- Implementation: app/identity/supabase_client.py
- Dependencies: app/dependencies.py
- Auth Routes: app/api/auth_routes.py
- User Routes: app/api/user_routes.py
- Config: app/config.py
Need Help? - Supabase Discord: https://discord.supabase.com - GitHub Issues: https://github.com/rawrjustin/archety/issues
Last Updated: November 13, 2025 Version: 1.0 - Initial Supabase Integration