This document outlines the security measures implemented in the BOUNTYExpo application.
- Authentication
- Session Management
- Admin Access Control
- Input Validation
- Password Security
- Rate Limiting
- Best Practices
- Security Checklist
BOUNTYExpo uses Supabase for authentication, which provides:
- JWT-based authentication
- Secure token storage using Expo SecureStore
- Automatic token refresh
- Session persistence
- Email validation: RFC-compliant email regex
- Password validation: Enforces strong password requirements
- Error handling: Specific error messages for different failure scenarios
- Email normalization: All emails are converted to lowercase
- Real-time validation: Field errors clear on user input
- Email validation: Required and format-checked
- Password validation: Required field
- Rate limiting: Client-side lockout after 5 failed attempts (5-minute cooldown)
- Error handling: Distinguishes between invalid credentials, unconfirmed email, etc.
- Session management: Automatic redirect on successful authentication
- Email validation: Verified before submission
- Rate limit handling: User-friendly messages for rate-limited requests
- Security notice: Uses generic success message to prevent user enumeration
// Tokens are stored in Expo SecureStore (encrypted storage)
// lib/supabase.ts
const ExpoSecureStoreAdapter: StorageAdapter = {
getItem: (key: string) => SecureStore.getItemAsync(key),
setItem: (key: string, value: string) => SecureStore.setItemAsync(key, value),
removeItem: (key: string) => SecureStore.deleteItemAsync(key),
};- Sessions are stored securely using Expo SecureStore
- Automatic token refresh enabled via Supabase client
- Session expiration handling with user-friendly error messages
- Secure Storage: All tokens stored in encrypted SecureStore
- Auto Refresh: Tokens automatically refreshed before expiration
- State Synchronization:
onAuthStateChangelistener keeps app state in sync - Session Fixation Prevention: New tokens generated on sign-in (handled by Supabase)
// Admin context monitors auth state changes
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange(
async (event, session) => {
if (event === 'SIGNED_OUT') {
await setIsAdmin(false);
} else if (event === 'SIGNED_IN' || event === 'TOKEN_REFRESHED') {
await verifyAdminStatus();
}
}
);
return () => subscription.unsubscribe();
}, []);- Initial check: Loads cached admin status from AsyncStorage
- Background verification: Verifies admin status with backend
- Cache expiry: Admin status re-verified every 5 minutes
- Auth state listener: Re-verifies on sign-in/sign-out events
// Admin verification checks user metadata
const isAdminUser =
session.user?.user_metadata?.role === 'admin' ||
session.user?.app_metadata?.role === 'admin';- Verifies JWT tokens with Supabase
- Adds user information to request object
- Sets
isAdminflag based on user metadata - Provides specific error messages for different failure scenarios
// Requires both authentication and admin role
export async function adminMiddleware(
request: AuthenticatedRequest,
reply: FastifyReply
) {
await authMiddleware(request, reply);
if (reply.sent) return;
if (!request.isAdmin) {
return reply.code(403).send({
error: 'Forbidden',
message: 'This resource requires administrator privileges'
});
}
}// Admin routes require authentication
if (!isAdmin) {
return <Redirect href="/tabs/bounty-app" />;
}All authentication forms implement comprehensive validation:
-
Email Validation
- Required field check
- RFC-compliant email regex:
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ - Real-time error clearing
-
Password Validation
- Required field check
- Minimum length enforcement
- Strong password requirements (sign-up only)
-
Field-Level Errors
- Visual feedback (red border on error)
- Clear error messages
- Real-time error clearing on input
Provides reusable validation functions:
validateEmail(email: string)validatePassword(password: string)validateUsername(username: string)validatePasswordMatch(password: string, confirmPassword: string)
Enforced in sign-up and password change flows:
// Pattern from hooks/use-form-validation.ts
strongPassword: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/Requirements:
- Minimum 8 characters
- At least one uppercase letter (A-Z)
- At least one lowercase letter (a-z)
- At least one number (0-9)
- At least one special character (@$!%*?&)
All password fields include show/hide toggle:
<TouchableOpacity
onPress={() => setShowPassword(s => !s)}
accessibilityLabel={showPassword ? 'Hide password' : 'Show password'}
>
<MaterialIcons
name={showPassword ? 'visibility-off' : 'visibility'}
size={20}
/>
</TouchableOpacity>- Requires current password (for user verification)
- Enforces strong password requirements
- Validates password match
- Uses Supabase
updateUserAPI for secure password updates
Comprehensive brute force protection on all auth endpoints.
All authentication endpoints are now protected with aggressive rate limiting:
- Limit: 5 requests per 15 minutes per IP address
- Response: HTTP 429 with retry information
- Headers: Standard rate limit headers (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset)
Protected Endpoints:
POST /app/auth/sign-up-form(Legacy API)POST /app/auth/sign-in-form(Legacy API)POST /auth/register(Legacy & Consolidated API)POST /auth/sign-in(Legacy & Consolidated API)POST /auth/sign-up(Consolidated API)POST /auth/identifier-sign-up(Legacy API)
Implementation Details:
- Legacy API: Uses
express-rate-limitmiddleware - Consolidated API: Custom in-memory rate limiter with automatic cleanup
- Per-IP tracking to prevent distributed attacks
- Logging of rate limit violations for monitoring
Documentation:
- See AUTH_RATE_LIMITING.md for complete details
- Test files:
tests/auth-rate-limiting*.test.js
Prevents brute force attacks:
// After 5 failed attempts
if (newAttempts >= 5) {
const lockout = Date.now() + (5 * 60 * 1000); // 5 minutes
setLockoutUntil(lockout);
setAuthError('Too many failed attempts. Please try again in 5 minutes.');
return;
}General API rate limiting per token:
- Limit: 100 requests per minute per token
- Window: 1 minute rolling window
- Response: 429 Too Many Requests with retry message
// Rate limiting configuration
const MAX_TOKENS = 100; // Maximum requests per window
const REFILL_RATE = 100; // Tokens to add per window
const WINDOW_MS = 60 * 1000; // 1 minute windowFor production environments, implement:
- Distributed rate limiting using Redis
- IP-based rate limiting at the API gateway level
- CAPTCHA after multiple failed login attempts
- Account lockout after repeated violations
-
Defense in Depth
- Multiple layers of validation (client + server)
- Admin verification at both frontend and backend
- Rate limiting at multiple levels
-
Principle of Least Privilege
- Users only get necessary permissions
- Admin routes require explicit admin role
- Token-based access control
-
Secure Defaults
- Sessions use secure storage by default
- Auto-refresh enabled
- HTTPS enforced (via Supabase)
-
Error Handling
- No sensitive information in error messages
- Generic messages for user enumeration prevention
- Detailed logging for debugging (server-side only)
-
Input Sanitization
- Email normalization (lowercase)
- Whitespace trimming
- Strong validation patterns
Backend should implement these headers (typically at reverse proxy level):
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'
Note: X-XSS-Protection header is deprecated and should not be used in modern applications. Use Content-Security-Policy instead.
- Token-based authentication provides natural CSRF protection
- No cookies used for authentication (JWT in Authorization header)
- State-changing operations require valid JWT
- React Native automatically escapes content
- No
dangerouslySetInnerHTMLusage - User input sanitized before display
- Strong password requirements enforced
- Email validation implemented
- Password visibility toggles added
- Rate limiting on login attempts
- Session tokens stored securely
- Automatic token refresh enabled
- Password reset functionality secured
- Admin routes protected (client-side)
- Admin middleware created (backend)
- Role verification implemented
- Admin status cached with expiry
- Auth state change monitoring
- Email validation with proper regex
- Password strength validation
- Real-time field validation
- Field-level error messages
- Input sanitization (normalization)
- Secure token storage (SecureStore)
- Session expiration handling
- Auto-refresh configured
- Session fixation protection (via Supabase)
- State synchronization
- Generic error messages for sensitive operations
- Specific errors for user feedback
- No sensitive data in error responses
- Server-side error logging
- JWT verification middleware
- Rate limiting implemented
- Admin-only middleware created
- Error handling standardized
- Token expiration checked
- Implement Redis-based rate limiting for production
- Add 2FA/MFA support
- Implement IP-based rate limiting
- Add CAPTCHA for repeated failures
- Set up security monitoring/alerting
- Implement audit logging for admin actions
- Add backend admin API endpoints
- Implement role-based access control (RBAC) database schema
- Add account lockout policies
- Implement email verification enforcement
- Add device fingerprinting
- Set up session invalidation on password change
If you discover a security vulnerability, please email security@bountyexpo.com with:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
Please do not create public GitHub issues for security vulnerabilities.
This document will be updated as new security features are implemented. Last updated: 2024
Note: This application uses Supabase for authentication, which provides enterprise-grade security. Many security features are handled by Supabase, including:
- Password hashing (bcrypt)
- Token signing and verification (JWT)
- Rate limiting (Supabase API level)
- DDoS protection
- Database security
Always keep Supabase client libraries up to date to benefit from security patches.