Authentication & Authorization - 1/2

From Security Foundations to Identity Management

You’ve mastered input validation that stops injection attacks, implemented cryptographic security with bulletproof password hashing, and built operational defenses that protect against DDoS attacks and compliance violations. Your applications are secure at every layer—input, cryptographic, and operational. But here’s the identity reality that determines whether users can actually access your secure application: even with perfect security foundations, your application is worthless if users can’t prove who they are or if the system can’t determine what they’re allowed to do.

The authentication nightmare that locks out legitimate users:

// Your perfectly secured application with bulletproof defenses
app.post(
  "/api/dashboard",
  inputValidation, // Perfect input validation
  rateLimiting, // DDoS protection
  csrfProtection, // CSRF tokens
  async (req, res) => {
    try {
      // But who is this user? How do we know they should access this?
      const userDashboard = await getDashboardData(req.user?.id);
      res.json({ dashboard: userDashboard });

      // Problems without proper authentication/authorization:
      // - req.user is undefined - no authentication system
      // - No way to verify user identity
      // - No control over who accesses what resources
      // - Every endpoint requires custom identity checking
      // - Session management is non-existent
      // - Password resets are impossible
      // - Social login is a pipe dream
    } catch (error) {
      res.status(500).json({ error: "Dashboard failed" });
    }
  }
);

// User experience without proper auth:
// - "Why do I have to re-enter my password every page refresh?"
// - "I can't log in with my Google account like everywhere else"
// - "The app forgot who I was after closing my browser"
// - "I can see admin features but I'm just a regular user"
// - "There's no way to reset my password"

The uncomfortable identity truth: Security without identity is like building a fortress with no doors—perfectly secure but completely inaccessible. Modern applications require sophisticated identity systems that seamlessly authenticate users, maintain sessions across devices, integrate with third-party providers, and enforce granular permissions without frustrating legitimate users.

Real-world authentication failure consequences:

// What happens when authentication systems fail:
const authenticationFailureImpact = {
  userExperience: {
    problem: "Users can't log in during Black Friday sale",
    impact: "$2.8M in lost revenue in 4 hours",
    cause: "Authentication service overwhelmed, no fallback",
    solution: "Required complete system rebuild",
  },

  securityBreach: {
    problem: "Weak JWT implementation allows token manipulation",
    impact: "Users can access any account by modifying token claims",
    scope: "450,000 user accounts compromised",
    compliance: "GDPR fine of $3.2M for inadequate access controls",
  },

  operationalChaos: {
    problem: "No role-based access control",
    impact: "Junior developer accidentally deletes production database",
    cause: "Everyone has admin access, no permission boundaries",
    recovery: "6 days downtime, 72 hours from backups",
  },

  // Perfect security means nothing if users can't securely access your app
  // or if access controls don't prevent unauthorized actions
};

Authentication and authorization mastery requires understanding:

  • Authentication vs. authorization fundamentals and how they work together to secure applications
  • Session-based authentication with secure session management and cross-device persistence
  • JWT token-based authentication with proper security considerations and token lifecycle management
  • OAuth 2.0 and OpenID Connect for third-party authentication and social login integration
  • Multi-factor authentication (MFA) that balances security with user experience

This article transforms you from security defender to identity architect. You’ll build authentication systems that users love, authorization frameworks that scale with your organization, and identity integrations that work seamlessly across platforms and devices.


Authentication vs. Authorization: The Foundation of Identity

Understanding the Critical Distinction

The identity concepts that developers confuse:

// ❌ Common confusion between authentication and authorization
const handleUserRequest = async (req, res) => {
  // This is NOT authentication or authorization - it's just checking if data exists
  const user = await db.users.findOne({ email: req.body.email });
  if (user) {
    // Dangerous assumption: existence = authentication = authorization
    const adminData = await getAdminData(); // No verification of identity or permissions
    res.json({ user, adminData });
  }

  // Problems with this approach:
  // - No verification that the person making the request IS this user
  // - No check that this user SHOULD access admin data
  // - Anyone who knows an email address gets full access
  // - No session management or token validation
  // - No permission boundaries or role checking
};

// The confusion that destroys security:
// Authentication ≠ "Does this user exist in our database?"
// Authorization ≠ "This user exists so they can do anything"

Professional authentication and authorization separation:

// ✅ Clear separation of authentication and authorization concerns
class IdentityManager {
  constructor() {
    this.sessionStore = new SessionStore();
    this.permissionEngine = new PermissionEngine();
    this.auditLogger = new AuditLogger();
  }

  // AUTHENTICATION: Verify WHO the user is
  async authenticate(credentials, authMethod = "password") {
    try {
      let authResult = null;

      switch (authMethod) {
        case "password":
          authResult = await this.authenticateWithPassword(credentials);
          break;
        case "token":
          authResult = await this.authenticateWithToken(credentials);
          break;
        case "oauth":
          authResult = await this.authenticateWithOAuth(credentials);
          break;
        case "mfa":
          authResult = await this.authenticateWithMFA(credentials);
          break;
        default:
          throw new AuthenticationError("Unsupported authentication method");
      }

      if (!authResult.success) {
        await this.logAuthenticationFailure(credentials, authMethod);
        throw new AuthenticationError(authResult.reason);
      }

      // Authentication successful - we now know WHO the user is
      const authenticatedUser = {
        id: authResult.user.id,
        email: authResult.user.email,
        username: authResult.user.username,
        authenticatedAt: new Date(),
        authMethod,
        sessionId: authResult.sessionId,
      };

      await this.logAuthenticationSuccess(authenticatedUser);

      return authenticatedUser;
    } catch (error) {
      console.error("Authentication failed:", error);
      throw error;
    }
  }

  // AUTHORIZATION: Verify WHAT the authenticated user can do
  async authorize(authenticatedUser, resource, action, context = {}) {
    try {
      // First verify the user is still authenticated
      const isValidSession = await this.validateSession(
        authenticatedUser.sessionId
      );
      if (!isValidSession) {
        throw new AuthorizationError(
          "Session expired - reauthentication required"
        );
      }

      // Get user's current permissions (may have changed since authentication)
      const userPermissions = await this.getUserPermissions(
        authenticatedUser.id
      );

      // Check if user has permission for this specific resource and action
      const hasPermission = await this.permissionEngine.checkPermission({
        userId: authenticatedUser.id,
        resource,
        action,
        permissions: userPermissions,
        context,
      });

      if (!hasPermission.granted) {
        await this.logAuthorizationFailure({
          userId: authenticatedUser.id,
          resource,
          action,
          reason: hasPermission.reason,
          context,
        });

        throw new AuthorizationError(`Access denied: ${hasPermission.reason}`);
      }

      // Authorization successful - user can perform this action
      const authorizationResult = {
        userId: authenticatedUser.id,
        resource,
        action,
        granted: true,
        grantedAt: new Date(),
        permissions: hasPermission.matchedPermissions,
        context,
      };

      await this.logAuthorizationSuccess(authorizationResult);

      return authorizationResult;
    } catch (error) {
      console.error("Authorization failed:", error);
      throw error;
    }
  }

