+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 204 of 354

๐Ÿ”’ Authentication: JWT and Sessions

Master authentication: jwt and sessions in TypeScript with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic understanding of JavaScript ๐Ÿ“
  • TypeScript installation โšก
  • VS Code or preferred IDE ๐Ÿ’ป
  • Basic Node.js knowledge ๐ŸŸข
  • HTTP/REST API concepts ๐ŸŒ

What you'll learn

  • Understand JWT and session authentication fundamentals ๐ŸŽฏ
  • Apply JWT authentication in real projects ๐Ÿ—๏ธ
  • Debug common authentication issues ๐Ÿ›
  • Write type-safe authentication code โœจ

๐ŸŽฏ Introduction

Welcome to the exciting world of TypeScript authentication! ๐ŸŽ‰ In this guide, weโ€™ll explore JWT (JSON Web Tokens) and session-based authentication - the two most popular ways to keep your applications secure.

Youโ€™ll discover how authentication can transform your TypeScript backend development experience. Whether youโ€™re building APIs ๐ŸŒ, web applications ๐Ÿ–ฅ๏ธ, or microservices ๐Ÿ“š, understanding authentication is essential for writing secure, maintainable code.

By the end of this tutorial, youโ€™ll feel confident implementing both JWT and session authentication in your own projects! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Authentication

๐Ÿค” What is Authentication?

Authentication is like being a bouncer at a nightclub ๐ŸŽจ. Think of it as checking IDs at the door - you need to verify someone is who they claim to be before letting them into your exclusive app!

In TypeScript terms, authentication is the process of verifying user credentials and managing their access to protected resources โœจ. This means you can:

  • โœจ Secure your API endpoints
  • ๐Ÿš€ Control user access levels
  • ๐Ÿ›ก๏ธ Protect sensitive data

๐Ÿ’ก JWT vs Sessions: The Great Debate

Hereโ€™s why developers have passionate discussions about these approaches:

JWT (JSON Web Tokens) ๐ŸŽซ:

  1. Stateless ๐Ÿ”’: All info stored in the token
  2. Scalable ๐Ÿ’ป: No server-side storage needed
  3. Cross-Domain ๐Ÿ“–: Works across different services
  4. Portable ๐Ÿ”ง: Easy to pass between services

Sessions ๐Ÿ—๏ธ:

  1. Server-Controlled ๐ŸŽฏ: Full control over sessions
  2. Secure by Default ๐Ÿ›ก๏ธ: Harder to tamper with
  3. Easy Revocation โšก: Can instantly invalidate sessions
  4. Simpler Logic ๐Ÿ“: Straightforward to implement

Real-world example: Imagine building a banking app ๐Ÿฆ. With JWT, itโ€™s like giving customers a tamper-proof ID card. With sessions, itโ€™s like keeping a guest list at the front desk.

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Setting Up Our Types

Letโ€™s start with friendly type definitions:

// ๐Ÿ‘‹ Hello, Authentication Types!
interface User {
  id: string;          // ๐Ÿ†” Unique identifier
  email: string;       // ๐Ÿ“ง User's email
  password: string;    // ๐Ÿ”‘ Hashed password
  role: "admin" | "user"; // ๐Ÿ‘ค User role
  createdAt: Date;     // ๐Ÿ“… Registration date
  emoji: string;       // ๐ŸŽญ User's favorite emoji!
}

// ๐ŸŽซ JWT Payload structure
interface JWTPayload {
  userId: string;      // ๐Ÿ‘ค User identifier
  email: string;       // ๐Ÿ“ง User email
  role: string;        // ๐ŸŽฏ User role
  iat: number;         // ๐Ÿ• Issued at
  exp: number;         // โฐ Expires at
}

// ๐Ÿ—๏ธ Session data structure
interface SessionData {
  userId: string;      // ๐Ÿ‘ค User identifier
  email: string;       // ๐Ÿ“ง User email
  role: string;        // ๐ŸŽฏ User role
  loginTime: Date;     // ๐Ÿ• When they logged in
  lastActivity: Date;  // โšก Last activity time
}

