Authentication Setup - Firestore Authentication
Authentication Setup - Firestore Authentication
Overview
The GCP Billing Agent uses custom Firestore-based authentication with JWT tokens. This provides:
- ✅ Simple setup - No OAuth consent screen or IAP configuration needed
- ✅ Domain restrictions - Enforce email domain requirements
- ✅ User management - Sign up, sign in, and account management
- ✅ Secure - Passwords hashed with bcrypt, JWT tokens for sessions
How It Works
- Sign Up: User creates account with email/password (domain validated)
- Password Storage: Passwords hashed with bcrypt and stored in Firestore
- Sign In: User authenticates, receives JWT token
- API Access: All API requests include JWT token in Authorization header
- User Isolation: Query history and data scoped to user_id from token
Architecture
User → Frontend (Sign Up/Login) → Backend API
↓
Firestore (users collection)
↓
JWT Token Generated
↓
Frontend stores token
↓
All API requests include token
Configuration
Domain Restrictions
Currently configured for @asl.apps-eval.com emails. To change:
- Backend (
web/backend/auth.py):REQUIRED_DOMAIN = "your-domain.com" # Change this - Frontend (
web/frontend/src/Auth.jsx):const REQUIRED_DOMAIN = "your-domain.com"; // Change this - Redeploy:
make deploy-web-simple PROJECT_ID=your-project-id
JWT Secret Key
The JWT secret key is automatically generated and stored in Cloud Run environment variables. It’s consistent across deployments to ensure tokens remain valid.
To view the secret key:
gcloud run services describe agent-engine-api \
--region=us-central1 \
--project=PROJECT_ID \
--format="value(spec.template.spec.containers[0].env)" | grep JWT_SECRET_KEY
To set a custom secret key:
gcloud run services update agent-engine-api \
--region=us-central1 \
--project=PROJECT_ID \
--update-env-vars JWT_SECRET_KEY=your-secret-key
⚠️ Important: Changing the JWT secret key will invalidate all existing tokens. Users will need to sign in again.
User Management
Sign Up
Users can create accounts through the UI:
- Navigate to the application
- Click “Sign Up”
- Enter email (must match required domain)
- Enter password
- Account created in Firestore
Sign In
Users sign in with email and password:
- Navigate to the application
- Click “Sign In”
- Enter credentials
- Receive JWT token
- Token stored in browser localStorage
Account Management
Users can:
- View their profile
- Delete their account (removes user and all query history)
Password Requirements
- Minimum length: 8 characters
- Maximum length: 72 bytes (bcrypt limit)
- Domain validation: Must be from required domain
Firestore Structure
Users Collection
users/{user_id} {
user_id: string,
email: string,
password_hash: string, // bcrypt hash
created_at: timestamp
}
Query History Collection
query_history/{query_id} {
user_id: string, // From JWT token (not client-provided)
agent_name: string,
message: string,
response: string,
timestamp: timestamp
}
Security Considerations
Password Security
- ✅ Passwords hashed with bcrypt (cost factor 12)
- ✅ Password length validated (8+ chars, max 72 bytes)
- ✅ Passwords never stored in plain text
- ✅ Passwords never logged
Token Security
- ✅ JWT tokens signed with secret key
- ✅ Tokens expire after 7 days (configurable)
- ✅ Tokens stored in localStorage (browser)
- ✅ Tokens included in Authorization header
Domain Restrictions
- ✅ Email domain validated on signup
- ✅ Generic error messages (don’t reveal domain)
- ✅ Backend validation (frontend validation is convenience only)
Data Isolation
- ✅ User ID extracted from JWT token (not client-provided)
- ✅ Query history scoped to authenticated user
- ✅ No cross-user data access
Troubleshooting
Sign Up Fails
Error: “Please use your ASL class account email address”
- Email must be from required domain
- Check domain configuration in
auth.pyandAuth.jsx
Error: “Password hashing failed”
- Password too long (>72 bytes)
- Check password length
- Try a shorter password
Sign In Fails
Error: “Invalid email or password”
- Check email and password are correct
- Verify account exists in Firestore
- Check password hash is valid
Error: “Invalid or expired token”
- Token may have expired (7 days default)
- JWT secret key may have changed
- Sign out and sign in again
Token Issues
Error: “Invalid or expired token” on API requests
- Check token is being sent in Authorization header
- Verify token hasn’t expired
- Check JWT_SECRET_KEY is consistent
Solution:
# Check token in browser console
localStorage.getItem('token')
# Sign out and sign in again
Firestore Permissions
Error: “Missing or insufficient permissions”
- Service account needs
roles/datastore.user - Check IAM permissions:
gcloud projects get-iam-policy PROJECT_ID \ --flatten="bindings[].members" \ --filter="bindings.members:agent-engine-api-sa@PROJECT_ID.iam.gserviceaccount.com"
Testing
Test Sign Up
# Use curl or Postman
curl -X POST https://api-url.run.app/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "test@asl.apps-eval.com",
"password": "testpassword123"
}'
Test Sign In
curl -X POST https://api-url.run.app/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "test@asl.apps-eval.com",
"password": "testpassword123"
}'
Test Authenticated Request
# Use token from sign in response
curl -X GET https://api-url.run.app/auth/me \
-H "Authorization: Bearer YOUR_TOKEN_HERE"
Cleanup
Delete User Account
Users can delete their own account through the UI, or manually:
# Using the cleanup script
make clean-history-user PROJECT_ID=project-id USER_ID=user-id
Delete All Users
⚠️ Use with caution!
# Manually delete from Firestore
# Or use Firestore console