  // Combined middleware for routes that need both
  requireAuth(resource, action) {
    return async (req, res, next) => {
      try {
        // Step 1: Authenticate - determine WHO the user is
        const authenticatedUser = await this.extractAuthenticatedUser(req);
        if (!authenticatedUser) {
          return res.status(401).json({
            error: "Authentication required",
            message: "You must be logged in to access this resource",
          });
        }

        // Step 2: Authorize - determine WHAT they can do
        const authorizationResult = await this.authorize(
          authenticatedUser,
          resource,
          action,
          {
            ip: req.ip,
            userAgent: req.get("User-Agent"),
            path: req.path,
            method: req.method,
          }
        );

        // Attach identity information to request
        req.user = authenticatedUser;
        req.permissions = authorizationResult.permissions;
        req.authContext = authorizationResult;

        next();
      } catch (error) {
        if (error instanceof AuthenticationError) {
          return res.status(401).json({
            error: "Authentication failed",
            message: error.message,
          });
        }

        if (error instanceof AuthorizationError) {
          return res.status(403).json({
            error: "Access denied",
            message: error.message,
          });
        }

        console.error("Auth middleware failed:", error);
        res.status(500).json({
          error: "Authentication system error",
          message: "Unable to verify your identity",
        });
      }
    };
  }

  async extractAuthenticatedUser(req) {
    // Try multiple authentication methods

    // Method 1: Session-based authentication
    const sessionId = req.cookies?.sessionId;
    if (sessionId) {
      const sessionData = await this.sessionStore.getSession(sessionId);
      if (sessionData && !sessionData.expired) {
        return sessionData.user;
      }
    }

    // Method 2: JWT token authentication
    const authHeader = req.headers.authorization;
    if (authHeader && authHeader.startsWith("Bearer ")) {
      const token = authHeader.slice(7);
      const tokenData = await this.validateJWTToken(token);
      if (tokenData) {
        return tokenData.user;
      }
    }

    // Method 3: API key authentication
    const apiKey = req.headers["x-api-key"];
    if (apiKey) {
      const apiKeyData = await this.validateApiKey(apiKey);
      if (apiKeyData) {
        return apiKeyData.user;
      }
    }

    return null; // No valid authentication found
  }
}

// Real-world usage with clear separation
const identityManager = new IdentityManager();

// Public endpoint - no authentication or authorization required
app.get("/api/public/status", (req, res) => {
  res.json({ status: "operational", timestamp: new Date() });
});

// Authentication required, no specific authorization
app.get(
  "/api/user/profile",
  identityManager.requireAuth("user:profile", "read"),
  async (req, res) => {
    // We know WHO the user is (req.user)
    // We know they can READ their profile (authorized)
    const profile = await getUserProfile(req.user.id);
    res.json({ profile });
  }
);

// Both authentication and specific authorization required
app.delete(
  "/api/admin/users/:userId",
  identityManager.requireAuth("admin:users", "delete"),
  async (req, res) => {
    // We know WHO is making the request (req.user)
    // We know they have DELETE permission on admin:users (authorized)
    await deleteUser(req.params.userId, req.user.id);
    res.json({ success: true });
  }
);

// Resource-specific authorization
app.get(
  "/api/projects/:projectId",
  identityManager.requireAuth("project", "read"),
  async (req, res) => {
    // Authorization engine checks if THIS user can read THIS specific project
    const project = await getProject(req.params.projectId);
    res.json({ project });
  }
);

Session-Based Authentication: Traditional but Robust

Secure Session Management Implementation

The session challenge that scales poorly:

// ❌ Naive session implementation that breaks in production
const sessions = new Map(); // In-memory storage - loses data on restart

app.post("/login", async (req, res) => {
  const { email, password } = req.body;

  const user = await validateCredentials(email, password);
  if (user) {
    const sessionId = Math.random().toString(36); // Weak session ID
    sessions.set(sessionId, { userId: user.id }); // No expiration

    res.cookie("sessionId", sessionId); // No security flags
    res.json({ success: true });
  }
});

// Problems with this approach:
// - Sessions lost on server restart
// - Weak session ID generation
// - No session expiration or cleanup
// - No CSRF protection
// - No secure cookie configuration
// - Doesn't scale across multiple servers
// - No session invalidation on logout

Professional session-based authentication:

// ✅ Production-ready session management system
const Redis = require("redis");
const crypto = require("crypto");

class SecureSessionManager {
  constructor() {
    this.redis = Redis.createClient(process.env.REDIS_URL);
    this.sessionPrefix = "session:";
    this.userSessionsPrefix = "user_sessions:";
    this.sessionTimeout = 24 * 60 * 60 * 1000; // 24 hours
    this.slidingSessionWindow = 2 * 60 * 60 * 1000; // 2 hours sliding window

    this.setupSessionCleanup();
  }

  // Generate cryptographically secure session ID
  generateSessionId() {
    return crypto.randomBytes(32).toString("hex");
  }

  async createSession(userId, metadata = {}) {
    try {
      const sessionId = this.generateSessionId();
      const expiresAt = new Date(Date.now() + this.sessionTimeout);

      const sessionData = {
        userId,
        sessionId,
        createdAt: new Date(),
        expiresAt,
        lastActivityAt: new Date(),
        ipAddress: metadata.ipAddress,
        userAgent: metadata.userAgent,
        deviceFingerprint: metadata.deviceFingerprint,
        isActive: true,
        loginMethod: metadata.loginMethod || "password",
        mfaVerified: metadata.mfaVerified || false,
      };

      // Store session data
      await this.redis.setex(
        `${this.sessionPrefix}${sessionId}`,
        Math.floor(this.sessionTimeout / 1000),
        JSON.stringify(sessionData)
      );

      // Track user's active sessions (for security monitoring)
      await this.addToUserSessions(userId, sessionId);

      // Log session creation
      this.logSessionEvent({
        type: "session_created",
        sessionId,
        userId,
        ...metadata,
      });

      return {
        sessionId,
        expiresAt,
        user: await this.getUserData(userId),
      };
    } catch (error) {
      console.error("Session creation failed:", error);
      throw new Error("Failed to create session");
    }
  }

  async validateSession(sessionId) {
    try {
      if (!sessionId) return null;

      const sessionData = await this.redis.get(
        `${this.sessionPrefix}${sessionId}`
      );
      if (!sessionData) return null;

      const session = JSON.parse(sessionData);

      // Check if session is expired
      if (new Date() > new Date(session.expiresAt)) {
        await this.destroySession(sessionId);
        return null;
      }

      // Check if session needs activity refresh (sliding window)
      const timeSinceLastActivity =
        Date.now() - new Date(session.lastActivityAt).getTime();
      if (timeSinceLastActivity > this.slidingSessionWindow) {
        // Update last activity and extend expiration
        session.lastActivityAt = new Date();
        session.expiresAt = new Date(Date.now() + this.sessionTimeout);

        await this.redis.setex(
          `${this.sessionPrefix}${sessionId}`,
          Math.floor(this.sessionTimeout / 1000),
          JSON.stringify(session)
        );
      }

      return session;
    } catch (error) {
      console.error("Session validation failed:", error);
      return null;
    }
  }

  async destroySession(sessionId) {
    try {
      // Get session data before destroying
      const sessionData = await this.redis.get(
        `${this.sessionPrefix}${sessionId}`
      );
      if (sessionData) {
        const session = JSON.parse(sessionData);

        // Remove from user's session list
        await this.removeFromUserSessions(session.userId, sessionId);

        // Log session destruction
        this.logSessionEvent({
          type: "session_destroyed",
          sessionId,
          userId: session.userId,
          reason: "explicit_logout",
        });
      }

      // Delete session data
      await this.redis.del(`${this.sessionPrefix}${sessionId}`);

      return true;
    } catch (error) {
      console.error("Session destruction failed:", error);
      return false;
    }
  }

  async destroyAllUserSessions(userId, exceptSessionId = null) {
    try {
      const userSessionsKey = `${this.userSessionsPrefix}${userId}`;
      const sessionIds = await this.redis.smembers(userSessionsKey);

      const destroyPromises = sessionIds
        .filter((sessionId) => sessionId !== exceptSessionId)
        .map((sessionId) => this.destroySession(sessionId));

      await Promise.all(destroyPromises);

      this.logSessionEvent({
        type: "all_sessions_destroyed",
        userId,
        sessionCount: sessionIds.length,
        exceptSessionId,
      });

      return sessionIds.length;
    } catch (error) {
      console.error("Failed to destroy all user sessions:", error);
      throw error;
    }
  }

