Authentication & Authorization

Introduction

Authentication and Authorization are fundamental security concepts that control access to resources. Authentication verifies who a user is, while authorization determines what they can do.

Key Concepts:

  • Identity Management
  • Access Control
  • Session Management
  • Token-based Auth
  • Single Sign-On
  • Multi-factor Authentication

Authentication Methods

Password Authentication

import { compare, hash } from 'bcrypt';

class PasswordAuth {
    private readonly SALT_ROUNDS = 12;

    async hashPassword(password: string): Promise {
        return await hash(password, this.SALT_ROUNDS);
    }

    async verifyPassword(
        password: string,
        hashedPassword: string
    ): Promise {
        return await compare(password, hashedPassword);
    }

    validatePasswordStrength(password: string): boolean {
        const minLength = 12;
        const hasUpperCase = /[A-Z]/.test(password);
        const hasLowerCase = /[a-z]/.test(password);
        const hasNumbers = /\d/.test(password);
        const hasSpecialChars = /[!@#$%^&*]/.test(password);

        return password.length >= minLength &&
            hasUpperCase &&
            hasLowerCase &&
            hasNumbers &&
            hasSpecialChars;
    }
}

Multi-Factor Authentication

import { authenticator } from 'otplib';
import { createTransport } from 'nodemailer';

class MFAService {
    private emailTransport;

    constructor() {
        this.emailTransport = createTransport({
            host: 'smtp.example.com',
            port: 587,
            secure: false,
            auth: {
                user: process.env.SMTP_USER,
                pass: process.env.SMTP_PASS
            }
        });
    }

    generateTOTPSecret(): string {
        return authenticator.generateSecret();
    }

    verifyTOTP(token: string, secret: string): boolean {
        return authenticator.verify({ token, secret });
    }

    async sendEmailCode(email: string): Promise {
        const code = Math.random().toString(36).slice(-6);
        
        await this.emailTransport.sendMail({
            from: 'security@example.com',
            to: email,
            subject: 'Your verification code',
            text: `Your verification code is: ${code}`
        });

        return code;
    }
}

Authorization Systems

Role-Based Access Control (RBAC)

interface Role {
    name: string;
    permissions: Set;
}

class RBACSystem {
    private roles: Map;
    private userRoles: Map>;

    constructor() {
        this.roles = new Map();
        this.userRoles = new Map();
    }

    createRole(name: string, permissions: string[]): void {
        this.roles.set(name, {
            name,
            permissions: new Set(permissions)
        });
    }

    assignRole(userId: string, roleName: string): void {
        if (!this.roles.has(roleName)) {
            throw new Error(`Role ${roleName} does not exist`);
        }

        if (!this.userRoles.has(userId)) {
            this.userRoles.set(userId, new Set());
        }

        this.userRoles.get(userId)?.add(roleName);
    }

    hasPermission(userId: string, permission: string): boolean {
        const userRoleNames = this.userRoles.get(userId) || new Set();
        
        for (const roleName of userRoleNames) {
            const role = this.roles.get(roleName);
            if (role?.permissions.has(permission)) {
                return true;
            }
        }
        
        return false;
    }
}

JWT Implementation

import { sign, verify } from 'jsonwebtoken';

interface TokenPayload {
    userId: string;
    roles: string[];
    exp?: number;
}

class JWTService {
    private readonly secret: string;
    private readonly accessTokenExpiry: string;
    private readonly refreshTokenExpiry: string;

    constructor() {
        this.secret = process.env.JWT_SECRET || 'your-secret-key';
        this.accessTokenExpiry = '15m';
        this.refreshTokenExpiry = '7d';
    }

    generateAccessToken(payload: TokenPayload): string {
        return sign(payload, this.secret, {
            expiresIn: this.accessTokenExpiry
        });
    }

    generateRefreshToken(userId: string): string {
        return sign({ userId }, this.secret, {
            expiresIn: this.refreshTokenExpiry
        });
    }

    verifyToken(token: string): TokenPayload {
        try {
            return verify(token, this.secret) as TokenPayload;
        } catch (error) {
            if (error.name === 'TokenExpiredError') {
                throw new Error('Token has expired');
            }
            throw new Error('Invalid token');
        }
    }

    refreshAccessToken(refreshToken: string): string {
        const payload = this.verifyToken(refreshToken);
        
        if (!payload.userId) {
            throw new Error('Invalid refresh token');
        }

        return this.generateAccessToken({
            userId: payload.userId,
            roles: payload.roles || []
        });
    }
}

OAuth & OIDC

OAuth 2.0 Flow

class OAuthService {
    private clientId: string;
    private clientSecret: string;
    private redirectUri: string;

    constructor() {
        this.clientId = process.env.OAUTH_CLIENT_ID;
        this.clientSecret = process.env.OAUTH_CLIENT_SECRET;
        this.redirectUri = process.env.OAUTH_REDIRECT_URI;
    }

    generateAuthUrl(
        provider: string,
        state: string,
        scope: string[]
    ): string {
        const params = new URLSearchParams({
            client_id: this.clientId,
            redirect_uri: this.redirectUri,
            response_type: 'code',
            scope: scope.join(' '),
            state
        });

        return `${provider}/oauth/authorize?${params.toString()}`;
    }

    async exchangeCodeForToken(
        provider: string,
        code: string
    ): Promise {
        const params = new URLSearchParams({
            client_id: this.clientId,
            client_secret: this.clientSecret,
            code,
            grant_type: 'authorization_code',
            redirect_uri: this.redirectUri
        });

        const response = await fetch(
            `${provider}/oauth/token`,
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: params.toString()
            }
        );

        if (!response.ok) {
            throw new Error('Failed to exchange code for token');
        }

        return response.json();
    }
}

Best Practices

Authentication:

  • Implement MFA
  • Use strong password policies
  • Secure password storage
  • Rate limiting
  • Account lockout
  • Secure session management

Authorization:

  • Principle of least privilege
  • Role-based access control
  • Regular access reviews
  • Audit logging
  • Token validation
  • Secure token storage

Implementation:

  • HTTPS everywhere
  • Secure cookie settings
  • CSRF protection
  • XSS prevention
  • Input validation
  • Error handling