๐Ÿ’ก Explanation: Notice how we define clear types for our authentication data! The emoji field adds personality to our users. ๐Ÿ˜Š

๐ŸŽฏ Common Authentication Patterns

Here are patterns youโ€™ll use daily:

// ๐Ÿ—๏ธ Pattern 1: Password hashing
import bcrypt from 'bcrypt';

const hashPassword = async (password: string): Promise<string> => {
  const saltRounds = 10; // ๐Ÿง‚ Salt rounds for security
  return await bcrypt.hash(password, saltRounds);
};

// ๐Ÿ” Pattern 2: Password verification
const verifyPassword = async (
  password: string, 
  hashedPassword: string
): Promise<boolean> => {
  return await bcrypt.compare(password, hashedPassword);
};

// ๐ŸŽจ Pattern 3: Login response type
interface LoginResponse {
  success: boolean;    // โœ… Login success status  
  token?: string;      // ๐ŸŽซ JWT token (if successful)
  user?: User;         // ๐Ÿ‘ค User data (without password!)
  message: string;     // ๐Ÿ“ Response message
  emoji: string;       // ๐ŸŽ‰ Success/error emoji
}

๐Ÿ’ก Practical Examples

๐Ÿ”’ Example 1: JWT Authentication System

Letโ€™s build a complete JWT authentication system:

import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import express, { Request, Response, NextFunction } from 'express';

// ๐ŸŽฏ JWT Authentication Service
class JWTAuthService {
  private readonly JWT_SECRET = process.env.JWT_SECRET || 'your-super-secret-key-๐Ÿ”';
  private readonly JWT_EXPIRES_IN = '24h';
  
  // ๐ŸŽซ Generate JWT token
  generateToken(user: User): string {
    const payload: JWTPayload = {
      userId: user.id,
      email: user.email,
      role: user.role,
      iat: Math.floor(Date.now() / 1000),
      exp: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 24 hours
    };
    
    return jwt.sign(payload, this.JWT_SECRET, { 
      expiresIn: this.JWT_EXPIRES_IN 
    });
  }
  
  // ๐Ÿ” Verify JWT token
  verifyToken(token: string): JWTPayload | null {
    try {
      return jwt.verify(token, this.JWT_SECRET) as JWTPayload;
    } catch (error) {
      console.log('๐Ÿšซ Invalid token:', error);
      return null;
    }
  }
  
  // ๐Ÿ›ก๏ธ Middleware for protecting routes
  authenticate = (req: Request, res: Response, next: NextFunction): void => {
    const authHeader = req.headers.authorization;
    const token = authHeader?.split(' ')[1]; // Bearer TOKEN
    
    if (!token) {
      res.status(401).json({ 
        message: 'Access denied! No token provided ๐Ÿšซ',
        emoji: '๐Ÿ”’'
      });
      return;
    }
    
    const payload = this.verifyToken(token);
    if (!payload) {
      res.status(403).json({ 
        message: 'Invalid token! ๐Ÿšซ',
        emoji: 'โŒ'
      });
      return;
    }
    
    // โœจ Add user info to request
    (req as any).user = payload;
    next();
  };
}

// ๐Ÿ—๏ธ User service for authentication
class UserService {
  private users: Map<string, User> = new Map();
  private authService = new JWTAuthService();
  
  // ๐Ÿ“ Register new user
  async register(email: string, password: string, emoji: string = '๐Ÿ˜Š'): Promise<LoginResponse> {
    // ๐Ÿ” Check if user already exists
    const existingUser = Array.from(this.users.values())
      .find(user => user.email === email);
    
    if (existingUser) {
      return {
        success: false,
        message: 'User already exists! ๐Ÿ™‹โ€โ™‚๏ธ',
        emoji: 'โš ๏ธ'
      };
    }
    
    // ๐Ÿ” Hash password and create user
    const hashedPassword = await bcrypt.hash(password, 10);
    const newUser: User = {
      id: Date.now().toString(),
      email,
      password: hashedPassword,
      role: 'user',
      createdAt: new Date(),
      emoji
    };
    
    this.users.set(newUser.id, newUser);
    const token = this.authService.generateToken(newUser);
    
    return {
      success: true,
      token,
      user: { ...newUser, password: '' }, // ๐Ÿšซ Never send password!
      message: 'Registration successful! Welcome aboard! ๐ŸŽ‰',
      emoji: '๐ŸŽŠ'
    };
  }
  