  async addToUserSessions(userId, sessionId) {
    const userSessionsKey = `${this.userSessionsPrefix}${userId}`;
    await this.redis.sadd(userSessionsKey, sessionId);

    // Set expiration on user sessions list
    await this.redis.expire(
      userSessionsKey,
      Math.floor(this.sessionTimeout / 1000)
    );
  }

  async removeFromUserSessions(userId, sessionId) {
    const userSessionsKey = `${this.userSessionsPrefix}${userId}`;
    await this.redis.srem(userSessionsKey, sessionId);
  }

  async getUserActiveSessions(userId) {
    try {
      const userSessionsKey = `${this.userSessionsPrefix}${userId}`;
      const sessionIds = await this.redis.smembers(userSessionsKey);

      const sessionPromises = sessionIds.map(async (sessionId) => {
        const sessionData = await this.redis.get(
          `${this.sessionPrefix}${sessionId}`
        );
        return sessionData ? JSON.parse(sessionData) : null;
      });

      const sessions = await Promise.all(sessionPromises);

      // Filter out null sessions (expired/invalid)
      return sessions.filter((session) => session !== null);
    } catch (error) {
      console.error("Failed to get user active sessions:", error);
      return [];
    }
  }

  setupSessionCleanup() {
    // Clean up expired sessions every hour
    setInterval(async () => {
      await this.cleanupExpiredSessions();
    }, 60 * 60 * 1000);
  }

  async cleanupExpiredSessions() {
    try {
      console.log("Starting expired session cleanup");

      // This would be more efficient with Redis SCAN in production
      const sessionKeys = await this.redis.keys(`${this.sessionPrefix}*`);
      let cleanedCount = 0;

      for (const key of sessionKeys) {
        const sessionData = await this.redis.get(key);
        if (sessionData) {
          const session = JSON.parse(sessionData);
          if (new Date() > new Date(session.expiresAt)) {
            await this.redis.del(key);
            cleanedCount++;
          }
        }
      }

      console.log(`Cleaned up ${cleanedCount} expired sessions`);
    } catch (error) {
      console.error("Session cleanup failed:", error);
    }
  }

  // Security monitoring
  async detectSuspiciousActivity(userId, newSessionMetadata) {
    try {
      const activeSessions = await this.getUserActiveSessions(userId);

      const suspiciousSignals = [];

      // Check for sessions from multiple countries
      const uniqueCountries = new Set();
      activeSessions.forEach((session) => {
        if (session.country) uniqueCountries.add(session.country);
      });

      if (uniqueCountries.size > 2) {
        suspiciousSignals.push("multiple_countries");
      }

      // Check for unusual device fingerprints
      const uniqueDevices = new Set();
      activeSessions.forEach((session) => {
        if (session.deviceFingerprint)
          uniqueDevices.add(session.deviceFingerprint);
      });

      if (uniqueDevices.size > 5) {
        suspiciousSignals.push("multiple_devices");
      }

      // Check for concurrent sessions from different IPs
      const recentSessions = activeSessions.filter(
        (session) =>
          Date.now() - new Date(session.lastActivityAt).getTime() <
          5 * 60 * 1000 // 5 minutes
      );

      const recentIPs = new Set(recentSessions.map((s) => s.ipAddress));
      if (recentIPs.size > 3) {
        suspiciousSignals.push("concurrent_different_ips");
      }

      if (suspiciousSignals.length > 0) {
        await this.alertSuspiciousActivity({
          userId,
          signals: suspiciousSignals,
          activeSessions: activeSessions.length,
          newSession: newSessionMetadata,
        });
      }

      return suspiciousSignals;
    } catch (error) {
      console.error("Suspicious activity detection failed:", error);
      return [];
    }
  }
}

// Authentication service using secure sessions
class SessionAuthService {
  constructor() {
    this.sessionManager = new SecureSessionManager();
    this.passwordManager = new SecurePasswordManager(); // From previous blog
  }

  async login(credentials, metadata = {}) {
    try {
      const { email, password } = credentials;

      // Authenticate user
      const user = await this.authenticateUser(email, password);
      if (!user) {
        throw new AuthenticationError("Invalid credentials");
      }

      // Check for suspicious activity
      const suspiciousSignals =
        await this.sessionManager.detectSuspiciousActivity(user.id, metadata);

      if (suspiciousSignals.length > 0) {
        // Require additional verification for suspicious logins
        return {
          success: false,
          requiresAdditionalVerification: true,
          verificationMethods: ["email", "sms"],
          suspiciousSignals,
        };
      }

      // Create session
      const sessionResult = await this.sessionManager.createSession(user.id, {
        ...metadata,
        loginMethod: "password",
      });

      return {
        success: true,
        session: sessionResult,
        user: sessionResult.user,
      };
    } catch (error) {
      console.error("Login failed:", error);
      throw error;
    }
  }

  async logout(sessionId) {
    try {
      const destroyed = await this.sessionManager.destroySession(sessionId);
      return { success: destroyed };
    } catch (error) {
      console.error("Logout failed:", error);
      throw error;
    }
  }

  async logoutAllDevices(userId, currentSessionId) {
    try {
      const destroyedCount = await this.sessionManager.destroyAllUserSessions(
        userId,
        currentSessionId
      );

      return {
        success: true,
        destroyedSessions: destroyedCount,
      };
    } catch (error) {
      console.error("Logout all devices failed:", error);
      throw error;
    }
  }

  // Session-based authentication middleware
  requireSession() {
    return async (req, res, next) => {
      try {
        const sessionId = req.cookies?.sessionId;

        if (!sessionId) {
          return res.status(401).json({
            error: "No session",
            message: "Please log in to continue",
          });
        }

        const session = await this.sessionManager.validateSession(sessionId);

        if (!session) {
          // Clear invalid session cookie
          res.clearCookie("sessionId");
          return res.status(401).json({
            error: "Invalid session",
            message: "Your session has expired. Please log in again.",
          });
        }

        // Get full user data
        const user = await this.getUserData(session.userId);
        if (!user || !user.isActive) {
          await this.sessionManager.destroySession(sessionId);
          res.clearCookie("sessionId");
          return res.status(401).json({
            error: "User not active",
            message: "Your account is no longer active",
          });
        }

        // Attach session and user to request
        req.session = session;
        req.user = user;

        next();
      } catch (error) {
        console.error("Session middleware failed:", error);
        res.status(500).json({
          error: "Session validation failed",
          message: "Unable to validate your session",
        });
      }
    };
  }
}

// Usage in routes
const sessionAuth = new SessionAuthService();

app.post("/api/login", async (req, res) => {
  try {
    const result = await sessionAuth.login(req.body, {
      ipAddress: req.ip,
      userAgent: req.get("User-Agent"),
      deviceFingerprint: req.headers["x-device-fingerprint"],
    });

    if (result.success) {
      // Set secure session cookie
      res.cookie("sessionId", result.session.sessionId, {
        httpOnly: true, // Prevent XSS access
        secure: process.env.NODE_ENV === "production", // HTTPS only in production
        sameSite: "strict", // CSRF protection
        maxAge: 24 * 60 * 60 * 1000, // 24 hours
      });

      res.json({
        success: true,
        user: result.user,
        expiresAt: result.session.expiresAt,
      });
    } else {
      res.status(202).json(result); // 202 Accepted, additional verification required
    }
  } catch (error) {
    res.status(401).json({
      error: "Login failed",
      message: error.message,
    });
  }
});

