Skip to main content

Authentication and Authorization in JavaScript

Created: May 8, 2026 Larry Qu 8 min read

Authentication and authorization are critical for security. This article covers authentication methods, authorization patterns, and secure identity management.

Introduction

Authentication and authorization provide:

  • User identity verification
  • Access control
  • Security
  • Compliance
  • User management

Understanding these concepts helps you:

  • Implement secure authentication
  • Manage user access
  • Protect resources
  • Implement authorization
  • Build secure systems

Authentication Basics

Password Hashing

// ✅ Good: Secure password hashing
const bcrypt = require('bcrypt');

class PasswordManager {
  static async hashPassword(password) {
    const saltRounds = 10;
    return bcrypt.hash(password, saltRounds);
  }

  static async verifyPassword(password, hash) {
    return bcrypt.compare(password, hash);
  }

  static validatePasswordStrength(password) {
    const requirements = {
      minLength: password.length >= 8,
      hasUppercase: /[A-Z]/.test(password),
      hasLowercase: /[a-z]/.test(password),
      hasNumbers: /\d/.test(password),
      hasSpecialChars: /[!@#$%^&*]/.test(password)
    };

    return {
      strong: Object.values(requirements).every(v => v),
      requirements
    };
  }
}

// Usage
const hash = await PasswordManager.hashPassword('MyPassword123!');
const isValid = await PasswordManager.verifyPassword('MyPassword123!', hash);
console.log(isValid); // true

const strength = PasswordManager.validatePasswordStrength('weak');
console.log(strength.strong); // false

Session-based Authentication

// ✅ Good: Session-based authentication
const express = require('express');
const session = require('express-session');

const app = express();

app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: true, // HTTPS only
    httpOnly: true, // No JavaScript access
    sameSite: 'strict',
    maxAge: 3600000 // 1 hour
  }
}));

class SessionAuthenticator {
  static async login(req, username, password) {
    // Verify credentials
    const user = await this.verifyCredentials(username, password);

    if (!user) {
      throw new Error('Invalid credentials');
    }

    // Create session
    req.session.userId = user.id;
    req.session.username = user.username;
    req.session.role = user.role;

    return user;
  }

  static logout(req) {
    req.session.destroy((err) => {
      if (err) {
        console.error('Error destroying session:', err);
      }
    });
  }

  static isAuthenticated(req) {
    return req.session && req.session.userId;
  }

  static async verifyCredentials(username, password) {
    // Database lookup
    const user = await User.findByUsername(username);

    if (!user) {
      return null;
    }

    const isValid = await PasswordManager.verifyPassword(password, user.passwordHash);

    return isValid ? user : null;
  }
}

// Usage
app.post('/login', async (req, res) => {
  try {
    const user = await SessionAuthenticator.login(
      req,
      req.body.username,
      req.body.password
    );
    res.json({ success: true, user });
  } catch (error) {
    res.status(401).json({ error: error.message });
  }
});

JWT Authentication

JWT Implementation

// ✅ Good: JWT authentication
const jwt = require('jsonwebtoken');

class JWTAuthenticator {
  static generateToken(payload, expiresIn = '1h') {
    return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn });
  }

  static verifyToken(token) {
    try {
      return jwt.verify(token, process.env.JWT_SECRET);
    } catch (error) {
      return null;
    }
  }

  static decodeToken(token) {
    return jwt.decode(token);
  }

  static async login(username, password) {
    // Verify credentials
    const user = await this.verifyCredentials(username, password);

    if (!user) {
      throw new Error('Invalid credentials');
    }

    // Generate tokens
    const accessToken = this.generateToken(
      { userId: user.id, username: user.username, role: user.role },
      '1h'
    );

    const refreshToken = this.generateToken(
      { userId: user.id },
      '7d'
    );

    return { accessToken, refreshToken, user };
  }

  static refreshAccessToken(refreshToken) {
    const payload = this.verifyToken(refreshToken);

    if (!payload) {
      throw new Error('Invalid refresh token');
    }

    return this.generateToken(
      { userId: payload.userId },
      '1h'
    );
  }

  static async verifyCredentials(username, password) {
    const user = await User.findByUsername(username);

    if (!user) {
      return null;
    }

    const isValid = await PasswordManager.verifyPassword(password, user.passwordHash);

    return isValid ? user : null;
  }
}

