Table of Contents
Overview
This guide covers security practices, authentication flows, and security considerations for developers working on the Striae project.
Authentication Architecture
Firebase Authentication
Striae uses Firebase Authentication as the primary authentication system:
// app/services/firebase.ts
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import firebaseConfig from '~/config/firebase';
export const app = initializeApp(firebaseConfig, "Striae");
export const auth = getAuth(app);
Implemented Features
-
Email/Password Authentication: Standard email and password login
-
Email Verification: Required before account activation
-
Multi-Factor Authentication (MFA): SMS-based second factor
-
Password Reset: Secure password reset flow
-
Session Management: Firebase token-based sessions
Password Security Requirements
Strong password validation is enforced during authentication:
// From app/routes/auth/login.tsx
const checkPasswordStrength = (password: string): boolean => {
const hasMinLength = password.length >= 10;
const hasUpperCase = /[A-Z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
return hasMinLength && hasUpperCase && hasNumber && hasSpecialChar;
};
Requirements:
-
Minimum 10 characters
-
At least one uppercase letter
-
At least one number
-
At least one special character
Multi-Factor Authentication (MFA)
MFA implementation using Firebase Auth:
// app/utils/mfa.ts
export const userHasMFA = (user: User): boolean => {
return multiFactor(user).enrolledFactors.length > 0;
};
MFA Flow
-
User completes email/password authentication
-
If MFA not enrolled, prompt for phone number enrollment
-
SMS verification code sent via Firebase
-
Future logins require both password and SMS code
Access Control
Direct Authentication Access
Application provides direct access to the authentication interface with email domain restrictions:
// app/routes/auth/login.tsx
// Email domain validation using free-email-domains package
const validateEmailDomain = (email: string): boolean => {
const emailDomain = email.toLowerCase().split('@')[1];
return !freeEmailDomains.includes(emailDomain);
};
Email Domain Restrictions:
-
Personal email providers (Gmail, Yahoo, Outlook, etc.) are blocked
-
Only work/institutional email addresses are allowed
-
Uses comprehensive free-email-domains package with 4,779+ blocked domains
API Security
Worker Authentication
All Cloudflare Workers use custom authentication headers:
// Example from workers/user-worker/src/user-worker.js
async function authenticate(request, env) {
const authKey = request.headers.get('X-Custom-Auth-Key');
if (authKey !== env.USER_DB_AUTH) throw new Error('Unauthorized');
}
CORS Configuration
Strict CORS policies implemented across all workers:
const corsHeaders = {
'Access-Control-Allow-Origin': 'https://www.striae.org',
'Access-Control-Allow-Methods': 'GET, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, X-Custom-Auth-Key',
'Content-Type': 'application/json'
};
API Key Management
API keys are managed through the Keys Worker:
// app/utils/auth.ts
async function getApiKey(keyType: KeyType): Promise<string> {
const response = await fetch(`${KEYS_URL}/${keyType}`, {
headers: {
'X-Custom-Auth-Key': KEYS_AUTH
}
});
return response.text();
}
Data Security
Signed URLs for Images
Image access is controlled through cryptographically signed URLs:
// workers/image-worker/src/image-worker.js
async function generateSignedUrl(url, env) {
const encoder = new TextEncoder();
const secretKeyData = encoder.encode(env.HMAC_KEY);
const key = await crypto.subtle.importKey(
'raw',
secretKeyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const expiry = Math.floor(Date.now() / 1000) + EXPIRATION;
url.searchParams.set('exp', expiry);
const stringToSign = url.pathname + '?' + url.searchParams.toString();
const mac = await crypto.subtle.sign('HMAC', key, encoder.encode(stringToSign));
const sig = bufferToHex(new Uint8Array(mac).buffer);
url.searchParams.set('sig', sig);
return url.toString();
}
Environment Variable Security
All sensitive configuration is stored as environment variables across the workers:
// Environment variables used across workers:
//
// User Worker:
// - USER_DB_AUTH: User worker authentication token
// - USER_DB: KV namespace binding for user data
//
// Data Worker:
// - R2_KEY_SECRET: Data worker authentication token
// - STRIAE_DATA: R2 bucket binding for file storage
//
// Image Worker:
// - API_TOKEN: Cloudflare Images API authentication
// - ACCOUNT_ID: Cloudflare account identifier for Images API
// - HMAC_KEY: HMAC secret key for signed URL generation
//
// Keys Worker:
// - KEYS_AUTH: Keys worker authentication token
// - R2_KEY_SECRET: Referenced for key distribution
// - ACCOUNT_HASH: Account hash for client-side operations
// - IMAGES_API_TOKEN: Referenced for key distribution
// - USER_DB_AUTH: Referenced for key distribution
//
// PDF Worker:
// - BROWSER: Puppeteer browser binding (no auth required)
//
// Turnstile Worker:
// - CFT_SECRET_KEY: Cloudflare Turnstile secret key
Security Notes:
-
All authentication tokens are unique, randomly generated secrets
-
KV and R2 bindings are configured in wrangler.jsonc files
-
No environment variables are exposed to client-side code
-
Keys Worker acts as a secure distribution point for other worker tokens
Error Handling
Secure Error Responses
Error handling sanitizes sensitive information:
// app/services/firebase-errors.ts
export const handleAuthError = (err: unknown): { message: string; data?: AuthErrorData } => {
if (err instanceof FirebaseError) {
switch (err.code) {
case 'auth/invalid-credential':
return { message: 'Invalid credentials' };
case 'auth/user-not-found':
return { message: 'No account found with this email' };
// ... other cases
default:
console.error('Firebase Auth Error:', errorData);
return { message: 'Something went wrong. Please contact support.' };
}
}
};
HTTP Status Codes
Proper distinction between authentication and authorization errors:
-
401 Unauthorized: Authentication failures (invalid credentials)
-
403 Forbidden: Authorization failures (insufficient permissions)
Security Configuration
Firebase Configuration
// app/config-example/firebase.ts
interface FirebaseConfig {
apiKey: string;
authDomain: string;
projectId: string;
storageBucket: string;
messagingSenderId: string;
appId: string;
measurementId: string;
}
Required Environment Setup
-
Firebase Project: Configure authentication settings
-
MFA Setup: Enable SMS authentication in Firebase Console
-
Worker Environment Variables: Set all required secrets
-
CORS Configuration: Ensure domain restrictions are properly set
Development Security Practices
Local Development
// Commented out in production
// connectAuthEmulator(auth, 'http://127.0.0.1:9099');
Testing Authentication
// app/utils/mfa.ts includes comprehensive MFA testing instructions
// Test with real phone numbers for SMS verification
// Use Firebase emulator for local development
Secret Management
-
Never commit secrets: Use
.env.example
templates -
Environment-specific configs: Separate dev/prod configurations
-
Key rotation: Regularly update API keys and secrets
-
Access logging: Monitor worker access patterns
Security Limitations
Not Currently Implemented
-
Role-Based Permissions: All authenticated users have same access
-
Rate Limiting: No request throttling in workers
-
Custom Audit Logging: No application-level audit trail system
-
API Versioning: No versioning strategy for breaking changes
-
Account Lockout: Relies on Firebase default protections
Cloudflare Worker Logging
While custom audit logging is not implemented, Cloudflare provides built-in logging capabilities for Workers:
Available Logging Features:
-
Real-time Logs: Console logs from workers available in Cloudflare dashboard
-
Request Analytics: HTTP request metrics, response codes, and performance data
-
Error Tracking: Automatic capture of worker exceptions and errors
-
Tail Logs: Live streaming of worker execution logs via
wrangler tail
Log Retention Policy:
-
Real-time Logs: Available for immediate viewing during development
-
Analytics Data: Retained for up to 30 days on Pro plans, longer on Enterprise
-
Error Logs: Captured in Cloudflare's error tracking system
-
Console Logs: Viewable in real-time but not persistently stored without external logging
What Gets Logged:
-
Worker execution times and performance metrics
-
HTTP request/response details (headers, status codes, response times)
-
Console.log() statements from worker code
-
Unhandled exceptions and stack traces
-
Geographic request distribution and caching metrics
Limitations:
-
No built-in request payload logging for security reasons
-
Console logs are not permanently stored without external log aggregation
-
No user action audit trail beyond HTTP request logs
-
Limited historical log search capabilities
Known Considerations
-
SMS Costs: MFA SMS usage should be monitored
-
User Session Timeout: Uses Firebase default session handling
-
Inactivity Logout: Automatically log out users after a period of inactivity
-
Cross-Origin: CORS restricted to single domain only
Security Checklist for New Features
Before Adding New Endpoints
-
Implement proper authentication
-
Add CORS headers
-
Validate input data
-
Use appropriate HTTP status codes
-
Add error handling
-
Test with invalid/malicious inputs
Before Deploying
-
Review environment variables
-
Test authentication flows
-
Verify CORS restrictions
-
Check error message sanitization
-
Validate permission checks
Incident Response
If Security Issue Discovered
-
Immediate: Disable affected endpoints if possible
-
Assessment: Determine scope and impact
-
Communication: Notify stakeholders
-
Fix: Implement and test solution
-
Deploy: Push fixes to production
-
Post-mortem: Document lessons learned
Monitoring
Current monitoring capabilities include:
-
Console Logs: Monitor worker logs for errors through Cloudflare dashboard
-
Firebase Console: Check authentication metrics and user activity
-
Cloudflare Analytics: Monitor traffic patterns, request volumes, and geographic distribution
Accessing Cloudflare Worker Logs:
# Real-time log streaming during development
wrangler tail --name striae-users
wrangler tail --name striae-images
# View logs in Cloudflare Dashboard:
# 1. Navigate to Workers & Pages
# 2. Select specific worker
# 3. Go to "Logs" tab for real-time view
# 4. Use "Analytics" tab for historical metrics
Log Analysis Recommendations:
-
Monitor authentication failure patterns
-
Track API response times and error rates
-
Review geographic access patterns for anomalies
-
Set up external log aggregation for long-term storage if needed
Available Metrics:
-
Request count and error rates by worker
-
Response time percentiles and performance trends
-
Geographic distribution of requests
-
Cache hit/miss ratios for static assets