app.post("/api/logout", sessionAuth.requireSession(), async (req, res) => {
  const sessionId = req.cookies.sessionId;
  const result = await sessionAuth.logout(sessionId);

  res.clearCookie("sessionId");
  res.json(result);
});

app.get(
  "/api/user/sessions",
  sessionAuth.requireSession(),
  async (req, res) => {
    const sessions = await sessionAuth.sessionManager.getUserActiveSessions(
      req.user.id
    );
    res.json({ sessions });
  }
);

JWT Token-Based Authentication: Stateless and Scalable

Professional JWT Implementation with Security

The JWT security pitfalls that destroy trust:

// ❌ Insecure JWT implementation that gets hacked
const jwt = require("jsonwebtoken");

app.post("/login", async (req, res) => {
  const user = await validateCredentials(req.body.email, req.body.password);
  if (user) {
    // DANGER: Weak JWT implementation
    const token = jwt.sign(
      {
        userId: user.id,
        role: "user", // Attacker can change this to 'admin'
      },
      "secret123" // Weak secret, easily cracked
      // No expiration - token valid forever
    );

    res.json({ token });
  }
});

// Vulnerable validation
app.use((req, res, next) => {
  const token = req.headers.authorization?.split(" ")[1];
  if (token) {
    try {
      req.user = jwt.verify(token, "secret123"); // Same weak secret
      // No additional validation - accepts any valid-looking JWT
    } catch (error) {
      // Silently fails, continues without authentication
    }
  }
  next();
});

// Attack vectors:
// 1. Brute force the weak secret
// 2. Modify token claims (change role to admin)
// 3. Use tokens forever (no expiration)
// 4. No blacklisting for compromised tokens
// 5. No refresh token mechanism

Production-ready JWT authentication system:

// ✅ Secure JWT implementation with comprehensive security
const jwt = require("jsonwebtoken");
const crypto = require("crypto");

class SecureJWTManager {
  constructor() {
    // Strong secrets from environment
    this.accessTokenSecret = process.env.JWT_ACCESS_SECRET;
    this.refreshTokenSecret = process.env.JWT_REFRESH_SECRET;

    if (!this.accessTokenSecret || !this.refreshTokenSecret) {
      throw new Error("JWT secrets not configured");
    }

    // Token configuration
    this.accessTokenExpiry = "15m"; // Short-lived access tokens
    this.refreshTokenExpiry = "7d"; // Longer-lived refresh tokens
    this.issuer = "your-app";
    this.audience = "your-app-users";

    // Token blacklist (Redis-based)
    this.tokenBlacklist = new Map(); // Use Redis in production
    this.setupTokenCleanup();
  }

  async generateTokenPair(user) {
    try {
      const tokenId = crypto.randomUUID(); // Unique token identifier
      const issuedAt = Math.floor(Date.now() / 1000);

      // Access token payload (minimal, frequently verified)
      const accessPayload = {
        sub: user.id, // Subject (user ID)
        email: user.email,
        username: user.username,
        role: user.role,
        permissions: user.permissions || [],
        iat: issuedAt,
        jti: tokenId, // JWT ID for blacklisting
        type: "access",
      };

      // Refresh token payload (more detailed, less frequently used)
      const refreshPayload = {
        sub: user.id,
        email: user.email,
        iat: issuedAt,
        jti: crypto.randomUUID(),
        type: "refresh",
        // Store device/session info for security
        deviceFingerprint: user.deviceFingerprint,
        ipAddress: user.ipAddress,
      };

      // Generate tokens
      const accessToken = jwt.sign(accessPayload, this.accessTokenSecret, {
        expiresIn: this.accessTokenExpiry,
        issuer: this.issuer,
        audience: this.audience,
        algorithm: "HS256",
      });

      const refreshToken = jwt.sign(refreshPayload, this.refreshTokenSecret, {
        expiresIn: this.refreshTokenExpiry,
        issuer: this.issuer,
        audience: this.audience,
        algorithm: "HS256",
      });

      // Store refresh token metadata for security tracking
      await this.storeRefreshTokenMetadata(refreshPayload.jti, {
        userId: user.id,
        deviceFingerprint: user.deviceFingerprint,
        ipAddress: user.ipAddress,
        userAgent: user.userAgent,
        createdAt: new Date(),
        isActive: true,
      });

      return {
        accessToken,
        refreshToken,
        accessTokenExpiresIn: this.parseExpiryToSeconds(this.accessTokenExpiry),
        refreshTokenExpiresIn: this.parseExpiryToSeconds(
          this.refreshTokenExpiry
        ),
        tokenType: "Bearer",
      };
    } catch (error) {
      console.error("Token generation failed:", error);
      throw new Error("Failed to generate authentication tokens");
    }
  }

  async verifyAccessToken(token) {
    try {
      // First check if token is blacklisted
      if (await this.isTokenBlacklisted(token)) {
        throw new Error("Token has been revoked");
      }

      // Verify token signature and claims
      const decoded = jwt.verify(token, this.accessTokenSecret, {
        issuer: this.issuer,
        audience: this.audience,
        algorithms: ["HS256"],
      });

      // Validate token type
      if (decoded.type !== "access") {
        throw new Error("Invalid token type");
      }

      // Additional security checks
      const securityChecks = await this.performSecurityChecks(decoded);
      if (!securityChecks.valid) {
        throw new Error(securityChecks.reason);
      }

      return {
        valid: true,
        user: {
          id: decoded.sub,
          email: decoded.email,
          username: decoded.username,
          role: decoded.role,
          permissions: decoded.permissions,
        },
        tokenId: decoded.jti,
        issuedAt: new Date(decoded.iat * 1000),
      };
    } catch (error) {
      console.error("Access token verification failed:", error);
      return {
        valid: false,
        error: error.message,
      };
    }
  }

  async refreshAccessToken(refreshToken) {
    try {
      // Verify refresh token
      const decoded = jwt.verify(refreshToken, this.refreshTokenSecret, {
        issuer: this.issuer,
        audience: this.audience,
        algorithms: ["HS256"],
      });

      if (decoded.type !== "refresh") {
        throw new Error("Invalid refresh token type");
      }

      // Check if refresh token is still active
      const refreshTokenMetadata = await this.getRefreshTokenMetadata(
        decoded.jti
      );
      if (!refreshTokenMetadata || !refreshTokenMetadata.isActive) {
        throw new Error("Refresh token has been revoked");
      }

      // Get current user data (permissions may have changed)
      const currentUser = await this.getCurrentUserData(decoded.sub);
      if (!currentUser || !currentUser.isActive) {
        throw new Error("User account is not active");
      }

      // Generate new access token with current user data
      const newTokenPair = await this.generateTokenPair({
        ...currentUser,
        deviceFingerprint: refreshTokenMetadata.deviceFingerprint,
        ipAddress: refreshTokenMetadata.ipAddress,
      });

      return {
        success: true,
        accessToken: newTokenPair.accessToken,
        accessTokenExpiresIn: newTokenPair.accessTokenExpiresIn,
      };
    } catch (error) {
      console.error("Token refresh failed:", error);
      return {
        success: false,
        error: error.message,
      };
    }
  }

  async revokeToken(token, tokenType = "access") {
    try {
      let decoded;

      if (tokenType === "access") {
        decoded = jwt.decode(token); // Don't verify expired tokens
      } else {
        decoded = jwt.verify(token, this.refreshTokenSecret);
      }

      if (!decoded) {
        throw new Error("Invalid token format");
      }

      // Add token to blacklist
      await this.blacklistToken(decoded.jti, decoded.exp);

      // If refreshing token, deactivate it
      if (tokenType === "refresh") {
        await this.deactivateRefreshToken(decoded.jti);
      }

      this.logTokenRevocation({
        tokenId: decoded.jti,
        userId: decoded.sub,
        tokenType,
        revokedAt: new Date(),
      });

      return { success: true };
    } catch (error) {
      console.error("Token revocation failed:", error);
      return { success: false, error: error.message };
    }
  }