// Usage
const { accessToken, refreshToken } = await JWTAuthenticator.login('john', 'password123');
console.log(accessToken); // JWT token

JWT Middleware

// ✅ Good: JWT middleware
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN

  if (!token) {
    return res.status(401).json({ error: 'Access token required' });
  }

  const payload = JWTAuthenticator.verifyToken(token);

  if (!payload) {
    return res.status(403).json({ error: 'Invalid or expired token' });
  }

  req.user = payload;
  next();
}

// Usage
app.get('/api/profile', authenticateToken, (req, res) => {
  res.json({ user: req.user });
});

OAuth 2.0

OAuth Implementation

// ✅ Good: OAuth 2.0 implementation
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL: '/auth/google/callback'
}, async (accessToken, refreshToken, profile, done) => {
  try {
    // Find or create user
    let user = await User.findByGoogleId(profile.id);

    if (!user) {
      user = await User.create({
        googleId: profile.id,
        email: profile.emails[0].value,
        name: profile.displayName,
        avatar: profile.photos[0].value
      });
    }

    return done(null, user);
  } catch (error) {
    return done(error);
  }
}));

// Routes
app.get('/auth/google',
  passport.authenticate('google', { scope: ['profile', 'email'] })
);

app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/login' }),
  (req, res) => {
    // Generate JWT token
    const token = JWTAuthenticator.generateToken({
      userId: req.user.id,
      email: req.user.email
    });

    res.redirect(`/?token=${token}`);
  }
);

Authorization

Role-Based Access Control (RBAC)

// ✅ Good: Role-based access control
class RoleBasedAccessControl {
  static roles = {
    admin: ['read', 'write', 'delete', 'manage_users'],
    moderator: ['read', 'write', 'delete'],
    user: ['read', 'write'],
    guest: ['read']
  };

  static hasPermission(role, permission) {
    const permissions = this.roles[role] || [];
    return permissions.includes(permission);
  }

  static hasRole(userRole, requiredRole) {
    const roleHierarchy = ['admin', 'moderator', 'user', 'guest'];
    const userLevel = roleHierarchy.indexOf(userRole);
    const requiredLevel = roleHierarchy.indexOf(requiredRole);

    return userLevel <= requiredLevel;
  }

  static middleware(requiredRole) {
    return (req, res, next) => {
      if (!req.user) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      if (!this.hasRole(req.user.role, requiredRole)) {
        return res.status(403).json({ error: 'Insufficient permissions' });
      }

      next();
    };
  }

  static permissionMiddleware(requiredPermission) {
    return (req, res, next) => {
      if (!req.user) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      if (!this.hasPermission(req.user.role, requiredPermission)) {
        return res.status(403).json({ error: 'Permission denied' });
      }

      next();
    };
  }
}

// Usage
app.delete('/api/users/:id',
  authenticateToken,
  RoleBasedAccessControl.middleware('admin'),
  (req, res) => {
    // Delete user
  }
);

Attribute-Based Access Control (ABAC)

// ✅ Good: Attribute-based access control
class AttributeBasedAccessControl {
  static policies = [
    {
      name: 'owner_can_edit',
      condition: (user, resource) => user.id === resource.ownerId
    },
    {
      name: 'admin_can_do_anything',
      condition: (user) => user.role === 'admin'
    },
    {
      name: 'published_content_readable',
      condition: (user, resource) => resource.published || user.id === resource.ownerId
    }
  ];

  static canAccess(user, resource, action) {
    for (const policy of this.policies) {
      if (policy.condition(user, resource)) {
        return true;
      }
    }

    return false;
  }

  static middleware(action) {
    return async (req, res, next) => {
      if (!req.user) {
        return res.status(401).json({ error: 'Authentication required' });
      }

      const resource = await this.getResource(req.params.id);

      if (!resource) {
        return res.status(404).json({ error: 'Resource not found' });
      }

      if (!this.canAccess(req.user, resource, action)) {
        return res.status(403).json({ error: 'Access denied' });
      }

      req.resource = resource;
      next();
    };
  }

  static async getResource(id) {
    // Fetch resource from database
    return null;
  }
}

// Usage
app.put('/api/posts/:id',
  authenticateToken,
  AttributeBasedAccessControl.middleware('edit'),
  (req, res) => {
    // Update post
  }
);

Multi-Factor Authentication (MFA)

TOTP Implementation

// ✅ Good: Time-based One-Time Password (TOTP)
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');

