npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gateway-cognito-auth

v1.1.0

Published

Production-ready AWS Cognito authentication package for Node.js/Express applications

Readme

@gateway/cognito-auth

npm version License: MIT Node.js Version

Production-ready AWS Cognito authentication package for Node.js/Express applications with TypeScript support, multi-tenant capabilities, and comprehensive security features.

✨ Features

  • 🔐 Complete AWS Cognito Integration - Full authentication flow with signup, login, and token refresh
  • 🚀 Express Middleware - Drop-in JWT verification middleware for route protection
  • 🏢 Multi-Tenant Support - Custom claims extraction for SaaS applications
  • 📦 Dual Package Exports - CommonJS and ESM support with tree-shaking
  • 🔒 Production-Safe Security - Comprehensive input validation and error handling
  • 📝 Full TypeScript Support - Complete type definitions and IntelliSense
  • Performance Optimized - JWKS caching and efficient JWT verification
  • 🧪 Battle-Tested - 95%+ test coverage with property-based testing
  • 🐳 Container Ready - Docker/ECS compatible with zero external dependencies

📦 Installation

npm install @gateway/cognito-auth

Peer Dependencies

npm install express  # Required for middleware functionality

🚀 Quick Start

Basic Setup

import { CognitoAuthManager } from '@gateway/cognito-auth';

const authManager = new CognitoAuthManager({
  userPoolId: 'us-east-1_XXXXXXXXX',
  clientId: 'your-client-id',
  region: 'us-east-1'
});

// Protect routes with middleware
app.use('/api/protected', authManager.authMiddleware({ tokenUse: 'id' }));

Environment Variables Setup

# Required
COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
COGNITO_CLIENT_ID=your-client-id
AWS_REGION=us-east-1

# Optional (for app clients with secrets)
COGNITO_CLIENT_SECRET=your-client-secret
import { loadConfigFromEnv } from '@gateway/cognito-auth';

// Load configuration from environment variables
const config = loadConfigFromEnv();
const authManager = new CognitoAuthManager(config);

📚 API Documentation

CognitoAuthManager

The main authentication class that handles all Cognito operations.

Constructor

new CognitoAuthManager(config: CognitoConfig)

Parameters:

  • config.userPoolId (string) - AWS Cognito User Pool ID
  • config.clientId (string) - AWS Cognito App Client ID
  • config.clientSecret (string, optional) - App Client Secret (for confidential clients)
  • config.region (string) - AWS region where User Pool is located

Example:

const authManager = new CognitoAuthManager({
  userPoolId: 'us-east-1_XXXXXXXXX',
  clientId: 'abcdef123456',
  clientSecret: 'secret-for-confidential-clients', // Optional
  region: 'us-east-1'
});

Methods

signup(email: string, password: string): Promise<SignupResponse>

Register a new user with email and password.

try {
  const result = await authManager.signup('[email protected]', 'SecurePassword123!');
  console.log('User created with ID:', result.userSub);
} catch (error) {
  console.error('Signup failed:', error.message);
}

Returns: { userSub: string } - User's unique identifier

Throws:

  • ValidationError - Invalid email or password format
  • AuthenticationError - User already exists or Cognito service error
forgotPassword(email: string): Promise<void>

Initiate password reset flow by sending reset link to user's email.

try {
  await authManager.forgotPassword('[email protected]');
  console.log('Password reset link sent to email');
} catch (error) {
  console.error('Password reset request failed:', error.message);
}

Throws:

  • ValidationError - Invalid email format
  • AuthenticationError - User not found or service error
confirmForgotPassword(username: string, code: string, newPassword: string): Promise<void>

Confirm password reset with verification code and new password.

try {
  await authManager.confirmForgotPassword('[email protected]', '123456', 'NewSecurePassword123!');
  console.log('Password reset successful');
} catch (error) {
  console.error('Password reset failed:', error.message);
}

Throws:

  • ValidationError - Invalid input format
  • AuthenticationError - Invalid/expired code or user not found