  async revokeAllUserTokens(userId) {
    try {
      // Get all active refresh tokens for user
      const refreshTokens = await this.getUserRefreshTokens(userId);

      // Deactivate all refresh tokens
      const revokePromises = refreshTokens.map((token) =>
        this.deactivateRefreshToken(token.jti)
      );

      await Promise.all(revokePromises);

      this.logTokenRevocation({
        userId,
        tokenType: "all",
        count: refreshTokens.length,
        revokedAt: new Date(),
      });

      return {
        success: true,
        revokedTokens: refreshTokens.length,
      };
    } catch (error) {
      console.error("Bulk token revocation failed:", error);
      return { success: false, error: error.message };
    }
  }

  // Security checks for additional validation
  async performSecurityChecks(tokenPayload) {
    // Check if user account is still active
    const user = await this.getCurrentUserData(tokenPayload.sub);
    if (!user || !user.isActive) {
      return { valid: false, reason: "User account inactive" };
    }

    // Check if user's role/permissions have changed significantly
    if (user.role !== tokenPayload.role) {
      return {
        valid: false,
        reason: "User role changed - reauthentication required",
      };
    }

    // Check token age vs security requirements
    const tokenAge = Date.now() - tokenPayload.iat * 1000;
    const maxTokenAge = 24 * 60 * 60 * 1000; // 24 hours max

    if (tokenAge > maxTokenAge) {
      return { valid: false, reason: "Token too old" };
    }

    return { valid: true };
  }

  // JWT authentication middleware
  requireJWT(requiredPermissions = []) {
    return async (req, res, next) => {
      try {
        const authHeader = req.headers.authorization;

        if (!authHeader || !authHeader.startsWith("Bearer ")) {
          return res.status(401).json({
            error: "No access token",
            message: "Authorization header with Bearer token required",
          });
        }

        const token = authHeader.slice(7);
        const verificationResult = await this.verifyAccessToken(token);

        if (!verificationResult.valid) {
          return res.status(401).json({
            error: "Invalid token",
            message: verificationResult.error,
          });
        }

        // Check permissions if required
        if (requiredPermissions.length > 0) {
          const userPermissions = verificationResult.user.permissions || [];
          const hasPermission = requiredPermissions.some(
            (permission) =>
              userPermissions.includes(permission) ||
              userPermissions.includes("admin")
          );

          if (!hasPermission) {
            return res.status(403).json({
              error: "Insufficient permissions",
              message: `Required permissions: ${requiredPermissions.join(
                ", "
              )}`,
              userPermissions,
            });
          }
        }

        // Attach user to request
        req.user = verificationResult.user;
        req.token = {
          id: verificationResult.tokenId,
          issuedAt: verificationResult.issuedAt,
        };

        next();
      } catch (error) {
        console.error("JWT middleware failed:", error);
        res.status(500).json({
          error: "Token validation failed",
          message: "Unable to validate your access token",
        });
      }
    };
  }

  parseExpiryToSeconds(expiry) {
    // Convert JWT expiry format to seconds
    const units = {
      s: 1,
      m: 60,
      h: 3600,
      d: 86400,
    };

    const match = expiry.match(/^(\d+)([smhd])$/);
    if (match) {
      return parseInt(match[1]) * units[match[2]];
    }

    return 3600; // Default 1 hour
  }
}

// Usage in authentication routes
const jwtManager = new SecureJWTManager();

app.post("/api/auth/login", async (req, res) => {
  try {
    const { email, password } = req.body;

    const user = await authenticateUser(email, password);
    if (!user) {
      return res.status(401).json({
        error: "Authentication failed",
        message: "Invalid email or password",
      });
    }

    const tokenPair = await jwtManager.generateTokenPair({
      ...user,
      deviceFingerprint: req.headers["x-device-fingerprint"],
      ipAddress: req.ip,
      userAgent: req.get("User-Agent"),
    });

    // Set refresh token as httpOnly cookie
    res.cookie("refreshToken", tokenPair.refreshToken, {
      httpOnly: true,
      secure: process.env.NODE_ENV === "production",
      sameSite: "strict",
      maxAge: tokenPair.refreshTokenExpiresIn * 1000,
    });

    res.json({
      success: true,
      accessToken: tokenPair.accessToken,
      tokenType: tokenPair.tokenType,
      expiresIn: tokenPair.accessTokenExpiresIn,
      user: {
        id: user.id,
        email: user.email,
        username: user.username,
        role: user.role,
      },
    });
  } catch (error) {
    console.error("Login failed:", error);
    res.status(500).json({
      error: "Login failed",
      message: "Unable to complete authentication",
    });
  }
});

app.post("/api/auth/refresh", async (req, res) => {
  try {
    const refreshToken = req.cookies.refreshToken;

    if (!refreshToken) {
      return res.status(401).json({
        error: "No refresh token",
        message: "Please log in again",
      });
    }

    const result = await jwtManager.refreshAccessToken(refreshToken);

    if (!result.success) {
      res.clearCookie("refreshToken");
      return res.status(401).json({
        error: "Token refresh failed",
        message: result.error,
      });
    }

    res.json({
      success: true,
      accessToken: result.accessToken,
      tokenType: "Bearer",
      expiresIn: result.accessTokenExpiresIn,
    });
  } catch (error) {
    console.error("Token refresh failed:", error);
    res.status(500).json({
      error: "Token refresh failed",
      message: "Unable to refresh your session",
    });
  }
});

app.post("/api/auth/logout", jwtManager.requireJWT(), async (req, res) => {
  try {
    const accessToken = req.headers.authorization.slice(7);
    const refreshToken = req.cookies.refreshToken;

    // Revoke both tokens
    await Promise.all([
      jwtManager.revokeToken(accessToken, "access"),
      refreshToken
        ? jwtManager.revokeToken(refreshToken, "refresh")
        : Promise.resolve(),
    ]);

    res.clearCookie("refreshToken");
    res.json({ success: true, message: "Logged out successfully" });
  } catch (error) {
    console.error("Logout failed:", error);
    res.status(500).json({
      error: "Logout failed",
      message: "Unable to complete logout",
    });
  }
});

// Protected route examples
app.get("/api/user/profile", jwtManager.requireJWT(), async (req, res) => {
  const profile = await getUserProfile(req.user.id);
  res.json({ profile });
});

app.delete(
  "/api/admin/users/:userId",
  jwtManager.requireJWT(["admin", "user:delete"]),
  async (req, res) => {
    await deleteUser(req.params.userId);
    res.json({ success: true });
  }
);

OAuth 2.0 and OpenID Connect: Third-Party Authentication

Professional OAuth Implementation

OAuth 2.0 integration for seamless user experience:

// ✅ Comprehensive OAuth 2.0 and OpenID Connect implementation
const axios = require("axios");
const crypto = require("crypto");