  // ๐Ÿ”‘ Login user
  async login(email: string, password: string): Promise<LoginResponse> {
    // ๐Ÿ” Find user by email
    const user = Array.from(this.users.values())
      .find(user => user.email === email);
    
    if (!user) {
      return {
        success: false,
        message: 'User not found! ๐Ÿ”',
        emoji: '๐Ÿ˜•'
      };
    }
    
    // ๐Ÿ” Verify password
    const isValidPassword = await bcrypt.compare(password, user.password);
    if (!isValidPassword) {
      return {
        success: false,
        message: 'Invalid password! ๐Ÿšซ',
        emoji: '๐Ÿ”'
      };
    }
    
    // โœ… Generate token and return success
    const token = this.authService.generateToken(user);
    return {
      success: true,
      token,
      user: { ...user, password: '' }, // ๐Ÿšซ Clean password
      message: `Welcome back, ${user.emoji}! ๐Ÿ‘‹`,
      emoji: '๐ŸŽ‰'
    };
  }
}

๐ŸŽฏ Try it yourself: Add a logout functionality that blacklists tokens!

๐Ÿ—๏ธ Example 2: Session-Based Authentication

Letโ€™s implement session authentication:

import session from 'express-session';
import connectRedis from 'connect-redis';
import redis from 'redis';

// ๐Ÿ—ƒ๏ธ Session store configuration
const redisClient = redis.createClient({
  host: 'localhost',
  port: 6379
});

const RedisStore = connectRedis(session);

// ๐ŸŽฎ Session authentication service
class SessionAuthService {
  private users: Map<string, User> = new Map();
  
  // โš™๏ธ Session middleware configuration
  getSessionMiddleware() {
    return session({
      store: new RedisStore({
        client: redisClient,
        prefix: 'myapp:sess:' // ๐Ÿท๏ธ Session prefix
      }),
      secret: process.env.SESSION_SECRET || 'your-session-secret-๐Ÿ—๏ธ',
      resave: false,
      saveUninitialized: false,
      rolling: true, // ๐Ÿ”„ Reset expiry on activity
      cookie: {
        secure: process.env.NODE_ENV === 'production', // ๐Ÿ”’ HTTPS only in prod
        httpOnly: true, // ๐Ÿ›ก๏ธ Prevent XSS
        maxAge: 24 * 60 * 60 * 1000, // ๐Ÿ“… 24 hours
        sameSite: 'strict' // ๐Ÿ›ก๏ธ CSRF protection
      }
    });
  }
  
  // ๐Ÿ”‘ Login with session
  async loginWithSession(
    req: express.Request, 
    email: string, 
    password: string
  ): Promise<LoginResponse> {
    // ๐Ÿ” Find and verify user (similar to JWT example)
    const user = Array.from(this.users.values())
      .find(user => user.email === email);
    
    if (!user || !await bcrypt.compare(password, user.password)) {
      return {
        success: false,
        message: 'Invalid credentials! ๐Ÿšซ',
        emoji: 'โŒ'
      };
    }
    
    // โœจ Create session data
    const sessionData: SessionData = {
      userId: user.id,
      email: user.email,
      role: user.role,
      loginTime: new Date(),
      lastActivity: new Date()
    };
    
    // ๐Ÿ—๏ธ Store in session
    (req.session as any).user = sessionData;
    
    return {
      success: true,
      user: { ...user, password: '' },
      message: `Welcome back! ${user.emoji} ๐ŸŽ‰`,
      emoji: '๐ŸŽŠ'
    };
  }
  
  // ๐Ÿ›ก๏ธ Session authentication middleware
  requireAuth = (req: express.Request, res: express.Response, next: express.NextFunction): void => {
    const sessionUser = (req.session as any).user as SessionData;
    
    if (!sessionUser) {
      res.status(401).json({
        message: 'Please log in first! ๐Ÿ”‘',
        emoji: '๐Ÿšช'
      });
      return;
    }
    
    // ๐Ÿ”„ Update last activity
    sessionUser.lastActivity = new Date();
    (req.session as any).user = sessionUser;
    
    // โœจ Add user to request
    (req as any).user = sessionUser;
    next();
  };
  