login(email: string, password: string): Promise<AuthTokens>

Authenticate user and receive JWT tokens.

try {
  const tokens = await authManager.login('[email protected]', 'SecurePassword123!');
  console.log('Login successful:', {
    idToken: tokens.idToken,
    accessToken: tokens.accessToken,
    refreshToken: tokens.refreshToken
  });
} catch (error) {
  console.error('Login failed:', error.message);
}

Returns: AuthTokens object with idToken, accessToken, and refreshToken

Throws:

  • ValidationError - Invalid email or password format
  • AuthenticationError - Invalid credentials or user not confirmed
refreshToken(refreshToken: string): Promise<AuthTokens>

Refresh expired tokens using a valid refresh token.

try {
  const newTokens = await authManager.refreshToken(existingRefreshToken);
  console.log('Tokens refreshed successfully');
} catch (error) {
  console.error('Token refresh failed:', error.message);
}

Returns: AuthTokens object with new idToken, accessToken, and refreshToken

Throws:

  • ValidationError - Invalid refresh token format
  • AuthenticationError - Expired or invalid refresh token
authMiddleware(options: AuthMiddlewareOptions): RequestHandler

Create Express middleware for JWT authentication.

// Verify ID tokens (recommended for user authentication)
app.use('/api/user', authManager.authMiddleware({ tokenUse: 'id' }));

// Verify access tokens (for API access)
app.use('/api/data', authManager.authMiddleware({ tokenUse: 'access' }));

// Skip verification in development
app.use('/api/dev', authManager.authMiddleware({ 
  tokenUse: 'id', 
  skipVerification: process.env.NODE_ENV === 'development' 
}));

Options:

  • tokenUse ('id' | 'access') - Type of token to verify
  • skipVerification (boolean, optional) - Skip verification for development

Express Middleware

The middleware automatically:

  • Extracts Bearer tokens from Authorization headers
  • Verifies JWT signatures using cached JWKS
  • Validates token claims (issuer, audience, expiration)
  • Attaches user information to req.user
  • Returns 401 for invalid/missing tokens

Accessing User Information

import { AuthenticatedRequest } from '@gateway/cognito-auth';

app.get('/api/profile', authManager.authMiddleware({ tokenUse: 'id' }), 
  (req: AuthenticatedRequest, res) => {
    const user = req.user;
    res.json({
      userId: user.sub,
      email: user.email,
      tenantId: user.customClaims['custom:tenantId'], // Multi-tenant support
      customData: user.customClaims
    });
  }
);

Configuration Utilities

loadConfigFromEnv(env?: Record<string, string>): CognitoConfig

Load configuration from environment variables.

import { loadConfigFromEnv } from '@gateway/cognito-auth';

// Use process.env
const config = loadConfigFromEnv();

// Use custom environment object
const config = loadConfigFromEnv({
  COGNITO_USER_POOL_ID: 'us-east-1_XXXXXXXXX',
  COGNITO_CLIENT_ID: 'abcdef123456',
  AWS_REGION: 'us-east-1'
});

validateCognitoConfig(config: unknown): CognitoConfig

Validate configuration object with detailed error messages.

import { validateCognitoConfig } from '@gateway/cognito-auth';

try {
  const validConfig = validateCognitoConfig({
    userPoolId: 'us-east-1_XXXXXXXXX',
    clientId: 'abcdef123456',
    region: 'us-east-1'
  });
} catch (error) {
  console.error('Configuration error:', error.message);
}

🔧 Configuration Reference

Environment Variables

| Variable | Required | Description | Example | |----------|----------|-------------|---------| | COGNITO_USER_POOL_ID | ✅ | AWS Cognito User Pool ID | us-east-1_XXXXXXXXX | | COGNITO_CLIENT_ID | ✅ | AWS Cognito App Client ID | abcdef123456789 | | AWS_REGION | ✅ | AWS region for User Pool | us-east-1 | | COGNITO_CLIENT_SECRET | ❌ | App Client Secret (confidential clients only) | secret123... |