class OAuthManager {
  constructor() {
    this.providers = {
      google: {
        clientId: process.env.GOOGLE_CLIENT_ID,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET,
        redirectUri: process.env.GOOGLE_REDIRECT_URI,
        authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
        tokenUrl: "https://oauth2.googleapis.com/token",
        userInfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
        scopes: ["openid", "email", "profile"],
      },

      github: {
        clientId: process.env.GITHUB_CLIENT_ID,
        clientSecret: process.env.GITHUB_CLIENT_SECRET,
        redirectUri: process.env.GITHUB_REDIRECT_URI,
        authUrl: "https://github.com/login/oauth/authorize",
        tokenUrl: "https://github.com/login/oauth/access_token",
        userInfoUrl: "https://api.github.com/user",
        scopes: ["user:email"],
      },

      microsoft: {
        clientId: process.env.MICROSOFT_CLIENT_ID,
        clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
        redirectUri: process.env.MICROSOFT_REDIRECT_URI,
        authUrl:
          "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
        tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
        userInfoUrl: "https://graph.microsoft.com/v1.0/me",
        scopes: ["openid", "email", "profile"],
      },
    };

    this.stateStore = new Map(); // Use Redis in production
    this.setupStateCleanup();
  }

  generateAuthUrl(provider, state = null) {
    try {
      const providerConfig = this.providers[provider];
      if (!providerConfig) {
        throw new Error(`Unsupported OAuth provider: ${provider}`);
      }

      // Generate secure state parameter to prevent CSRF
      const stateValue = state || crypto.randomBytes(32).toString("hex");
      const stateExpiry = Date.now() + 10 * 60 * 1000; // 10 minutes

      this.stateStore.set(stateValue, {
        provider,
        createdAt: Date.now(),
        expiresAt: stateExpiry,
      });

      const params = new URLSearchParams({
        client_id: providerConfig.clientId,
        redirect_uri: providerConfig.redirectUri,
        scope: providerConfig.scopes.join(" "),
        response_type: "code",
        state: stateValue,
        // Additional security parameters
        nonce: crypto.randomBytes(16).toString("hex"),
      });

      return {
        authUrl: `${providerConfig.authUrl}?${params.toString()}`,
        state: stateValue,
      };
    } catch (error) {
      console.error("OAuth URL generation failed:", error);
      throw error;
    }
  }

  async handleCallback(provider, code, state) {
    try {
      // Validate state parameter
      if (!this.validateState(state, provider)) {
        throw new Error("Invalid state parameter - possible CSRF attack");
      }

      // Exchange code for tokens
      const tokenResponse = await this.exchangeCodeForTokens(provider, code);

      // Get user information
      const userInfo = await this.getUserInfo(
        provider,
        tokenResponse.access_token
      );

      // Process user data and handle account linking
      const processedUser = await this.processOAuthUser(
        provider,
        userInfo,
        tokenResponse
      );

      // Clean up state
      this.stateStore.delete(state);

      return {
        success: true,
        user: processedUser,
        tokens: tokenResponse,
      };
    } catch (error) {
      console.error("OAuth callback failed:", error);
      throw error;
    }
  }

  validateState(state, expectedProvider) {
    if (!state) return false;

    const stateData = this.stateStore.get(state);
    if (!stateData) return false;

    // Check expiration
    if (Date.now() > stateData.expiresAt) {
      this.stateStore.delete(state);
      return false;
    }

    // Check provider match
    if (stateData.provider !== expectedProvider) {
      return false;
    }

    return true;
  }

  async exchangeCodeForTokens(provider, code) {
    try {
      const providerConfig = this.providers[provider];

      const tokenRequest = {
        client_id: providerConfig.clientId,
        client_secret: providerConfig.clientSecret,
        code,
        grant_type: "authorization_code",
        redirect_uri: providerConfig.redirectUri,
      };

      const response = await axios.post(providerConfig.tokenUrl, tokenRequest, {
        headers: {
          Accept: "application/json",
          "Content-Type": "application/x-www-form-urlencoded",
        },
      });

      if (response.data.error) {
        throw new Error(
          `Token exchange failed: ${response.data.error_description}`
        );
      }

      return {
        access_token: response.data.access_token,
        refresh_token: response.data.refresh_token,
        id_token: response.data.id_token, // OpenID Connect
        token_type: response.data.token_type,
        expires_in: response.data.expires_in,
        scope: response.data.scope,
      };
    } catch (error) {
      console.error("Token exchange failed:", error);
      throw new Error("Failed to exchange authorization code for tokens");
    }
  }

  async getUserInfo(provider, accessToken) {
    try {
      const providerConfig = this.providers[provider];

      const response = await axios.get(providerConfig.userInfoUrl, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          Accept: "application/json",
        },
      });

      return response.data;
    } catch (error) {
      console.error("User info fetch failed:", error);
      throw new Error("Failed to fetch user information");
    }
  }

  async processOAuthUser(provider, oauthUserInfo, tokens) {
    try {
      // Normalize user data across providers
      const normalizedUser = this.normalizeUserData(provider, oauthUserInfo);

      // Check if user already exists
      const existingUser = await this.findUserByEmail(normalizedUser.email);

      if (existingUser) {
        // Link OAuth account to existing user
        await this.linkOAuthAccount(existingUser.id, provider, {
          providerId: normalizedUser.providerId,
          providerData: oauthUserInfo,
          tokens,
        });

        return {
          ...existingUser,
          isNewUser: false,
          linkedProvider: provider,
        };
      } else {
        // Create new user from OAuth data
        const newUser = await this.createUserFromOAuth(
          provider,
          normalizedUser,
          tokens
        );

        return {
          ...newUser,
          isNewUser: true,
          primaryProvider: provider,
        };
      }
    } catch (error) {
      console.error("OAuth user processing failed:", error);
      throw error;
    }
  }

  normalizeUserData(provider, oauthData) {
    switch (provider) {
      case "google":
        return {
          providerId: oauthData.id,
          email: oauthData.email,
          firstName: oauthData.given_name,
          lastName: oauthData.family_name,
          fullName: oauthData.name,
          profilePicture: oauthData.picture,
          emailVerified: oauthData.email_verified,
          locale: oauthData.locale,
        };

      case "github":
        return {
          providerId: oauthData.id.toString(),
          email: oauthData.email,
          firstName: oauthData.name?.split(" ")[0],
          lastName: oauthData.name?.split(" ").slice(1).join(" "),
          fullName: oauthData.name,
          username: oauthData.login,
          profilePicture: oauthData.avatar_url,
          bio: oauthData.bio,
          company: oauthData.company,
          location: oauthData.location,
        };

      case "microsoft":
        return {
          providerId: oauthData.id,
          email: oauthData.mail || oauthData.userPrincipalName,
          firstName: oauthData.givenName,
          lastName: oauthData.surname,
          fullName: oauthData.displayName,
          jobTitle: oauthData.jobTitle,
          officeLocation: oauthData.officeLocation,
        };

      default:
        throw new Error(
          `Normalization not implemented for provider: ${provider}`
        );
    }
  }

  async createUserFromOAuth(provider, normalizedUser, tokens) {
    try {
      const userId = crypto.randomUUID();

      const newUser = {
        id: userId,
        email: normalizedUser.email,
        username: normalizedUser.username || normalizedUser.email.split("@")[0],
        firstName: normalizedUser.firstName,
        lastName: normalizedUser.lastName,
        fullName: normalizedUser.fullName,
        profilePicture: normalizedUser.profilePicture,
        emailVerified: normalizedUser.emailVerified || false,
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
        authMethod: "oauth",
        primaryOAuthProvider: provider,
      };

      // Create user account
      await db.users.insertOne(newUser);

      // Create OAuth account link
      await this.linkOAuthAccount(userId, provider, {
        providerId: normalizedUser.providerId,
        providerData: normalizedUser,
        tokens,
        isPrimary: true,
      });

      // Log OAuth account creation
      this.logOAuthEvent({
        type: "account_created",
        userId,
        provider,
        email: normalizedUser.email,
        timestamp: new Date(),
      });

      return newUser;
    } catch (error) {
      console.error("OAuth user creation failed:", error);
      throw error;
    }
  }

  async linkOAuthAccount(userId, provider, oauthData) {
    try {
      const linkData = {
        userId,
        provider,
        providerId: oauthData.providerId,
        providerData: oauthData.providerData,
        accessToken: this.encryptToken(oauthData.tokens.access_token),
        refreshToken: oauthData.tokens.refresh_token
          ? this.encryptToken(oauthData.tokens.refresh_token)
          : null,
        tokenExpiresAt: oauthData.tokens.expires_in
          ? new Date(Date.now() + oauthData.tokens.expires_in * 1000)
          : null,
        isPrimary: oauthData.isPrimary || false,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      // Check if link already exists
      const existingLink = await db.oauthAccounts.findOne({
        userId,
        provider,
        providerId: oauthData.providerId,
      });

      if (existingLink) {
        // Update existing link
        await db.oauthAccounts.updateOne(
          { _id: existingLink._id },
          { $set: { ...linkData, updatedAt: new Date() } }
        );
      } else {
        // Create new link
        await db.oauthAccounts.insertOne(linkData);
      }

      this.logOAuthEvent({
        type: "account_linked",
        userId,
        provider,
        providerId: oauthData.providerId,
        timestamp: new Date(),
      });
    } catch (error) {
      console.error("OAuth account linking failed:", error);
      throw error;
    }
  }
}