  // ๐Ÿ‘‹ Logout (destroy session)
  logout(req: express.Request): Promise<boolean> {
    return new Promise((resolve) => {
      req.session.destroy((err) => {
        if (err) {
          console.log('๐Ÿšซ Session destroy error:', err);
          resolve(false);
        } else {
          console.log('๐Ÿ‘‹ Session destroyed successfully!');
          resolve(true);
        }
      });
    });
  }
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Refresh Token System

When youโ€™re ready to level up, implement refresh tokens:

// ๐ŸŽฏ Advanced token system with refresh
interface TokenPair {
  accessToken: string;    // ๐ŸŽซ Short-lived (15 mins)
  refreshToken: string;   // ๐Ÿ”„ Long-lived (7 days)
  expiresIn: number;      // โฐ Access token expiry
  tokenType: "Bearer";    // ๐Ÿท๏ธ Token type
}

class AdvancedJWTService {
  private readonly ACCESS_TOKEN_EXPIRY = '15m';
  private readonly REFRESH_TOKEN_EXPIRY = '7d';
  private refreshTokens: Set<string> = new Set(); // ๐Ÿ—ƒ๏ธ Store valid refresh tokens
  
  // ๐ŸŽซ Generate token pair
  generateTokenPair(user: User): TokenPair {
    const accessToken = jwt.sign(
      { userId: user.id, email: user.email, role: user.role },
      process.env.JWT_SECRET!,
      { expiresIn: this.ACCESS_TOKEN_EXPIRY }
    );
    
    const refreshToken = jwt.sign(
      { userId: user.id, type: 'refresh' },
      process.env.REFRESH_SECRET!,
      { expiresIn: this.REFRESH_TOKEN_EXPIRY }
    );
    
    // ๐Ÿ—ƒ๏ธ Store refresh token
    this.refreshTokens.add(refreshToken);
    
    return {
      accessToken,
      refreshToken,
      expiresIn: 15 * 60, // 15 minutes in seconds
      tokenType: "Bearer"
    };
  }
  
  // ๐Ÿ”„ Refresh access token
  refreshAccessToken(refreshToken: string): TokenPair | null {
    try {
      // ๐Ÿ” Verify refresh token
      const payload = jwt.verify(refreshToken, process.env.REFRESH_SECRET!) as any;
      
      if (!this.refreshTokens.has(refreshToken) || payload.type !== 'refresh') {
        return null;
      }
      
      // ๐Ÿ—‘๏ธ Remove old refresh token
      this.refreshTokens.delete(refreshToken);
      
      // ๐ŸŽซ Generate new token pair
      // (You'd fetch user from database using payload.userId)
      const user = this.getUserById(payload.userId);
      return user ? this.generateTokenPair(user) : null;
      
    } catch (error) {
      console.log('๐Ÿšซ Refresh token error:', error);
      return null;
    }
  }
  
  // ๐Ÿ—‘๏ธ Revoke refresh token
  revokeRefreshToken(refreshToken: string): boolean {
    return this.refreshTokens.delete(refreshToken);
  }
}

๐Ÿ—๏ธ Advanced Topic 2: Role-Based Access Control (RBAC)

For enterprise-level security:

// ๐Ÿš€ Role-based access control system
type Permission = 'read' | 'write' | 'delete' | 'admin';
type Resource = 'users' | 'posts' | 'comments' | 'settings';

interface Role {
  name: string;
  permissions: Map<Resource, Permission[]>;
  emoji: string; // ๐ŸŽญ Role emoji!
}

class RBACService {
  private roles: Map<string, Role> = new Map([
    ['admin', {
      name: 'admin',
      permissions: new Map([
        ['users', ['read', 'write', 'delete', 'admin']],
        ['posts', ['read', 'write', 'delete', 'admin']],
        ['comments', ['read', 'write', 'delete', 'admin']],
        ['settings', ['read', 'write', 'delete', 'admin']]
      ]),
      emoji: '๐Ÿ‘‘'
    }],
    ['user', {
      name: 'user',
      permissions: new Map([
        ['posts', ['read', 'write']],
        ['comments', ['read', 'write']]
      ]),
      emoji: '๐Ÿ‘ค'
    }]
  ]);
  
