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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@verisure-italy/express-authentication-middleware

v1.7.2

Published

Express middleware for Verisure authentication

Downloads

782

Readme

Express Authentication Middleware

A TypeScript Express middleware for authentication using DynamoDB with support for Bearer token validation and user lookup.

Features

  • 🔐 Bearer Token Authentication: Extracts and validates Bearer tokens from Authorization headers
  • 🗄️ DynamoDB Integration: Uses dynamo-kit for efficient database operations
  • 👤 User Lookup: Automatic user retrieval after token validation
  • Token Expiration: Built-in token expiration validation
  • 🎯 Type Safety: Full TypeScript support with AAA types
  • 🔄 Backward Compatibility: Supports both DynamoDB and HTTP-based authentication

Installation

npm install @verisure-italy/express-authentication-middleware
# or
pnpm add @verisure-italy/express-authentication-middleware

Quick Start

import express from 'express'
import { dynamoAuthMiddleware } from '@verisure-italy/express-authentication-middleware'

const app = express()

// Configure the middleware
app.use(dynamoAuthMiddleware({
  tokenTableName: 'access_token',
  userTableName: 'user',
  dynamoConfig: {
    region: 'eu-west-1'
  }
}))

// Protected route
app.get('/protected', (req, res) => {
  if (req._auth) {
    res.json({ message: `Hello ${req._auth.user.username}`, user: req._auth.user })
  } else {
    res.status(401).json({ error: 'Authentication required' })
  }
})

Configuration

DynamoDB Authentication

import { dynamoAuthMiddleware } from '@verisure-italy/express-authentication-middleware'

app.use(dynamoAuthMiddleware({
  // Required: Table names
  tokenTableName: 'access_token',    // Table containing access tokens
  userTableName: 'user',             // Table containing user data
  
  // Optional: DynamoDB configuration
  dynamoConfig: {
    region: 'eu-west-1',
    endpoint: 'http://localhost:8000', // For local development
    tablePrefix: 'dev'                 // Table prefix
  }
}))

Environment Variables

The middleware automatically reads these environment variables:

  • AWS_REGION: Default AWS region
  • DYNAMO_ENDPOINT: Custom DynamoDB endpoint
  • DYNAMO_TABLE_PREFIX: Table name prefix
  • IS_OFFLINE / AWS_SAM_LOCAL: Auto-detects local development

Database Schema

Access Token Table

Uses the AccessToken type from @verisure-italy/aaa-types:

Required fields:

  • id (String): Token record ID
  • token (String): The access token value
  • user (String): Username associated with the token
  • expires (Number): Token expiration timestamp in seconds
  • scope (String, optional): Token scope
  • client (Object): Client information with id field

Required indexes:

  • token-index: GSI with token as hash key

Example record:

{
  "id": "token-123",
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": "john.doe",
  "expires": 1697234567,
  "scope": "read write",
  "client": { "id": "client-app" },
  "createdAt": 1697148167,
  "updatedAt": 1697148167
}

User Table

Uses the UserDetails type from @verisure-italy/aaa-types:

Required fields:

  • id (String): User ID
  • username (String): Username
  • roles (Array): User roles
  • createdAt (Number): Creation timestamp
  • updatedAt (Number): Last update timestamp

Required indexes:

  • username-index: GSI with username as hash key

Example record:

{
  "id": "user-123",
  "username": "john.doe",
  "roles": ["ROLE_USER", "ROLE_ADMIN"],
  "createdAt": 1697148167,
  "updatedAt": 1697148167
}

Authentication Flow

  1. Extract Token: Reads Authorization: Bearer <token> header
  2. Token Lookup: Queries access_token table using token-index
  3. Expiration Check: Validates token hasn't expired
  4. User Lookup: Queries user table using username-index
  5. Request Augmentation: Adds authentication info to req._auth

Request Augmentation

After successful authentication, the middleware adds:

interface Request {
  _auth?: {
    user: UserDetails
    token: {
      accessToken: string
      accessTokenExpiresAt: string
      scope?: string
    }
  }
}

Note: Only _auth is added to the request. This follows the pattern of prefixing middleware properties with _ to avoid conflicts.

Usage Examples

Basic Protected Route

app.get('/profile', (req, res) => {
  if (!req._auth) {
    return res.status(401).json({ error: 'Authentication required' })
  }
  
  res.json({
    id: req._auth.user.id,
    username: req._auth.user.username,
    roles: req._auth.user.roles
  })
})