// OAuth routes
const oauthManager = new OAuthManager();

app.get("/api/auth/oauth/:provider", (req, res) => {
  try {
    const { provider } = req.params;
    const { authUrl, state } = oauthManager.generateAuthUrl(provider);

    // Store state in session for additional security
    req.session.oauthState = state;

    res.redirect(authUrl);
  } catch (error) {
    console.error("OAuth initiation failed:", error);
    res.status(400).json({
      error: "OAuth setup failed",
      message: error.message,
    });
  }
});

app.get("/api/auth/oauth/:provider/callback", async (req, res) => {
  try {
    const { provider } = req.params;
    const { code, state, error } = req.query;

    if (error) {
      return res.status(400).json({
        error: "OAuth authorization failed",
        message: error,
      });
    }

    if (!code || !state) {
      return res.status(400).json({
        error: "Invalid callback",
        message: "Missing authorization code or state",
      });
    }

    const result = await oauthManager.handleCallback(provider, code, state);

    if (result.success) {
      // Create session or JWT for the authenticated user
      const sessionResult = await sessionAuth.createSession(result.user.id, {
        ipAddress: req.ip,
        userAgent: req.get("User-Agent"),
        loginMethod: `oauth_${provider}`,
      });

      // Set session cookie
      res.cookie("sessionId", sessionResult.sessionId, {
        httpOnly: true,
        secure: process.env.NODE_ENV === "production",
        sameSite: "strict",
        maxAge: 24 * 60 * 60 * 1000,
      });

      // Redirect to success page
      res.redirect(
        `${process.env.FRONTEND_URL}/auth/success?newUser=${result.user.isNewUser}`
      );
    } else {
      res.redirect(
        `${process.env.FRONTEND_URL}/auth/error?reason=oauth_failed`
      );
    }
  } catch (error) {
    console.error("OAuth callback failed:", error);
    res.redirect(`${process.env.FRONTEND_URL}/auth/error?reason=server_error`);
  }
});

Multi-Factor Authentication: Enhanced Security

Professional MFA Implementation

Comprehensive MFA system for critical security:

// ✅ Production-ready Multi-Factor Authentication
const speakeasy = require("speakeasy");
const QRCode = require("qrcode");
const crypto = require("crypto");

class MultiFactorAuthManager {
  constructor() {
    this.issuer = process.env.APP_NAME || "Your App";
    this.mfaTokenExpiry = 5 * 60 * 1000; // 5 minutes
    this.backupCodeCount = 10;
    this.maxMFAAttempts = 3;
    this.setupMFACleanup();
  }