  // ๐Ÿ” Check if user has permission
  hasPermission(userRole: string, resource: Resource, permission: Permission): boolean {
    const role = this.roles.get(userRole);
    if (!role) return false;
    
    const resourcePermissions = role.permissions.get(resource);
    return resourcePermissions?.includes(permission) || false;
  }
  
  // ๐Ÿ›ก๏ธ Permission middleware factory
  requirePermission = (resource: Resource, permission: Permission) => {
    return (req: express.Request, res: express.Response, next: express.NextFunction) => {
      const user = (req as any).user;
      
      if (!user || !this.hasPermission(user.role, resource, permission)) {
        res.status(403).json({
          message: `Access denied! Need ${permission} permission for ${resource} ๐Ÿšซ`,
          emoji: '๐Ÿ›ก๏ธ'
        });
        return;
      }
      
      next();
    };
  };
}

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Storing Passwords in Plain Text

// โŒ Wrong way - NEVER do this!
const badUser = {
  email: "[email protected]",
  password: "mysecretpassword123" // ๐Ÿ’ฅ Huge security risk!
};

// โœ… Correct way - always hash passwords!
import bcrypt from 'bcrypt';

const goodUser = {
  email: "[email protected]",
  password: await bcrypt.hash("mysecretpassword123", 10) // ๐Ÿ›ก๏ธ Safe and secure!
};

๐Ÿคฏ Pitfall 2: Sending Passwords in Responses

// โŒ Dangerous - exposing password!
const loginResponse = {
  success: true,
  user: {
    id: "123",
    email: "[email protected]", 
    password: "hashedpassword" // ๐Ÿ’ฅ Never send this!
  }
};

// โœ… Safe - clean user data!
const safeLoginResponse = {
  success: true,
  user: {
    id: "123",
    email: "[email protected]"
    // ๐Ÿšซ Password intentionally omitted!
  },
  message: "Login successful! ๐ŸŽ‰"
};

๐Ÿ” Pitfall 3: Weak JWT Secrets

// โŒ Terrible - easily guessable!
const weakSecret = "secret"; // ๐Ÿ’ฅ Hackers will break this instantly!

// โœ… Strong - cryptographically secure!
const strongSecret = process.env.JWT_SECRET || crypto.randomBytes(64).toString('hex');
// ๐Ÿ›ก๏ธ Use environment variables for production!

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Strong Secrets: Generate cryptographically strong secrets for JWT and sessions
  2. ๐Ÿ“ Hash Passwords: Always use bcrypt or similar for password hashing
  3. ๐Ÿ›ก๏ธ Validate Input: Sanitize and validate all user inputs
  4. ๐ŸŽจ Clean Responses: Never send passwords or sensitive data in API responses
  5. โœจ Use HTTPS: Always use HTTPS in production for secure token transmission
  6. โฐ Short-lived Tokens: Keep access tokens short-lived (15-30 minutes)
  7. ๐Ÿ”„ Implement Refresh: Use refresh tokens for seamless user experience
  8. ๐Ÿšซ Logout Properly: Always provide secure logout functionality

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Complete Authentication System

Create a type-safe authentication system with both JWT and session support:

๐Ÿ“‹ Requirements:

  • โœ… User registration and login endpoints
  • ๐Ÿท๏ธ Both JWT and session authentication
  • ๐Ÿ‘ค Role-based access control
  • ๐Ÿ“… Password reset functionality
  • ๐ŸŽจ Each user needs a favorite emoji!
  • ๐Ÿ”„ Refresh token implementation

๐Ÿš€ Bonus Points:

  • Add password strength validation
  • Implement account lockout after failed attempts
  • Create audit logging for security events
  • Add two-factor authentication (2FA)

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Complete authentication system!
import express from 'express';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import rateLimit from 'express-rate-limit';

interface CompleteUser {
  id: string;
  email: string;
  password: string;
  role: 'admin' | 'user';
  emoji: string;
  isLocked: boolean;
  failedLoginAttempts: number;
  lastLoginAttempt?: Date;
  createdAt: Date;
  lastLogin?: Date;
}

class CompleteAuthSystem {
  private users: Map<string, CompleteUser> = new Map();
  private refreshTokens: Set<string> = new Set();
  private readonly MAX_FAILED_ATTEMPTS = 5;
  private readonly LOCKOUT_DURATION = 15 * 60 * 1000; // 15 minutes

  // ๐Ÿ›ก๏ธ Rate limiting middleware
  private loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 5, // Max 5 attempts per window
    message: {
      error: 'Too many login attempts! Try again later ๐Ÿšซ',
      emoji: 'โฐ'
    }
  });

  // ๐Ÿ“ Register with validation
  async register(
    email: string, 
    password: string, 
    emoji: string = '๐Ÿ˜Š'
  ): Promise<{ success: boolean; message: string; user?: any; token?: string }> {
    
    // ๐Ÿ” Email validation
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
      return {
        success: false,
        message: 'Invalid email format! ๐Ÿ“ง'
      };
    }

    // ๐Ÿ” Password strength validation
    if (password.length < 8) {
      return {
        success: false,
        message: 'Password must be at least 8 characters long! ๐Ÿ”’'
      };
    }

    // ๐Ÿ” Check if user exists
    const existingUser = Array.from(this.users.values())
      .find(user => user.email === email);
    
    if (existingUser) {
      return {
        success: false,
        message: 'Email already registered! ๐Ÿ™‹โ€โ™‚๏ธ'
      };
    }

    // ๐Ÿ—๏ธ Create user
    const hashedPassword = await bcrypt.hash(password, 12);
    const newUser: CompleteUser = {
      id: Date.now().toString(),
      email,
      password: hashedPassword,
      role: 'user',
      emoji,
      isLocked: false,
      failedLoginAttempts: 0,
      createdAt: new Date()
    };

    this.users.set(newUser.id, newUser);

    // ๐ŸŽซ Generate tokens
    const tokens = this.generateTokenPair(newUser);

    return {
      success: true,
      message: `Welcome aboard! ${emoji} ๐ŸŽ‰`,
      user: this.sanitizeUser(newUser),
      token: tokens.accessToken
    };
  }

  // ๐Ÿ”‘ Enhanced login with security features
  async login(
    email: string, 
    password: string
  ): Promise<{ success: boolean; message: string; user?: any; tokens?: any }> {
    
    const user = Array.from(this.users.values())
      .find(u => u.email === email);

    if (!user) {
      return {
        success: false,
        message: 'Invalid credentials! ๐Ÿšซ'
      };
    }

    // ๐Ÿ”’ Check if account is locked
    if (this.isAccountLocked(user)) {
      return {
        success: false,
        message: 'Account temporarily locked! Try again later ๐Ÿ”’'
      };
    }

    // ๐Ÿ” Verify password
    const isValidPassword = await bcrypt.compare(password, user.password);
    
    if (!isValidPassword) {
      // ๐Ÿ“ˆ Increment failed attempts
      user.failedLoginAttempts++;
      user.lastLoginAttempt = new Date();
      
      if (user.failedLoginAttempts >= this.MAX_FAILED_ATTEMPTS) {
        user.isLocked = true;
      }

      return {
        success: false,
        message: `Invalid password! ${this.MAX_FAILED_ATTEMPTS - user.failedLoginAttempts} attempts remaining ๐Ÿšซ`
      };
    }

    // โœ… Successful login - reset counters
    user.failedLoginAttempts = 0;
    user.isLocked = false;
    user.lastLogin = new Date();

    const tokens = this.generateTokenPair(user);

    return {
      success: true,
      message: `Welcome back! ${user.emoji} ๐Ÿ‘‹`,
      user: this.sanitizeUser(user),
      tokens
    };
  }

  // ๐Ÿ”„ Token pair generation
  private generateTokenPair(user: CompleteUser) {
    const accessToken = jwt.sign(
      { 
        userId: user.id, 
        email: user.email, 
        role: user.role,
        emoji: user.emoji 
      },
      process.env.JWT_SECRET || 'fallback-secret',
      { expiresIn: '15m' }
    );

    const refreshToken = jwt.sign(
      { userId: user.id, type: 'refresh' },
      process.env.REFRESH_SECRET || 'refresh-secret',
      { expiresIn: '7d' }
    );

    this.refreshTokens.add(refreshToken);

    return {
      accessToken,
      refreshToken,
      expiresIn: 15 * 60, // 15 minutes
      tokenType: 'Bearer'
    };
  }

  // ๐Ÿ”’ Check if account is locked
  private isAccountLocked(user: CompleteUser): boolean {
    if (!user.isLocked || !user.lastLoginAttempt) return false;
    
    const timeSinceLock = Date.now() - user.lastLoginAttempt.getTime();
    if (timeSinceLock > this.LOCKOUT_DURATION) {
      // ๐Ÿ”“ Auto-unlock after lockout duration
      user.isLocked = false;
      user.failedLoginAttempts = 0;
      return false;
    }
    
    return true;
  }

  // ๐Ÿงน Remove sensitive data
  private sanitizeUser(user: CompleteUser) {
    const { password, failedLoginAttempts, isLocked, ...safeUser } = user;
    return safeUser;
  }

  // ๐Ÿ›ก๏ธ Authentication middleware
  authenticate = (req: express.Request, res: express.Response, next: express.NextFunction) => {
    const authHeader = req.headers.authorization;
    const token = authHeader?.split(' ')[1];

    if (!token) {
      res.status(401).json({
        message: 'Access token required! ๐ŸŽซ',
        emoji: '๐Ÿšช'
      });
      return;
    }

    try {
      const payload = jwt.verify(token, process.env.JWT_SECRET || 'fallback-secret') as any;
      (req as any).user = payload;
      next();
    } catch (error) {
      res.status(403).json({
        message: 'Invalid or expired token! ๐Ÿšซ',
        emoji: 'โฐ'
      });
    }
  };
}