Role-Based Authorization

const requireRole = (role: string) => (req: Request, res: Response, next: NextFunction) => {
  if (!req._auth) {
    return res.status(401).json({ error: 'Authentication required' })
  }
  
  if (!req._auth.user.roles.includes(role)) {
    return res.status(403).json({ error: 'Insufficient permissions' })
  }
  
  next()
}

app.get('/admin', requireRole('ROLE_ADMIN'), (req, res) => {
  res.json({ message: 'Admin access granted', user: req._auth!.user })
})

Access Token Information

app.get('/token-info', (req, res) => {
  if (!req._auth) {
    return res.status(401).json({ error: 'Authentication required' })
  }
  
  res.json({
    token: {
      expiresAt: req._auth.token.accessTokenExpiresAt,
      scope: req._auth.token.scope
    },
    user: req._auth.user
  })
})

Local Development

// Automatic local detection
app.use(dynamoAuthMiddleware({
  tokenTableName: 'access_token',
  userTableName: 'user'
  // Will automatically use localhost:8000 if IS_OFFLINE=true
}))

// Manual local configuration
app.use(dynamoAuthMiddleware({
  tokenTableName: 'access_token',
  userTableName: 'user',
  dynamoConfig: {
    endpoint: 'http://localhost:8000',
    region: 'local'
  }
}))

Multi-Environment Setup

const getAuthMiddleware = () => {
  const environment = process.env.NODE_ENV || 'development'
  
  const config = {
    tokenTableName: 'access_token',
    userTableName: 'user',
    dynamoConfig: {
      region: 'eu-west-1',
      tablePrefix: environment
    }
  }
  
  if (environment === 'development') {
    config.dynamoConfig.endpoint = 'http://localhost:8000'
  }
  
  return dynamoAuthMiddleware(config)
}

app.use(getAuthMiddleware())

Error Handling

The middleware handles various error scenarios:

Authentication Errors (401)

  • Invalid token: Token not found in database
  • Token expired: Token expiration time has passed
  • User not found: User no longer exists in database

Error Handling Example

app.use((err: any, req: Request, res: Response, next: NextFunction) => {
  if (err.statusCode === 401) {
    return res.status(401).json({ 
      error: 'Authentication failed', 
      message: err.message 
    })
  }
  
  // Handle other errors
  next(err)
})

Backward Compatibility

The middleware maintains backward compatibility with HTTP-based authentication:

import { middleware } from '@verisure-italy/express-authentication-middleware'

// HTTP-based authentication (legacy)
app.use(middleware({
  tokenUrl: 'https://auth.example.com/token/info'
}))

// DynamoDB authentication (new)
app.use(middleware({
  tokenTableName: 'access_token',
  userTableName: 'user'
}))

TypeScript Support

Full TypeScript support with proper type definitions:

import { Request, Response } from 'express'
import { UserDetails } from '@verisure-italy/aaa-types'

app.get('/profile', (req: Request, res: Response) => {
  // req._auth is properly typed
  if (req._auth) {
    // TypeScript knows user properties are available
    console.log(req._auth.user.username, req._auth.user.roles)
    console.log(req._auth.token.accessTokenExpiresAt)
  }
})

Performance Considerations

  • Client Caching: DynamoDB client is cached for optimal performance
  • Index Usage: Efficient GSI queries for token and user lookup
  • Minimal Data Transfer: Fetches only required fields
  • Connection Reuse: Single client instance per configuration

AAA Types Integration

The middleware uses official AAA types:

  • AccessToken: From @verisure-italy/aaa-types for token records
  • UserDetails: From @verisure-italy/aaa-types for user information

This ensures consistency across the authentication system and automatic compatibility with AAA service schemas.

Security Notes

  • Tokens are validated against database records
  • Automatic expiration checking prevents stale token usage
  • User existence is verified on each request
  • No sensitive data is logged or exposed
  • Uses timestamp-based expiration (expires field)

Testing

import request from 'supertest'
import express from 'express'

const app = express()
app.use(dynamoAuthMiddleware({ /* config */ }))

describe('Authentication', () => {
  it('should require valid token', async () => {
    const response = await request(app)
      .get('/protected')
      .expect(401)
  })
  
  it('should accept valid token', async () => {
    const response = await request(app)
      .get('/protected')
      .set('Authorization', 'Bearer valid-token')
      .expect(200)
      
    expect(response.body._auth).toBeDefined()
  })
})