Understanding Authentication vs Authorization
Password-Based Authentication
Password Hashing with bcrypt
Typescript
import bcrypt from 'bcrypt';
const SALT_ROUNDS = 12;
export class AuthService {
async hashPassword(password: string): Promise<string> {
return await bcrypt.hash(password, SALT_ROUNDS);
}
async verifyPassword(password: string, hashedPassword: string): Promise<boolean> {
return await bcrypt.compare(password, hashedPassword);
}
async createUser(userData: CreateUserData): Promise<User> {
const hashedPassword = await this.hashPassword(userData.password);
const user = await this.userRepository.create({
...userData,
password: hashedPassword,
});
return user;
}
}
JWT (JSON Web Tokens) Implementation
JWT Service
Typescript
import jwt from 'jsonwebtoken';
interface TokenPayload {
userId: string;
email: string;
role: string;
}
export class JWTService {
private readonly secret: string;
private readonly expiresIn: string;
constructor() {
this.secret = process.env.JWT_SECRET!;
this.expiresIn = process.env.JWT_EXPIRES_IN || '24h';
}
generateToken(payload: TokenPayload): string {
return jwt.sign(payload, this.secret, {
expiresIn: this.expiresIn,
issuer: 'myapp.com',
audience: 'myapp-users',
});
}
verifyToken(token: string): TokenPayload | null {
try {
const decoded = jwt.verify(token, this.secret) as TokenPayload;
return decoded;
} catch (error) {
return null;
}
}
}
Session-Based Authentication
Session Configuration
Typescript
import session from 'express-session';
import connectRedis from 'connect-redis';
import Redis from 'ioredis';
const RedisStore = connectRedis(session);
const redis = new Redis(process.env.REDIS_URL);
export const sessionConfig = {
store: new RedisStore({ client: redis }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000, // 24 hours
sameSite: 'strict' as const,
},
name: 'sessionId',
};
OAuth 2.0 and Social Authentication
Google OAuth Implementation
Typescript
import { OAuth2Client } from 'google-auth-library';
export class GoogleAuthService {
private client: OAuth2Client;
constructor() {
this.client = new OAuth2Client(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
process.env.GOOGLE_REDIRECT_URI
);
}
getAuthUrl(): string {
return this.client.generateAuthUrl({
access_type: 'offline',
scope: ['profile', 'email'],
prompt: 'consent',
});
}
async getUserInfo(accessToken: string): Promise<GoogleUserInfo> {
const ticket = await this.client.verifyIdToken({
idToken: accessToken,
audience: process.env.GOOGLE_CLIENT_ID,
});
const payload = ticket.getPayload();
return {
id: payload!.sub,
email: payload!.email!,
name: payload!.name!,
picture: payload!.picture,
};
}
}
Multi-Factor Authentication (MFA)
TOTP Implementation
Typescript
import speakeasy from 'speakeasy';
import QRCode from 'qrcode';
export class MFAService {
generateSecret(userEmail: string): { secret: string; qrCodeUrl: string } {
const secret = speakeasy.generateSecret({
name: `MyApp (${userEmail})`,
issuer: 'MyApp',
});
return {
secret: secret.base32,
qrCodeUrl: secret.otpauth_url!,
};
}
verifyTOTP(token: string, secret: string): boolean {
return speakeasy.totp.verify({
secret,
encoding: 'base32',
token,
window: 2, // Allow 2 time windows for clock skew
});
}
}
Authorization Patterns
Role-Based Access Control (RBAC)
Typescript
enum Role {
ADMIN = 'admin',
MODERATOR = 'moderator',
USER = 'user',
GUEST = 'guest',
}
enum Permission {
READ_USERS = 'read:users',
WRITE_USERS = 'write:users',
DELETE_USERS = 'delete:users',
READ_POSTS = 'read:posts',
WRITE_POSTS = 'write:posts',
DELETE_POSTS = 'delete:posts',
}
const rolePermissions: Record<Role, Permission[]> = {
[Role.ADMIN]: Object.values(Permission),
[Role.MODERATOR]: [
Permission.READ_USERS,
Permission.READ_POSTS,
Permission.WRITE_POSTS,
],
[Role.USER]: [Permission.READ_USERS, Permission.READ_POSTS, Permission.WRITE_POSTS],
[Role.GUEST]: [Permission.READ_POSTS],
};
export class AuthorizationService {
hasPermission(userRole: Role, permission: Permission): boolean {
return rolePermissions[userRole]?.includes(permission) || false;
}
}
Security Best Practices
Password Security
Typescript
import zxcvbn from 'zxcvbn';
export class PasswordPolicy {
private static readonly MIN_STRENGTH = 3; // 0-4 scale
private static readonly MIN_LENGTH = 8;
static validatePassword(password: string): { valid: boolean; errors: string[] } {
const errors: string[] = [];
const strength = zxcvbn(password);
if (password.length < this.MIN_LENGTH) {
errors.push(`Password must be at least ${this.MIN_LENGTH} characters long`);
}
if (strength.score < this.MIN_STRENGTH) {
errors.push('Password is too weak. Please use a stronger password.');
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain at least one uppercase letter');
}
if (!/[a-z]/.test(password)) {
errors.push('Password must contain at least one lowercase letter');
}
if (!/\d/.test(password)) {
errors.push('Password must contain at least one number');
}
return {
valid: errors.length === 0,
errors,
};
}
}
Rate Limiting
Typescript
import rateLimit from 'express-rate-limit';
// General rate limiting
export 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, please try again later.',
standardHeaders: true,
legacyHeaders: false,
});
// Strict rate limiting for auth endpoints
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many authentication attempts, please try again later.',
skipSuccessfulRequests: true,
});
Best Practices
- Never store plain text passwords - Always hash them with bcrypt
- Use HTTPS in production - Protect data in transit
- Implement proper session management - Secure session storage
- Add rate limiting - Prevent brute force attacks
- Use strong password policies - Enforce complex passwords
- Implement MFA - Add an extra layer of security
- Follow the principle of least privilege - Grant minimum necessary permissions
- Regular security audits - Test and review your security measures
- Keep dependencies updated - Patch security vulnerabilities
- Monitor and log - Track authentication events for security analysis