  // Setup TOTP (Time-based One-Time Password)
  async setupTOTP(userId) {
    try {
      const user = await this.getUserData(userId);
      if (!user) {
        throw new Error("User not found");
      }

      // Generate secret for TOTP
      const secret = speakeasy.generateSecret({
        name: `${this.issuer} (${user.email})`,
        issuer: this.issuer,
        length: 32,
      });

      // Generate QR code for authenticator apps
      const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);

      // Generate backup codes
      const backupCodes = this.generateBackupCodes();

      // Store MFA setup (encrypted)
      const mfaSetup = {
        userId,
        method: "totp",
        secret: this.encryptSecret(secret.base32),
        backupCodes: backupCodes.map((code) => this.hashBackupCode(code)),
        isVerified: false,
        setupAt: new Date(),
        backupCodesUsed: [],
      };

      await db.mfaSetups.insertOne(mfaSetup);

      return {
        secret: secret.base32,
        qrCode: qrCodeUrl,
        backupCodes: backupCodes,
        manualEntryKey: secret.base32,
      };
    } catch (error) {
      console.error("TOTP setup failed:", error);
      throw error;
    }
  }

  async verifyTOTPSetup(userId, token) {
    try {
      const mfaSetup = await db.mfaSetups.findOne({
        userId,
        method: "totp",
        isVerified: false,
      });

      if (!mfaSetup) {
        throw new Error("No pending TOTP setup found");
      }

      const decryptedSecret = this.decryptSecret(mfaSetup.secret);

      const verified = speakeasy.totp.verify({
        secret: decryptedSecret,
        encoding: "base32",
        token: token,
        window: 2, // Allow 2 time steps tolerance
      });

      if (!verified) {
        throw new Error("Invalid TOTP token");
      }

      // Mark TOTP as verified and activate
      await db.mfaSetups.updateOne(
        { _id: mfaSetup._id },
        {
          $set: {
            isVerified: true,
            verifiedAt: new Date(),
            isActive: true,
          },
        }
      );

      // Update user's MFA status
      await db.users.updateOne(
        { id: userId },
        {
          $set: {
            mfaEnabled: true,
            mfaMethod: "totp",
            mfaEnabledAt: new Date(),
          },
        }
      );

      this.logMFAEvent({
        type: "mfa_enabled",
        userId,
        method: "totp",
        timestamp: new Date(),
      });

      return { success: true, message: "MFA enabled successfully" };
    } catch (error) {
      console.error("TOTP verification failed:", error);
      throw error;
    }
  }

  async verifyMFA(userId, token, method = "totp") {
    try {
      const mfaSetup = await db.mfaSetups.findOne({
        userId,
        method,
        isVerified: true,
        isActive: true,
      });

      if (!mfaSetup) {
        throw new Error("MFA not configured for this user");
      }

      // Check for rate limiting
      const recentAttempts = await this.getRecentMFAAttempts(userId);
      if (recentAttempts >= this.maxMFAAttempts) {
        throw new Error("Too many MFA attempts. Please try again later.");
      }

      let verified = false;
      let usedBackupCode = false;

      if (method === "totp") {
        // Verify TOTP token
        const decryptedSecret = this.decryptSecret(mfaSetup.secret);
        verified = speakeasy.totp.verify({
          secret: decryptedSecret,
          encoding: "base32",
          token: token,
          window: 2,
        });
      } else if (method === "backup_code") {
        // Verify backup code
        const hashedToken = this.hashBackupCode(token);
        const backupCodeIndex = mfaSetup.backupCodes.indexOf(hashedToken);

        if (
          backupCodeIndex !== -1 &&
          !mfaSetup.backupCodesUsed.includes(hashedToken)
        ) {
          verified = true;
          usedBackupCode = true;

          // Mark backup code as used
          await db.mfaSetups.updateOne(
            { _id: mfaSetup._id },
            {
              $push: { backupCodesUsed: hashedToken },
            }
          );
        }
      }

      // Log MFA attempt
      this.logMFAAttempt({
        userId,
        method,
        success: verified,
        timestamp: new Date(),
        usedBackupCode,
      });

      if (!verified) {
        throw new Error("Invalid MFA token");
      }

      return {
        success: true,
        method,
        usedBackupCode,
        remainingBackupCodes: usedBackupCode
          ? mfaSetup.backupCodes.length - mfaSetup.backupCodesUsed.length - 1
          : mfaSetup.backupCodes.length - mfaSetup.backupCodesUsed.length,
      };
    } catch (error) {
      console.error("MFA verification failed:", error);
      throw error;
    }
  }

  async requireMFA(userId, action) {
    try {
      // Check if MFA is required for this action
      const mfaRequirements = await this.getMFARequirements(userId, action);
      if (!mfaRequirements.required) {
        return { required: false };
      }

      // Generate MFA challenge
      const challengeId = crypto.randomUUID();
      const expiresAt = new Date(Date.now() + this.mfaTokenExpiry);

      const challenge = {
        challengeId,
        userId,
        action,
        createdAt: new Date(),
        expiresAt,
        verified: false,
        attempts: 0,
      };

      await db.mfaChallenges.insertOne(challenge);

      return {
        required: true,
        challengeId,
        expiresAt,
        availableMethods: mfaRequirements.availableMethods,
      };
    } catch (error) {
      console.error("MFA requirement check failed:", error);
      throw error;
    }
  }

  async completeMFAChallenge(challengeId, token, method = "totp") {
    try {
      const challenge = await db.mfaChallenges.findOne({
        challengeId,
        verified: false,
        expiresAt: { $gt: new Date() },
      });

      if (!challenge) {
        throw new Error("Invalid or expired MFA challenge");
      }

      // Verify MFA token
      const verificationResult = await this.verifyMFA(
        challenge.userId,
        token,
        method
      );

      if (!verificationResult.success) {
        // Increment attempt count
        await db.mfaChallenges.updateOne(
          { challengeId },
          { $inc: { attempts: 1 } }
        );

        throw new Error("MFA verification failed");
      }

      // Mark challenge as completed
      await db.mfaChallenges.updateOne(
        { challengeId },
        {
          $set: {
            verified: true,
            completedAt: new Date(),
            method,
          },
        }
      );

      return {
        success: true,
        challengeId,
        completedAt: new Date(),
      };
    } catch (error) {
      console.error("MFA challenge completion failed:", error);
      throw error;
    }
  }

  // MFA middleware for sensitive operations
  requireMFAMiddleware(action) {
    return async (req, res, next) => {
      try {
        if (!req.user) {
          return res.status(401).json({
            error: "Authentication required",
          });
        }

        // Check if user has MFA enabled
        const user = await db.users.findOne({ id: req.user.id });
        if (!user.mfaEnabled) {
          return res.status(403).json({
            error: "MFA required",
            message:
              "Multi-factor authentication must be enabled for this action",
            setupUrl: "/api/mfa/setup",
          });
        }

        // Check if MFA is required for this action
        const mfaRequirement = await this.requireMFA(req.user.id, action);

        if (!mfaRequirement.required) {
          return next(); // MFA not required
        }

        // Check for MFA challenge completion
        const challengeId = req.headers["x-mfa-challenge-id"];
        if (!challengeId) {
          return res.status(403).json({
            error: "MFA challenge required",
            challenge: mfaRequirement,
          });
        }

        const challenge = await db.mfaChallenges.findOne({
          challengeId,
          userId: req.user.id,
          verified: true,
          expiresAt: { $gt: new Date() },
        });

        if (!challenge) {
          return res.status(403).json({
            error: "Invalid or expired MFA challenge",
            challenge: mfaRequirement,
          });
        }

        // MFA verified, attach challenge info to request
        req.mfaChallenge = challenge;

        next();
      } catch (error) {
        console.error("MFA middleware failed:", error);
        res.status(500).json({
          error: "MFA verification failed",
          message: "Unable to verify multi-factor authentication",
        });
      }
    };
  }

  generateBackupCodes() {
    const codes = [];
    for (let i = 0; i < this.backupCodeCount; i++) {
      const code = crypto.randomInt(100000000, 999999999).toString();
      codes.push(code);
    }
    return codes;
  }

  hashBackupCode(code) {
    return crypto.createHash("sha256").update(code).digest("hex");
  }
}

// MFA API routes
const mfaManager = new MultiFactorAuthManager();

app.post(
  "/api/mfa/setup/totp",
  sessionAuth.requireSession(),
  async (req, res) => {
    try {
      const setup = await mfaManager.setupTOTP(req.user.id);

      res.json({
        success: true,
        setup: {
          qrCode: setup.qrCode,
          manualEntryKey: setup.manualEntryKey,
          backupCodes: setup.backupCodes,
        },
      });
    } catch (error) {
      console.error("MFA setup failed:", error);
      res.status(500).json({
        error: "MFA setup failed",
        message: error.message,
      });
    }
  }
);

app.post(
  "/api/mfa/verify/setup",
  sessionAuth.requireSession(),
  async (req, res) => {
    try {
      const { token } = req.body;

      const result = await mfaManager.verifyTOTPSetup(req.user.id, token);

      res.json(result);
    } catch (error) {
      res.status(400).json({
        error: "MFA verification failed",
        message: error.message,
      });
    }
  }
);

app.post(
  "/api/mfa/challenge/:challengeId",
  sessionAuth.requireSession(),
  async (req, res) => {
    try {
      const { challengeId } = req.params;
      const { token, method } = req.body;

      const result = await mfaManager.completeMFAChallenge(
        challengeId,
        token,
        method
      );

      res.json(result);
    } catch (error) {
      res.status(400).json({
        error: "MFA challenge failed",
        message: error.message,
      });
    }
  }
);

// Protected route requiring MFA
app.delete(
  "/api/account",
  sessionAuth.requireSession(),
  mfaManager.requireMFAMiddleware("account_deletion"),
  async (req, res) => {
    try {
      await deleteUserAccount(req.user.id);

      res.json({
        success: true,
        message: "Account deleted successfully",
      });
    } catch (error) {
      res.status(500).json({
        error: "Account deletion failed",
        message: error.message,
      });
    }
  }
);

Key Takeaways

Authentication and authorization form the identity foundation that determines who can access your secure application and what they can do. Session-based authentication provides robust server-side control, JWT enables stateless scalability, OAuth integrates third-party providers seamlessly, and MFA adds critical security layers for sensitive operations.

The identity management mindset:

  • Authentication ≠ Authorization: Know WHO the user is (authentication) separately from WHAT they can do (authorization)
  • Security through redundancy: Multiple authentication factors and token validation layers prevent single points of failure
  • User experience matters: Seamless identity flows encourage adoption while maintaining security
  • Session management scales: Proper session handling enables multi-device, multi-platform user experiences

What distinguishes professional identity systems:

  • Clear separation between authentication and authorization with proper middleware architecture
  • Session management that handles expiration, cleanup, and security monitoring automatically
  • JWT implementations that use proper secrets, expiration, and blacklisting for compromised tokens
  • OAuth integrations that support multiple providers with normalized user data handling
  • MFA systems that balance security requirements with user experience

What’s Next

This article covered authentication fundamentals, token management, and third-party integration. The next article completes the identity picture with role-based access control (RBAC), permission systems, social login scaling, Single Sign-On (SSO), and authentication best practices that enterprise applications require.

Authentication establishes identity. Authorization controls access. Together, they create the foundation for secure, scalable applications that users trust and administrators can manage effectively across complex organizational structures.