Overview
Security is paramount in authentication systems. The Strike Auth Service implements multiple layers of security to protect user data, prevent unauthorized access, and maintain system integrity. This guide covers the key security concepts, best practices, and implementation strategies.
Authentication Security
Multi-Factor Authentication (MFA)
The Strike Auth Service supports multiple authentication factors to enhance security:
Email Verification Email-based verification for account creation and password recovery
SMS OTP SMS-based one-time passwords for additional security
Magic Links Passwordless authentication via secure email links
OAuth Providers Integration with trusted OAuth providers (Google, GitHub, etc.)
Password Security
Password Requirements
const passwordRequirements = {
minLength: 8 ,
maxLength: 128 ,
requireUppercase: true ,
requireLowercase: true ,
requireNumbers: true ,
requireSpecialChars: true ,
preventCommonPasswords: true ,
preventUserInfoInPassword: true
};
// Example validation
function validatePassword ( password , userInfo ) {
const checks = {
length: password . length >= 8 && password . length <= 128 ,
uppercase: / [ A-Z ] / . test ( password ),
lowercase: / [ a-z ] / . test ( password ),
numbers: / \d / . test ( password ),
special: / [ !@#$%^&*(),.?":{}|<> ] / . test ( password ),
notCommon: ! commonPasswords . includes ( password . toLowerCase ()),
notUserInfo: ! containsUserInfo ( password , userInfo )
};
return Object . values ( checks ). every ( check => check );
}
Password Hashing
The service uses industry-standard bcrypt with appropriate salt rounds:
const bcrypt = require ( 'bcrypt' );
// Secure password hashing
async function hashPassword ( password ) {
const saltRounds = 12 ; // Recommended for 2024
return await bcrypt . hash ( password , saltRounds );
}
// Secure password verification
async function verifyPassword ( password , hash ) {
return await bcrypt . compare ( password , hash );
}
Token Security
JWT Implementation
Token Structure
// JWT Header
{
"alg" : "HS256" ,
"typ" : "JWT"
}
// JWT Payload
{
"sub" : "user_id" ,
"email" : "user@example.com" ,
"role" : "authenticated" ,
"iat" : 1703097600 ,
"exp" : 1703184000 ,
"aud" : "authenticated" ,
"iss" : "https://api.strike.com/auth"
}
Token Security Features
Access tokens expire within 1 hour to limit exposure window const tokenExpiry = {
accessToken: '1h' ,
refreshToken: '30d' ,
magicLink: '15m' ,
resetToken: '1h'
};
Tokens are signed with strong secrets and algorithms const jwt = require ( 'jsonwebtoken' );
// Use strong, randomly generated secrets
const JWT_SECRET = process . env . JWT_SECRET ; // 256-bit random string
const REFRESH_SECRET = process . env . REFRESH_SECRET ; // Separate secret
// Sign with secure algorithm
const token = jwt . sign ( payload , JWT_SECRET , {
algorithm: 'HS256' ,
expiresIn: '1h' ,
issuer: 'https://api.strike.com/auth' ,
audience: 'authenticated'
});
Refresh tokens are rotated on each use to prevent replay attacks async function refreshTokens ( refreshToken ) {
// Verify current refresh token
const decoded = jwt . verify ( refreshToken , REFRESH_SECRET );
// Invalidate old refresh token
await invalidateRefreshToken ( refreshToken );
// Generate new token pair
const newAccessToken = generateAccessToken ( decoded . sub );
const newRefreshToken = generateRefreshToken ( decoded . sub );
return { accessToken: newAccessToken , refreshToken: newRefreshToken };
}
Token Storage Security
Client-Side Storage
Never store sensitive tokens in localStorage or sessionStorage in production applications.
Recommended Approaches:
HTTP-Only Cookies (Most Secure)
// Set secure HTTP-only cookie
app . use ( cookieParser ());
function setTokenCookie ( res , token ) {
res . cookie ( 'auth_token' , token , {
httpOnly: true ,
secure: process . env . NODE_ENV === 'production' ,
sameSite: 'strict' ,
maxAge: 3600000 , // 1 hour
path: '/'
});
}
Memory Storage with Refresh
// Store in memory, refresh before expiry
class TokenManager {
constructor () {
this . accessToken = null ;
this . refreshToken = null ;
this . expiryTime = null ;
}
setTokens ( accessToken , refreshToken ) {
this . accessToken = accessToken ;
this . refreshToken = refreshToken ;
// Decode to get expiry
const decoded = jwt . decode ( accessToken );
this . expiryTime = decoded . exp * 1000 ;
// Auto-refresh before expiry
this . scheduleRefresh ();
}
scheduleRefresh () {
const timeUntilExpiry = this . expiryTime - Date . now ();
const refreshTime = timeUntilExpiry - 300000 ; // 5 minutes before expiry
setTimeout (() => this . refreshTokens (), refreshTime );
}
}
Rate Limiting and DDoS Protection
Request Rate Limiting
const rateLimit = require ( 'express-rate-limit' );
// General API rate limiting
const generalLimiter = rateLimit ({
windowMs: 15 * 60 * 1000 , // 15 minutes
max: 100 , // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP' ,
standardHeaders: true ,
legacyHeaders: false
});
// Strict rate limiting for auth endpoints
const authLimiter = rateLimit ({
windowMs: 15 * 60 * 1000 , // 15 minutes
max: 5 , // Limit each IP to 5 auth attempts per windowMs
message: 'Too many authentication attempts' ,
skipSuccessfulRequests: true
});
// Apply to routes
app . use ( '/auth/login' , authLimiter );
app . use ( '/auth/signup' , authLimiter );
app . use ( '/auth/recover' , authLimiter );
Advanced Rate Limiting
// User-specific rate limiting
const userLimiter = rateLimit ({
windowMs: 15 * 60 * 1000 ,
max: 10 ,
keyGenerator : ( req ) => req . user ?. id || req . ip ,
skip : ( req ) => req . user ?. role === 'admin'
});
// Sliding window rate limiting
class SlidingWindowLimiter {
constructor ( maxRequests , windowMs ) {
this . maxRequests = maxRequests ;
this . windowMs = windowMs ;
this . requests = new Map ();
}
isAllowed ( key ) {
const now = Date . now ();
const userRequests = this . requests . get ( key ) || [];
// Remove old requests outside window
const validRequests = userRequests . filter (
timestamp => now - timestamp < this . windowMs
);
if ( validRequests . length >= this . maxRequests ) {
return false ;
}
validRequests . push ( now );
this . requests . set ( key , validRequests );
return true ;
}
}
Request Validation
const Joi = require ( 'joi' );
// Email validation schema
const emailSchema = Joi . string ()
. email ({ tlds: { allow: false } })
. max ( 254 )
. required ();
// Password validation schema
const passwordSchema = Joi . string ()
. min ( 8 )
. max ( 128 )
. pattern ( / ^ (?= . * [ a-z ] )(?= . * [ A-Z ] )(?= . * \d )(?= . * [ @$!%*?& ] ) [ A-Za-z\d@$!%*?& ] / )
. required ();
// User registration validation
const registrationSchema = Joi . object ({
email: emailSchema ,
password: passwordSchema ,
name: Joi . string (). min ( 1 ). max ( 100 ). required (),
metadata: Joi . object (). max ( 10 ) // Limit metadata size
});
// Validation middleware
function validateRegistration ( req , res , next ) {
const { error , value } = registrationSchema . validate ( req . body );
if ( error ) {
return res . status ( 400 ). json ({
error: 'Validation failed' ,
details: error . details . map ( d => d . message )
});
}
req . validatedData = value ;
next ();
}
SQL Injection Prevention
// Use parameterized queries
async function getUserByEmail ( email ) {
const { data , error } = await supabase
. from ( 'users' )
. select ( '*' )
. eq ( 'email' , email ) // Automatically parameterized
. single ();
return { data , error };
}
// For raw SQL (if needed), use proper escaping
const { Pool } = require ( 'pg' );
const pool = new Pool ();
async function getUserByEmailRaw ( email ) {
const query = 'SELECT * FROM users WHERE email = $1' ;
const values = [ email ];
const result = await pool . query ( query , values );
return result . rows [ 0 ];
}
HTTPS and Transport Security
TLS Configuration
const https = require ( 'https' );
const fs = require ( 'fs' );
// HTTPS server configuration
const httpsOptions = {
key: fs . readFileSync ( 'path/to/private-key.pem' ),
cert: fs . readFileSync ( 'path/to/certificate.pem' ),
// Use strong cipher suites
ciphers: [
'ECDHE-RSA-AES128-GCM-SHA256' ,
'ECDHE-RSA-AES256-GCM-SHA384' ,
'ECDHE-RSA-AES128-SHA256' ,
'ECDHE-RSA-AES256-SHA384'
]. join ( ':' ),
honorCipherOrder: true ,
secureProtocol: 'TLSv1_2_method'
};
const server = https . createServer ( httpsOptions , app );
const helmet = require ( 'helmet' );
// Apply security headers
app . use ( helmet ({
contentSecurityPolicy: {
directives: {
defaultSrc: [ "'self'" ],
styleSrc: [ "'self'" , "'unsafe-inline'" ],
scriptSrc: [ "'self'" ],
imgSrc: [ "'self'" , "data:" , "https:" ],
connectSrc: [ "'self'" , "https://api.strike.com" ]
}
},
hsts: {
maxAge: 31536000 ,
includeSubDomains: true ,
preload: true
}
}));
// Additional security headers
app . use (( req , res , next ) => {
res . setHeader ( 'X-Frame-Options' , 'DENY' );
res . setHeader ( 'X-Content-Type-Options' , 'nosniff' );
res . setHeader ( 'Referrer-Policy' , 'strict-origin-when-cross-origin' );
next ();
});
CORS Security
const cors = require ( 'cors' );
// Secure CORS configuration
const corsOptions = {
origin : function ( origin , callback ) {
const allowedOrigins = [
'https://app.strike.com' ,
'https://dashboard.strike.com' ,
'https://strike.com'
];
// Allow requests with no origin (mobile apps, etc.)
if ( ! origin ) return callback ( null , true );
if ( allowedOrigins . includes ( origin )) {
callback ( null , true );
} else {
callback ( new Error ( 'Not allowed by CORS' ));
}
},
credentials: true ,
methods: [ 'GET' , 'POST' , 'PUT' , 'DELETE' ],
allowedHeaders: [ 'Content-Type' , 'Authorization' ],
maxAge: 86400 // 24 hours
};
app . use ( cors ( corsOptions ));
Session Security
Session Management
const session = require ( 'express-session' );
const MongoStore = require ( 'connect-mongo' );
// Secure session configuration
app . use ( session ({
secret: process . env . SESSION_SECRET ,
resave: false ,
saveUninitialized: false ,
store: MongoStore . create ({
mongoUrl: process . env . MONGODB_URI ,
touchAfter: 24 * 3600 // Lazy session update
}),
cookie: {
secure: process . env . NODE_ENV === 'production' ,
httpOnly: true ,
maxAge: 1000 * 60 * 60 * 24 , // 24 hours
sameSite: 'strict'
},
name: 'strike_session' // Don't use default session name
}));
Session Invalidation
// Logout and invalidate session
app . post ( '/auth/logout' , async ( req , res ) => {
try {
// Invalidate refresh token in database
if ( req . user ?. id ) {
await supabase
. from ( 'refresh_tokens' )
. delete ()
. eq ( 'user_id' , req . user . id );
}
// Destroy session
req . session . destroy (( err ) => {
if ( err ) {
console . error ( 'Session destruction error:' , err );
}
// Clear cookie
res . clearCookie ( 'strike_session' );
res . json ({ message: 'Logged out successfully' });
});
} catch ( error ) {
console . error ( 'Logout error:' , error );
res . status ( 500 ). json ({ error: 'Logout failed' });
}
});
Audit Logging and Monitoring
Security Event Logging
// Security event logger
class SecurityLogger {
static async logEvent ( event , details ) {
const logEntry = {
timestamp: new Date (). toISOString (),
event_type: event ,
ip_address: details . ip ,
user_agent: details . userAgent ,
user_id: details . userId ,
email: details . email ,
success: details . success ,
error_message: details . error ,
metadata: details . metadata
};
// Log to database
await supabase
. from ( 'security_logs' )
. insert ( logEntry );
// Log to external service for critical events
if ( this . isCriticalEvent ( event )) {
await this . sendToSecurityService ( logEntry );
}
}
static isCriticalEvent ( event ) {
const criticalEvents = [
'failed_login_attempt' ,
'account_lockout' ,
'password_reset_request' ,
'admin_action' ,
'suspicious_activity'
];
return criticalEvents . includes ( event );
}
}
// Usage in authentication
app . post ( '/auth/login' , async ( req , res ) => {
try {
const { email , password } = req . body ;
const result = await authenticateUser ( email , password );
await SecurityLogger . logEvent ( 'login_attempt' , {
ip: req . ip ,
userAgent: req . get ( 'User-Agent' ),
email: email ,
success: !! result . user ,
error: result . error ?. message
});
if ( result . user ) {
res . json ({ user: result . user , token: result . token });
} else {
res . status ( 401 ). json ({ error: 'Invalid credentials' });
}
} catch ( error ) {
await SecurityLogger . logEvent ( 'login_error' , {
ip: req . ip ,
userAgent: req . get ( 'User-Agent' ),
email: req . body . email ,
success: false ,
error: error . message
});
res . status ( 500 ). json ({ error: 'Authentication failed' });
}
});
Threat Detection and Response
Suspicious Activity Detection
// Anomaly detection
class ThreatDetector {
static async checkSuspiciousActivity ( userId , activity ) {
const checks = await Promise . all ([
this . checkRapidRequests ( userId ),
this . checkUnusualLocation ( userId , activity . ip ),
this . checkDeviceFingerprint ( userId , activity . userAgent ),
this . checkTimePattern ( userId )
]);
const suspiciousScore = checks . reduce (( score , check ) => score + check . score , 0 );
if ( suspiciousScore > 70 ) {
await this . triggerSecurityResponse ( userId , suspiciousScore , checks );
}
return { suspicious: suspiciousScore > 50 , score: suspiciousScore };
}
static async triggerSecurityResponse ( userId , score , details ) {
// Lock account temporarily
await supabase
. from ( 'users' )
. update ({
locked_until: new Date ( Date . now () + 15 * 60 * 1000 ), // 15 minutes
lock_reason: 'suspicious_activity'
})
. eq ( 'id' , userId );
// Send security alert
await this . sendSecurityAlert ( userId , score , details );
// Log incident
await SecurityLogger . logEvent ( 'suspicious_activity' , {
userId ,
score ,
details ,
action: 'account_locked'
});
}
}
Security Best Practices
Development Guidelines
Principle of Least Privilege Grant minimum necessary permissions to users and services
Defense in Depth Implement multiple layers of security controls
Fail Securely Ensure system fails to a secure state when errors occur
Regular Updates Keep dependencies and security patches up to date
Security Checklist
Compliance and Standards
GDPR Compliance
// Data subject rights implementation
class GDPRCompliance {
static async handleDataRequest ( userId , requestType ) {
switch ( requestType ) {
case 'access' :
return await this . exportUserData ( userId );
case 'rectification' :
return await this . updateUserData ( userId );
case 'erasure' :
return await this . deleteUserData ( userId );
case 'portability' :
return await this . exportPortableData ( userId );
default :
throw new Error ( 'Invalid request type' );
}
}
static async deleteUserData ( userId ) {
// Anonymize or delete user data
await supabase
. from ( 'users' )
. update ({
email: `deleted_ ${ userId } @example.com` ,
name: 'Deleted User' ,
deleted_at: new Date (). toISOString ()
})
. eq ( 'id' , userId );
// Log deletion for compliance
await SecurityLogger . logEvent ( 'gdpr_deletion' , {
userId ,
timestamp: new Date (). toISOString ()
});
}
}
SOC 2 Compliance
Key controls for SOC 2 Type II compliance:
Access Controls : Role-based permissions and MFA
Encryption : Data encryption at rest and in transit
Monitoring : Comprehensive logging and alerting
Incident Response : Documented procedures and testing
Change Management : Controlled deployment processes
This comprehensive security framework ensures that the Strike Auth Service maintains the highest security standards while providing a seamless user experience.