class TOTPManager {
  static generateSecret(username) {
    return speakeasy.generateSecret({
      name: `MyApp (${username})`,
      issuer: 'MyApp',
      length: 32
    });
  }

  static async generateQRCode(secret) {
    return QRCode.toDataURL(secret.otpauth_url);
  }

  static verifyToken(secret, token) {
    return speakeasy.totp.verify({
      secret: secret,
      encoding: 'base32',
      token: token,
      window: 2
    });
  }

  static async setupMFA(user) {
    const secret = this.generateSecret(user.username);
    const qrCode = await this.generateQRCode(secret);

    // Store secret temporarily
    user.mfaSecret = secret.base32;
    user.mfaEnabled = false;

    return { qrCode, secret: secret.base32 };
  }

  static async confirmMFA(user, token) {
    const isValid = this.verifyToken(user.mfaSecret, token);

    if (isValid) {
      user.mfaEnabled = true;
      await user.save();
    }

    return isValid;
  }

  static async verifyMFAToken(user, token) {
    if (!user.mfaEnabled) {
      return true;
    }

    return this.verifyToken(user.mfaSecret, token);
  }
}

// Usage
const { qrCode, secret } = await TOTPManager.setupMFA(user);
// Display QR code to user
// User scans with authenticator app
// User provides token to confirm
const confirmed = await TOTPManager.confirmMFA(user, userToken);

Practical Examples

Secure Login Flow

// ✅ Good: Secure login flow
app.post('/api/auth/login', async (req, res) => {
  try {
    const { username, password, mfaToken } = req.body;

    // Validate input
    if (!username || !password) {
      return res.status(400).json({ error: 'Username and password required' });
    }

    // Find user
    const user = await User.findByUsername(username);

    if (!user) {
      // Don't reveal if user exists
      return res.status(401).json({ error: 'Invalid credentials' });
    }

    // Verify password
    const isValid = await PasswordManager.verifyPassword(password, user.passwordHash);

    if (!isValid) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }

    // Verify MFA if enabled
    if (user.mfaEnabled) {
      if (!mfaToken) {
        return res.status(401).json({ error: 'MFA token required' });
      }

      const mfaValid = await TOTPManager.verifyMFAToken(user, mfaToken);

      if (!mfaValid) {
        return res.status(401).json({ error: 'Invalid MFA token' });
      }
    }

    // Generate tokens
    const accessToken = JWTAuthenticator.generateToken({
      userId: user.id,
      username: user.username,
      role: user.role
    });

    const refreshToken = JWTAuthenticator.generateToken({
      userId: user.id
    }, '7d');

    // Set secure cookie
    res.cookie('refreshToken', refreshToken, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      maxAge: 7 * 24 * 60 * 60 * 1000
    });

    res.json({
      success: true,
      accessToken,
      user: {
        id: user.id,
        username: user.username,
        email: user.email,
        role: user.role
      }
    });
  } catch (error) {
    console.error('Login error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Best Practices

  1. Use HTTPS everywhere:
    // ✅ Good
    // All authentication over HTTPS
    
    // ❌ Bad
    // HTTP connections allowed
    ```javascript
    
  2. Hash passwords with bcrypt:
    // ✅ Good
    const hash = await bcrypt.hash(password, 10);
    
    // ❌ Bad
    const hash = sha256(password);
    ```javascript
    
  3. Use secure token storage:
    // ✅ Good
    // Store in httpOnly cookie or secure storage
    
    // ❌ Bad
    // localStorage.setItem('token', token);
    ```javascript
    

Common Mistakes

  1. Storing passwords in plain text:
    // ❌ Bad
    user.password = password;
    
    // ✅ Good
    user.passwordHash = await bcrypt.hash(password, 10);
    ```javascript
    
  2. Exposing user existence:
    // ❌ Bad
    if (!user) return 'User not found';
    
    // ✅ Good
    return 'Invalid credentials';
    ```javascript
    
  3. Not validating tokens:
    // ❌ Bad
    const payload = jwt.decode(token);
    
    // ✅ Good
    const payload = jwt.verify(token, secret);
    

Summary

Authentication and authorization are critical. Key takeaways:

  • Hash passwords securely
  • Use JWT or sessions
  • Implement RBAC
  • Use HTTPS
  • Validate tokens
  • Implement MFA
  • Secure token storage
  • Follow best practices

Next Steps

Resources

Comments

Share this article

Scan to read on mobile