Authentication
Learn how to implement secure authentication in your applications. This guide covers user registration, login, multi-factor authentication, and authorization with best practices.
Prerequisites
- Understanding of web security concepts
- Familiarity with JWT and session-based auth
- Basic knowledge of cryptography
- Experience with Node.js and Express
Authentication Overview

Visual representation of the authentication workflow and components.
1
Authentication Setup
Configure the authentication system:
// Authentication configuration
const authConfig = {
// JWT configuration
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: '24h',
algorithm: 'HS256',
issuer: 'symbiosis.host',
audience: 'symbiosis.host/api'
},
// Password configuration
password: {
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecial: true,
saltRounds: 12
},
// Session configuration
session: {
name: 'sid',
secret: process.env.SESSION_SECRET,
cookie: {
maxAge: 86400000, // 24 hours
httpOnly: true,
secure: true,
sameSite: 'strict'
},
resave: false,
saveUninitialized: false
},
// Rate limiting
rateLimit: {
window: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests, please try again later'
}
};
// Initialize authentication
const initAuth = (app) => {
// Set up session middleware
app.use(session(authConfig.session));
// Set up rate limiting
app.use('/api/auth', rateLimit(authConfig.rateLimit));
// Initialize passport
app.use(passport.initialize());
app.use(passport.session());
// Configure passport strategies
configurePassport();
return app;
};
2
User Registration
Implement secure user registration:
// User registration service
const userService = {
async register(userData) {
// Validate user data
const validationResult = this.validateUserData(userData);
if (!validationResult.valid) {
throw new Error(`Invalid user data: ${validationResult.errors.join(', ')}`);
}
// Check if user already exists
const existingUser = await db.users.findOne({ email: userData.email });
if (existingUser) {
throw new Error('User with this email already exists');
}
// Hash password
const passwordHash = await bcrypt.hash(
userData.password,
authConfig.password.saltRounds
);
// Create user
const user = await db.users.create({
email: userData.email,
passwordHash,
firstName: userData.firstName,
lastName: userData.lastName,
role: 'user',
status: 'active',
createdAt: new Date()
});
// Create user profile
await db.profiles.create({
userId: user.id,
createdAt: new Date()
});
// Send welcome email
await emailService.sendWelcomeEmail(user);
return this.sanitizeUser(user);
},
validateUserData(data) {
const errors = [];
// Validate email
if (!data.email || !this.isValidEmail(data.email)) {
errors.push('Valid email is required');
}
// Validate password
if (!data.password) {
errors.push('Password is required');
} else {
if (data.password.length < authConfig.password.minLength) {
errors.push(`Password must be at least ${authConfig.password.minLength} characters`);
}
if (authConfig.password.requireUppercase && !/[A-Z]/.test(data.password)) {
errors.push('Password must contain at least one uppercase letter');
}
if (authConfig.password.requireLowercase && !/[a-z]/.test(data.password)) {
errors.push('Password must contain at least one lowercase letter');
}
if (authConfig.password.requireNumbers && !/[0-9]/.test(data.password)) {
errors.push('Password must contain at least one number');
}
if (authConfig.password.requireSpecial && !/[!@#$%^&*]/.test(data.password)) {
errors.push('Password must contain at least one special character');
}
}
// Validate name
if (!data.firstName || !data.lastName) {
errors.push('First and last name are required');
}
return {
valid: errors.length === 0,
errors
};
}
};
3
Login and Authentication
Implement secure login and authentication:
// Authentication service
const authService = {
async login(email, password) {
// Find user
const user = await db.users.findOne({ email });
if (!user) {
throw new Error('Invalid credentials');
}
// Check if user is active
if (user.status !== 'active') {
throw new Error('Account is not active');
}
// Verify password
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
// Log failed attempt
await this.logAuthAttempt(user.id, 'login', false);
throw new Error('Invalid credentials');
}
// Log successful attempt
await this.logAuthAttempt(user.id, 'login', true);
// Update last login
await db.users.update(
{ id: user.id },
{ lastLogin: new Date() }
);
// Generate token
const token = this.generateToken(user);
return {
user: userService.sanitizeUser(user),
token
};
},
generateToken(user) {
const payload = {
sub: user.id,
email: user.email,
role: user.role
};
return jwt.sign(
payload,
authConfig.jwt.secret,
{
expiresIn: authConfig.jwt.expiresIn,
algorithm: authConfig.jwt.algorithm,
issuer: authConfig.jwt.issuer,
audience: authConfig.jwt.audience
}
);
},
async verifyToken(token) {
try {
const decoded = jwt.verify(
token,
authConfig.jwt.secret,
{
algorithms: [authConfig.jwt.algorithm],
issuer: authConfig.jwt.issuer,
audience: authConfig.jwt.audience
}
);
// Get user
const user = await db.users.findOne({ id: decoded.sub });
if (!user || user.status !== 'active') {
return null;
}
return userService.sanitizeUser(user);
} catch (error) {
return null;
}
}
};
4
Multi-Factor Authentication
Implement multi-factor authentication:
// MFA service
const mfaService = {
// MFA methods
methods: {
totp: {
name: 'Time-based One-Time Password',
setup: async (userId) => {
// Generate secret
const secret = speakeasy.generateSecret({
length: 20,
name: `Symbiosis.host (${userId})`
});
// Store secret
await db.mfa.create({
userId,
method: 'totp',
secret: secret.base32,
active: false,
createdAt: new Date()
});
// Generate QR code
const qrCode = await qrcode.toDataURL(secret.otpauth_url);
return {
secret: secret.base32,
qrCode
};
},
verify: async (userId, token) => {
// Get MFA record
const mfa = await db.mfa.findOne({
userId,
method: 'totp'
});
if (!mfa) {
return false;
}
// Verify token
const verified = speakeasy.totp.verify({
secret: mfa.secret,
encoding: 'base32',
token,
window: 1 // Allow 1 period before/after
});
if (verified && !mfa.active) {
// Activate MFA if this is the first verification
await db.mfa.update(
{ id: mfa.id },
{ active: true }
);
}
return verified;
}
},
sms: {
name: 'SMS Authentication',
setup: async (userId, phoneNumber) => {
// Validate phone number
if (!phoneNumber || !phoneNumber.match(/^\+[1-9]\d{1,14}$/)) {
throw new Error('Invalid phone number format');
}
// Store phone number
await db.mfa.create({
userId,
method: 'sms',
phoneNumber,
active: false,
createdAt: new Date()
});
// Generate verification code
const code = this.generateCode();
// Store code
await db.verificationCodes.create({
userId,
code,
type: 'mfa_setup',
expiresAt: new Date(Date.now() + 10 * 60 * 1000) // 10 minutes
});
// Send SMS
await smsService.send(phoneNumber, `Your verification code is: ${code}`);
return true;
},
verify: async (userId, code) => {
// Get verification code
const verification = await db.verificationCodes.findOne({
userId,
code,
type: 'mfa_setup',
expiresAt: { $gt: new Date() }
});
if (!verification) {
return false;
}
// Delete verification code
await db.verificationCodes.delete({ id: verification.id });
// Activate MFA
await db.mfa.update(
{ userId, method: 'sms' },
{ active: true }
);
return true;
},
challenge: async (userId) => {
// Get MFA record
const mfa = await db.mfa.findOne({
userId,
method: 'sms',
active: true
});
if (!mfa) {
throw new Error('SMS MFA not set up');
}
// Generate code
const code = this.generateCode();
// Store code
await db.verificationCodes.create({
userId,
code,
type: 'mfa_login',
expiresAt: new Date(Date.now() + 10 * 60 * 1000) // 10 minutes
});
// Send SMS
await smsService.send(mfa.phoneNumber, `Your login code is: ${code}`);
return true;
}
}
},
// Generate random code
generateCode() {
return Math.floor(100000 + Math.random() * 900000).toString();
},
// Get user's MFA methods
async getUserMethods(userId) {
const methods = await db.mfa.find({ userId });
return methods.map(method => ({
id: method.id,
method: method.method,
name: this.methods[method.method].name,
active: method.active,
createdAt: method.createdAt
}));
}
};
5
Authorization and Access Control
Implement authorization and access control:
// Authorization service
const authorizationService = {
// Role definitions
roles: {
admin: {
name: 'Administrator',
permissions: ['read:*', 'write:*', 'delete:*']
},
manager: {
name: 'Manager',
permissions: ['read:*', 'write:users', 'write:content']
},
user: {
name: 'User',
permissions: ['read:content', 'write:own_content']
},
guest: {
name: 'Guest',
permissions: ['read:public_content']
}
},
// Check if user has permission
hasPermission(user, permission) {
if (!user) {
return this.roles.guest.permissions.includes(permission);
}
const role = this.roles[user.role];
if (!role) {
return false;
}
// Check for wildcard permissions
for (const perm of role.permissions) {
if (perm === permission) {
return true;
}
if (perm.endsWith(':*')) {
const prefix = perm.slice(0, -2);
if (permission.startsWith(prefix)) {
return true;
}
}
}
return false;
},
// Middleware to check permission
requirePermission(permission) {
return (req, res, next) => {
if (!this.hasPermission(req.user, permission)) {
return res.status(403).json({
error: 'Forbidden',
message: 'You do not have permission to access this resource'
});
}
next();
};
},
// Assign role to user
async assignRole(userId, role) {
if (!this.roles[role]) {
throw new Error(`Invalid role: ${role}`);
}
await db.users.update(
{ id: userId },
{ role }
);
return true;
}
};
Best Practices
Password Security
Best practices for password management:
- Use strong hashing algorithms
- Implement password policies
- Prevent credential stuffing
- Secure password reset flows
Token Management
Secure token handling:
- Short expiration times
- Secure storage
- Proper validation
- Token revocation
Multi-Factor Authentication
Enhance security with MFA:
- Offer multiple methods
- Secure recovery options
- Risk-based challenges
- Clear user guidance
Authentication Methods Comparison
Method | Pros | Cons | Best For |
---|---|---|---|
Session-based |
|
| Traditional web applications, single-server setups |
JWT-based |
|
| Microservices, SPAs, distributed systems |
OAuth/OIDC |
|
| Applications requiring third-party integration, SSO |
Passwordless |
|
| Consumer applications, security-focused services |
Security Considerations
OWASP Top 10 Mitigations
- Broken Authentication: Implement account lockout, MFA, and strong password policies
- Sensitive Data Exposure: Encrypt sensitive data, use HTTPS, and implement proper access controls
- Broken Access Control: Enforce proper authorization checks and principle of least privilege
- Security Misconfiguration: Use secure defaults and regularly audit configurations
- Cross-Site Scripting: Implement proper output encoding and Content Security Policy
Common Attack Vectors
- Credential Stuffing: Use rate limiting, CAPTCHA, and monitor for unusual login patterns
- Brute Force: Implement progressive delays and account lockout policies
- Session Hijacking: Use secure cookies, HTTPS, and short session timeouts
- Man-in-the-Middle: Enforce HTTPS with HSTS and certificate pinning
- Phishing: Implement MFA and user education
Common Issues
Authentication Failures
Common authentication problems:
- Expired tokens or sessions
- Incorrect credentials
- Account lockouts
- CORS issues with tokens
MFA Challenges
Multi-factor authentication issues:
- Time synchronization problems
- Lost access to MFA device
- SMS delivery failures
- Recovery code management