// ๐ŸŽฎ Usage example
const authSystem = new CompleteAuthSystem();
const app = express();

app.use(express.json());

// ๐Ÿ“ Registration endpoint
app.post('/register', async (req, res) => {
  const { email, password, emoji } = req.body;
  const result = await authSystem.register(email, password, emoji);
  
  res.status(result.success ? 201 : 400).json(result);
});

// ๐Ÿ”‘ Login endpoint
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const result = await authSystem.login(email, password);
  
  res.status(result.success ? 200 : 401).json(result);
});

// ๐Ÿ›ก๏ธ Protected route example
app.get('/profile', authSystem.authenticate, (req, res) => {
  const user = (req as any).user;
  res.json({
    message: `Hello ${user.emoji}! Here's your profile ๐Ÿ‘‹`,
    user: user
  });
});

console.log('๐Ÿš€ Authentication server ready on port 3000!');

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Implement JWT authentication with confidence ๐Ÿ’ช
  • โœ… Create session-based auth that scales ๐Ÿš€
  • โœ… Avoid security pitfalls that trip up beginners ๐Ÿ›ก๏ธ
  • โœ… Apply RBAC patterns for enterprise apps ๐ŸŽฏ
  • โœ… Debug authentication issues like a pro ๐Ÿ›
  • โœ… Build secure, type-safe backends with TypeScript! โœจ

Remember: Security is not a feature, itโ€™s a foundation! Build it right from the start. ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered authentication fundamentals!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the exercises above
  2. ๐Ÿ—๏ธ Build a complete app with authentication
  3. ๐Ÿ“š Move on to our next tutorial: Authorization & Middleware
  4. ๐ŸŒŸ Share your secure apps with the world!
  5. ๐Ÿ” Explore OAuth2 and social login integrations

Remember: Every security expert was once a beginner. Keep coding, keep learning, and most importantly, keep your users safe! ๐Ÿš€


Happy coding! ๐ŸŽ‰๐Ÿ”’โœจ