Passport.js

AuthenticationNode.jsExpressMiddlewareOAuthStrategiesServer-sideVersatility

Library

Passport.js

Overview

Passport.js is authentication middleware for Node.js that provides simple and flexible authentication systems. As of 2025, it supports over 500 strategies, covering a wide range of authentication methods from local authentication to OAuth, OpenID, and SAML, making it a mature solution. With deep integration with Express.js, it provides stable authentication functionality for traditional server-side applications. Its modular design allows selecting only the necessary authentication methods to optimize application size.

Details

Passport.js 2025 is designed as middleware with the singular responsibility of authentication and can be integrated into Express-based applications. With over 500 strategies, it comprehensively supports everything from social logins like Google, Facebook, GitHub, and Twitter to enterprise authentication including LDAP, SAML, and WebAuthn. Session management integrates with external stores like Redis, establishing login sessions by setting the req.user property upon successful authentication. It also supports modern authentication technologies like passwordless authentication and Web Authentication (WebAuthn), enabling authentication flows using fingerprint and facial recognition.

Key Features

  • Rich Strategy Support: Over 500 authentication strategies covering all authentication requirements
  • Modular Design: Select only needed authentication features to optimize application size
  • Express Integration: Complete compatibility and easy implementation with Express.js applications
  • Session Management: Scalable session management with external stores like Redis
  • Custom Strategies: Extensibility to implement custom authentication logic
  • Mature Ecosystem: Over 10 years of proven track record with extensive community support

Pros and Cons

Pros

  • High stability and reliability due to mature track record with adoption by numerous companies
  • Comprehensive authentication method support through rich strategy ecosystem
  • Easy integration with existing server-side applications through complete Express.js integration
  • Modular design keeps application size minimal with only necessary dependencies
  • Flexible authentication logic implementation through custom strategies
  • Easy problem resolution through extensive documentation and community support

Cons

  • Designed for traditional server-side rendering, not optimized for modern SPAs
  • Other alternatives tend to be prioritized for modern frameworks like Next.js/React
  • Configuration can become complex, with high learning costs for beginners
  • Session-based authentication not suitable for stateless API design
  • TypeScript support requires separate type definition files, somewhat cumbersome
  • Limited standard support for modern JWT authentication and headless authentication

Reference Pages

Code Examples

Basic Setup and Installation

# Install Passport.js and Express
npm install passport express express-session

# Install required strategies
npm install passport-local          # Local authentication
npm install passport-google-oauth20 # Google OAuth
npm install passport-facebook       # Facebook authentication
npm install passport-github2        # GitHub authentication
npm install passport-jwt           # JWT authentication

# TypeScript type definitions
npm install @types/passport @types/passport-local @types/passport-google-oauth20

Basic Passport Configuration and Local Authentication

// app.ts - Basic Express + Passport setup
import express from 'express'
import passport from 'passport'
import { Strategy as LocalStrategy } from 'passport-local'
import session from 'express-session'
import bcrypt from 'bcrypt'

const app = express()

// Middleware setup
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// Session configuration
app.use(session({
  secret: process.env.SESSION_SECRET || 'your-secret-key',
  resave: false,
  saveUninitialized: false,
  cookie: { 
    secure: process.env.NODE_ENV === 'production', // HTTPS required
    maxAge: 24 * 60 * 60 * 1000 // 24 hours
  }
}))

// Passport initialization
app.use(passport.initialize())
app.use(passport.session())

// User data (use database in actual implementation)
const users = [
  {
    id: 1,
    username: 'admin',
    email: '[email protected]',
    password: '$2b$10$...' // bcrypt hashed password
  }
]