Configuration Object

interface CognitoConfig {
  userPoolId: string;    // Format: region_poolId
  clientId: string;      // Alphanumeric string
  clientSecret?: string; // Optional for public clients
  region: string;        // Valid AWS region
}

Middleware Options

interface AuthMiddlewareOptions {
  tokenUse: 'id' | 'access';     // Token type to verify
  skipVerification?: boolean;     // Skip verification (development only)
}

🏢 Multi-Tenant Support

The package automatically extracts custom claims from JWT tokens for multi-tenant applications:

app.get('/api/tenant-data', authManager.authMiddleware({ tokenUse: 'id' }), 
  (req: AuthenticatedRequest, res) => {
    const { customClaims } = req.user;
    
    // Access tenant-specific claims
    const tenantId = customClaims['custom:tenantId'];
    const role = customClaims['custom:role'];
    const permissions = customClaims['custom:permissions'];
    
    // Use tenant information for data isolation
    const data = await getTenantData(tenantId);
    res.json(data);
  }
);

Setting Custom Claims in Cognito

Custom claims must be set in your Cognito User Pool using Lambda triggers or Admin APIs:

// Example: Pre Token Generation Lambda trigger
exports.handler = async (event) => {
  event.response = {
    claimsOverrideDetails: {
      claimsToAddOrOverride: {
        'custom:tenantId': 'tenant-123',
        'custom:role': 'admin',
        'custom:permissions': 'read,write,delete'
      }
    }
  };
  return event;
};

🔒 Security Features

Input Validation

All inputs are validated and sanitized to prevent injection attacks:

// Email validation with sanitization
const email = validateEmail(userInput.email);

// Password strength validation
const password = validatePassword(userInput.password);

// JWT token format validation
const token = validateJWTToken(authHeader);

Error Handling

Production-safe error responses that don't expose sensitive information:

// Development: Detailed error messages
{
  "error": "ValidationError",
  "message": "Invalid email format: user@invalid",
  "code": "INVALID_EMAIL"
}

// Production: Generic error messages
{
  "error": "Authentication failed",
  "message": "Invalid credentials provided",
  "code": "AUTH_FAILED"
}

JWKS Caching

Automatic caching of JSON Web Key Sets for performance and security:

  • 5-minute TTL to balance security and performance
  • Automatic cache invalidation
  • Minimal network requests to Cognito endpoints

🧪 Testing

Running Tests

# Run all tests
npm test

# Run with coverage
npm run test:coverage

# Run LocalStack integration tests
npm run test:localstack

# Watch mode for development
npm run test:watch

Property-Based Testing

The package includes comprehensive property-based tests that verify correctness across thousands of generated inputs:

// Example: Configuration validation property test
test('Property 1: Configuration Validation Completeness', () => {
  fc.assert(fc.property(
    fc.record({
      userPoolId: fc.string(),
      clientId: fc.string(),
      region: fc.string()
    }),
    (config) => {
      // Test that valid configs pass and invalid configs fail appropriately
      const result = validateCognitoConfig(config);
      expect(result).toBeDefined();
    }
  ));
});

🐳 Docker Support

The package is fully compatible with containerized environments:

FROM node:20-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

# Environment variables for Cognito configuration
ENV COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
ENV COGNITO_CLIENT_ID=abcdef123456
ENV AWS_REGION=us-east-1

EXPOSE 3000
CMD ["npm", "start"]

Docker Compose Example

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
      - COGNITO_CLIENT_ID=abcdef123456
      - AWS_REGION=us-east-1
      - NODE_ENV=production

📋 Examples

Complete Express Application

import express from 'express';
import { CognitoAuthManager, loadConfigFromEnv, AuthenticatedRequest } from '@gateway/cognito-auth';

const app = express();
app.use(express.json());

// Load configuration from environment
const config = loadConfigFromEnv();
const authManager = new CognitoAuthManager(config);