// Local authentication strategy setup
passport.use(new LocalStrategy(
  {
    usernameField: 'email', // Use email as username
    passwordField: 'password'
  },
  async (email, password, done) => {
    try {
      // Find user
      const user = users.find(u => u.email === email)
      if (!user) {
        return done(null, false, { message: 'User not found' })
      }

      // Verify password
      const isValidPassword = await bcrypt.compare(password, user.password)
      if (!isValidPassword) {
        return done(null, false, { message: 'Incorrect password' })
      }

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

// Serialization (save to session)
passport.serializeUser((user: any, done) => {
  done(null, user.id)
})

// Deserialization (restore from session)
passport.deserializeUser((id: number, done) => {
  const user = users.find(u => u.id === id)
  done(null, user)
})

// Login route
app.post('/login', 
  passport.authenticate('local', {
    successRedirect: '/dashboard',
    failureRedirect: '/login',
    failureFlash: true
  })
)

// Protect routes requiring authentication
function ensureAuthenticated(req: express.Request, res: express.Response, next: express.NextFunction) {
  if (req.isAuthenticated()) {
    return next()
  }
  res.redirect('/login')
}

app.get('/dashboard', ensureAuthenticated, (req, res) => {
  res.json({
    message: 'Dashboard',
    user: req.user
  })
})

// Logout
app.post('/logout', (req, res) => {
  req.logout((err) => {
    if (err) {
      return res.status(500).json({ error: 'Logout error' })
    }
    res.redirect('/')
  })
})

app.listen(3000, () => {
  console.log('Server started on port 3000')
})

Google OAuth 2.0 Authentication Implementation

// auth/google.ts - Google OAuth strategy
import passport from 'passport'
import { Strategy as GoogleStrategy } from 'passport-google-oauth20'

// Google OAuth strategy configuration
passport.use(new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID!,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  callbackURL: 'http://localhost:3000/auth/google/callback',
  scope: ['profile', 'email']
}, async (accessToken, refreshToken, profile, done) => {
  try {
    // Find existing user
    let user = users.find(u => u.googleId === profile.id)
    
    if (user) {
      // Existing user
      return done(null, user)
    } else {
      // Create new user
      const newUser = {
        id: users.length + 1,
        googleId: profile.id,
        username: profile.displayName,
        email: profile.emails?.[0]?.value,
        avatar: profile.photos?.[0]?.value,
        provider: 'google'
      }
      
      users.push(newUser)
      return done(null, newUser)
    }
  } catch (error) {
    return done(error, null)
  }
}))

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

// Google authentication callback
app.get('/auth/google/callback',
  passport.authenticate('google', { 
    failureRedirect: '/login',
    failureMessage: true 
  }),
  (req, res) => {
    // Success handler
    res.redirect('/dashboard')
  }
)

JWT Authentication Strategy Implementation

// auth/jwt.ts - JWT authentication strategy
import passport from 'passport'
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'
import jwt from 'jsonwebtoken'

// JWT strategy configuration
passport.use(new JwtStrategy({
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  secretOrKey: process.env.JWT_SECRET || 'your-jwt-secret',
  algorithms: ['HS256']
}, async (payload, done) => {
  try {
    // Get user ID from payload
    const user = users.find(u => u.id === payload.sub)
    
    if (user) {
      return done(null, user)
    } else {
      return done(null, false)
    }
  } catch (error) {
    return done(error, false)
  }
}))

// JWT token generation function
export function generateToken(user: any): string {
  const payload = {
    sub: user.id,
    email: user.email,
    username: user.username,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // 24 hours
  }
  
  return jwt.sign(payload, process.env.JWT_SECRET || 'your-jwt-secret')
}

// API authentication endpoint
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body
  
  try {
    // User authentication
    const user = users.find(u => u.email === email)
    if (!user) {
      return res.status(401).json({ error: 'User not found' })
    }
    
    const isValidPassword = await bcrypt.compare(password, user.password)
    if (!isValidPassword) {
      return res.status(401).json({ error: 'Incorrect password' })
    }
    
    // Generate JWT token
    const token = generateToken(user)
    
    res.json({
      message: 'Login successful',
      token,
      user: {
        id: user.id,
        username: user.username,
        email: user.email
      }
    })
  } catch (error) {
    res.status(500).json({ error: 'Server error' })
  }
})

// JWT protected route
app.get('/api/profile', 
  passport.authenticate('jwt', { session: false }),
  (req, res) => {
    res.json({
      message: 'Profile information',
      user: req.user
    })
  }
)

Multiple Strategy Combination and Routing

// auth/strategies.ts - Integrated management of multiple strategies
import passport from 'passport'
import { Strategy as LocalStrategy } from 'passport-local'
import { Strategy as GoogleStrategy } from 'passport-google-oauth20'
import { Strategy as GitHubStrategy } from 'passport-github2'
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'

// Initialize all strategies
export function initializeStrategies() {
  // Local authentication
  passport.use('local', new LocalStrategy(/* configuration */))
  
  // Google OAuth
  passport.use('google', new GoogleStrategy(/* configuration */))
  
  // GitHub OAuth
  passport.use('github', new GitHubStrategy({
    clientID: process.env.GITHUB_CLIENT_ID!,
    clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    callbackURL: 'http://localhost:3000/auth/github/callback'
  }, async (accessToken, refreshToken, profile, done) => {
    // GitHub authentication logic
  }))
  
  // JWT authentication
  passport.use('jwt', new JwtStrategy(/* configuration */))
}

// Authentication router definition
export function setupAuthRoutes(app: express.Application) {
  // Local login
  app.post('/auth/local', 
    passport.authenticate('local', { session: true }),
    (req, res) => res.json({ success: true, user: req.user })
  )
  
  // Google authentication
  app.get('/auth/google', passport.authenticate('google'))
  app.get('/auth/google/callback', 
    passport.authenticate('google'),
    (req, res) => res.redirect('/dashboard')
  )
  
  // GitHub authentication
  app.get('/auth/github', passport.authenticate('github'))
  app.get('/auth/github/callback',
    passport.authenticate('github'),
    (req, res) => res.redirect('/dashboard')
  )
  
  // API JWT authentication
  app.get('/api/user',
    passport.authenticate('jwt', { session: false }),
    (req, res) => res.json(req.user)
  )
}

// Authentication check function
export function requireAuth(...strategies: string[]) {
  return (req: express.Request, res: express.Response, next: express.NextFunction) => {
    // Try authentication with any of multiple strategies
    passport.authenticate(strategies, { session: false }, (err, user) => {
      if (err) return next(err)
      if (!user) return res.status(401).json({ error: 'Authentication required' })
      
      req.user = user
      next()
    })(req, res, next)
  }
}

// Usage example
app.get('/protected', 
  requireAuth('jwt', 'local'),
  (req, res) => {
    res.json({ message: 'Access granted', user: req.user })
  }
)

Custom Strategy and Error Handling

// auth/custom.ts - Custom strategy implementation
import passport from 'passport'
import { Strategy } from 'passport-strategy'

// Custom strategy for API key authentication
class ApiKeyStrategy extends Strategy {
  name: string = 'apikey'
  
  constructor(private verify: (apiKey: string, done: any) => void) {
    super()
  }
  
  authenticate(req: express.Request) {
    const apiKey = req.headers['x-api-key'] as string
    
    if (!apiKey) {
      return this.fail({ message: 'API key required' }, 401)
    }
    
    this.verify(apiKey, (err: any, user: any) => {
      if (err) return this.error(err)
      if (!user) return this.fail({ message: 'Invalid API key' }, 401)
      
      this.success(user)
    })
  }
}

// Register custom strategy
passport.use(new ApiKeyStrategy(async (apiKey, done) => {
  try {
    // API key validation logic
    const validApiKeys = ['abc123', 'def456', 'ghi789']
    
    if (validApiKeys.includes(apiKey)) {
      const apiUser = {
        id: 'api-user',
        type: 'api',
        apiKey: apiKey,
        permissions: ['read', 'write']
      }
      return done(null, apiUser)
    } else {
      return done(null, false)
    }
  } catch (error) {
    return done(error)
  }
}))

// Error handling
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
  console.error('Authentication error:', err)
  
  if (err.name === 'AuthenticationError') {
    return res.status(401).json({
      error: 'Authentication failed',
      message: err.message
    })
  }
  
  res.status(500).json({
    error: 'Server error',
    message: process.env.NODE_ENV === 'development' ? err.message : 'Internal error occurred'
  })
})

// Custom strategy usage example
app.get('/api/data',
  passport.authenticate('apikey', { session: false }),
  (req, res) => {
    res.json({
      message: 'API key authentication successful',
      data: 'protected data',
      user: req.user
    })
  }
)

WebAuthn (Biometric Authentication) Implementation

// auth/webauthn.ts - WebAuthn authentication implementation
import passport from 'passport'
import { Strategy as WebAuthnStrategy } from 'passport-webauthn'

// WebAuthn strategy configuration
passport.use(new WebAuthnStrategy({
  origin: 'http://localhost:3000',
  rpID: 'localhost',
  rpName: 'My App'
}, {
  register: async (user, id, publicKey, counter, transports) => {
    // Register authenticator
    const credential = {
      id: id,
      publicKey: publicKey,
      counter: counter,
      transports: transports,
      userId: user.id
    }
    
    // Save to database (implementation example)
    // await saveCredential(credential)
    
    return { verified: true }
  },
  
  authenticate: async (id, counter, cb) => {
    // Verify authenticator
    try {
      // const credential = await getCredential(id)
      // credential verification logic
      
      const user = users.find(u => u.id === 1) // Simplified
      cb(null, user, { verified: true })
    } catch (error) {
      cb(error)
    }
  }
}))

// WebAuthn registration start
app.post('/auth/webauthn/register/begin',
  passport.authenticate('session'),
  (req, res) => {
    // Generate registration options
    res.json({
      challenge: 'challenge-string',
      rp: { name: 'My App', id: 'localhost' },
      user: {
        id: req.user.id,
        name: req.user.email,
        displayName: req.user.username
      },
      pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
      timeout: 60000
    })
  }
)

// WebAuthn registration complete
app.post('/auth/webauthn/register/complete',
  passport.authenticate('webauthn'),
  (req, res) => {
    res.json({ success: true, message: 'Biometric authentication registered' })
  }
)

// WebAuthn authentication
app.post('/auth/webauthn/authenticate',
  passport.authenticate('webauthn'),
  (req, res) => {
    res.json({ 
      success: true, 
      message: 'Biometric authentication successful',
      user: req.user 
    })
  }
)