// Public routes
app.post('/auth/signup', async (req, res) => {
  try {
    const { email, password } = req.body;
    const result = await authManager.signup(email, password);
    res.json({ success: true, userSub: result.userSub });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

app.post('/auth/forgot-password', async (req, res) => {
  try {
    const { email } = req.body;
    await authManager.forgotPassword(email);
    res.json({ success: true, message: 'Password reset link sent to email' });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

app.post('/auth/reset-password', async (req, res) => {
  try {
    const { username, code, newPassword } = req.body;
    await authManager.confirmForgotPassword(username, code, newPassword);
    res.json({ success: true, message: 'Password reset successful' });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

app.get('/reset-password', (req, res) => {
  // Serve password reset page
  res.sendFile(path.join(__dirname, 'reset-password.html'));
});

app.post('/auth/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const tokens = await authManager.login(email, password);
    res.json({ success: true, tokens });
  } catch (error) {
    res.status(401).json({ error: error.message });
  }
});

app.post('/auth/refresh', async (req, res) => {
  try {
    const { refreshToken } = req.body;
    const tokens = await authManager.refreshToken(refreshToken);
    res.json({ success: true, tokens });
  } catch (error) {
    res.status(401).json({ error: error.message });
  }
});

// Protected routes
app.use('/api', authManager.authMiddleware({ tokenUse: 'id' }));

app.get('/api/profile', (req: AuthenticatedRequest, res) => {
  res.json({
    user: req.user,
    message: 'This is a protected route'
  });
});

app.get('/api/tenant-data', (req: AuthenticatedRequest, res) => {
  const tenantId = req.user.customClaims['custom:tenantId'];
  res.json({
    tenantId,
    data: `Data for tenant ${tenantId}`,
    user: req.user.email
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Error Handling Best Practices

import { CognitoAuthError, ValidationError, AuthenticationError } from '@gateway/cognito-auth';

app.post('/auth/login', async (req, res) => {
  try {
    const tokens = await authManager.login(req.body.email, req.body.password);
    res.json({ success: true, tokens });
  } catch (error) {
    if (error instanceof ValidationError) {
      return res.status(400).json({
        error: 'Validation Error',
        message: error.message,
        code: error.code
      });
    }
    
    if (error instanceof AuthenticationError) {
      return res.status(401).json({
        error: 'Authentication Error',
        message: error.message,
        code: error.code
      });
    }
    
    // Generic error for unexpected cases
    res.status(500).json({
      error: 'Internal Server Error',
      message: 'An unexpected error occurred'
    });
  }
});

Custom Claims Processing

import { AuthenticatedRequest } from '@gateway/cognito-auth';

// Middleware to extract tenant context
function extractTenantContext(req: AuthenticatedRequest, res: Response, next: NextFunction) {
  const tenantId = req.user.customClaims['custom:tenantId'];
  
  if (!tenantId) {
    return res.status(403).json({ error: 'No tenant context found' });
  }
  
  // Add tenant context to request
  (req as any).tenantId = tenantId;
  next();
}

// Use tenant-aware middleware
app.use('/api/tenant', authManager.authMiddleware({ tokenUse: 'id' }));
app.use('/api/tenant', extractTenantContext);

app.get('/api/tenant/users', (req: any, res) => {
  const tenantId = req.tenantId;
  // Fetch users for specific tenant
  res.json({ tenantId, users: [] });
});

🔧 Development

Local Development with LocalStack

# Start LocalStack for local Cognito testing
npm run dev:setup

# Run tests against LocalStack
npm run test:localstack

# Stop LocalStack
npm run dev:stop

Building the Package

# Clean previous builds
npm run clean

# Build all formats (CommonJS, ESM, TypeScript definitions)
npm run build

# Verify build output
ls -la dist/

🤝 Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Development Setup

  1. Clone the repository
  2. Install dependencies: npm install
  3. Run tests: npm test
  4. Start LocalStack: npm run dev:setup
  5. Run integration tests: npm run test:localstack

📄 License

MIT License - see LICENSE file for details.

🔗 Links

📞 Support


Made with ❤️ by the C3Labs Team