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

nestjs-jwks

v0.2.4

Published

JWT key management with automatic rotation and JWKS endpoint for NestJS applications

Downloads

127

Readme

nestjs-jwks

JWT key management with automatic rotation and JWKS endpoint for NestJS applications. Built with the JOSE library for robust cryptographic operations.

npm version License: MIT

Features

  • 🔄 Automatic Key Rotation: Keys are automatically rotated at configurable intervals
  • 🔐 Multiple Algorithms: Supports RSA, ECDSA, and EdDSA algorithms
  • 🔗 JWKS Endpoint: Provides a standard JWKS endpoint for public key discovery (RFC 7517)
  • 📁 Persistent Storage: Public keys are stored securely on the filesystem
  • 🛡️ Secure by Default: Private keys are non-extractable and kept in memory only
  • ⚙️ Simple Configuration: Easy to customize rotation intervals, expiration times, endpoints, etc.
  • 🔧 JOSE Integration: Built on the industry-standard JOSE library for reliable JWT operations

Installation

npm install nestjs-jwks
# or
yarn add nestjs-jwks
# or
pnpm add nestjs-jwks

Quick Start

1. Import the Module

import { Module } from "@nestjs/common";
import { JwksModule } from "nestjs-jwks";

@Module({
  imports: [
    JwksModule.forRoot({
      algorithm: "EdDSA",
      rotationInterval: 7 * 24 * 60 * 60 * 1000, // 7 days
      expirationTime: 28 * 24 * 60 * 60 * 1000, // 28 days
    }),
  ],
})
export class AppModule {}

⚠️ Important: This module is global by design. Import JwksModule.forRoot() or JwksModule.forRootAsync() only once in your root module (typically AppModule). The JwksService will then be available for injection throughout your entire application without needing to import the module in other feature modules.

💡 Note: For dynamic configuration (e.g., using ConfigService), use JwksModule.forRootAsync(). Note that only service configuration can be resolved asynchronously – controller configuration must always be provided synchronously as NestJS requires route information at module initialization. See the Configuration section below for async configuration examples.

2. Use the Service with JOSE

import * as jose from "jose";
import { Injectable } from "@nestjs/common";
import { JwksService } from "nestjs-jwks";

@Injectable()
export class AuthService {
  constructor(private readonly jwksService: JwksService) {}

  async signToken(payload: any): Promise<string> {
    // Use JOSE library with the managed keys
    return await new jose.SignJWT(payload)
      .setProtectedHeader({
        alg: this.jwksService.alg,
        kid: this.jwksService.kid,
      })
      .setIssuedAt()
      .setExpirationTime("1h")
      .sign(this.jwksService.privateKey);
  }

  async verifyToken(token: string): Promise<any> {
    // JOSE library handles key resolution automatically
    const { payload } = await jose.jwtVerify(token, this.jwksService.getKey);
    return payload;
  }
}

3. Access JWKS Endpoint

The module automatically creates a JWKS endpoint at:

GET /.well-known/jwks.json

Configuration

Options Interfaces

The module options are split into two parts: service options (which can be resolved asynchronously) and controller options (which must be provided synchronously). For sync registration, both are combined in JwksModuleOptions, while for async registration, JwksModuleAsyncOptions resolves service options through a factory while keeping controller options direct. This separation enables dynamic service options while keeping routing paths static as required by NestJS.

Service Options

Service options control the cryptographic behavior and key management. These options can be resolved asynchronously when using forRootAsync().

interface JwksServiceOptions {
  algorithm?:
    | "Ed25519"
    | "EdDSA"
    | "ES256"
    | "ES384"
    | "ES512"
    | "PS256"
    | "PS384"
    | "PS512"
    | "RS256"
    | "RS384"
    | "RS512";
  modulusLength?: number;
  rotationInterval?: number;
  expirationTime?: number;
  keysDirectory?: string;
}

Controller Options

Controller options define the HTTP endpoint configuration. These options must always be provided synchronously, as NestJS requires route paths to be known at module initialization time.

interface JwksControllerOptions {
  path?: string;
  endpoint?: string;
  headers?: Record<string, string>;
}

Module Options

JwksModuleOptions combines both service and controller options for synchronous registration with forRoot(). JwksModuleAsyncOptions separates them, allowing service options to be resolved through a factory while controller options remain direct.

interface JwksModuleOptions extends JwksServiceOptions {
  controller?: JwksControllerOptions;
}
interface JwksModuleAsyncOptions extends Pick<ModuleMetadata, "imports"> {
  useFactory: (
    ...args: any[]
  ) => JwksServiceOptions | Promise<JwksServiceOptions>; // Factory returns only service options
  inject?: any[]; // Dependencies to inject into factory
  controller?: JwksControllerOptions; // Provided directly (synchronously)
}

Configuration Reference

| Option | Default Value | Description | | --------------------- | ---------------------------------------- | ------------------------------------------------------------------------ | | algorithm | 'EdDSA' | Cryptographic algorithm | | modulusLength | 2048 | RSA key length in bits (RSA algorithms only) | | rotationInterval | 604800000 (7d) | Interval between automatic key rotations (in milliseconds) | | expirationTime | 2419200000 (28d) | Time until keys are removed from JWKS (in milliseconds) | | keysDirectory | './keys' | Directory to store keys (relative to process.cwd()) | | controller.path | '.well-known' | Controller base path | | controller.endpoint | 'jwks.json' | JWKS endpoint | | controller.headers | { "Content-Type": "application/json" } | HTTP headers for JWKS response (custom headers are merged with defaults) |

Usage Examples

Here are two common ways to configure the JWKS module in your NestJS application. In both examples, the module creates a JWKS endpoint at: GET /auth/keys

Synchronous Configuration

JwksModule.forRoot({
  algorithm: "RS256",
  modulusLength: 4096,
  rotationInterval: 24 * 60 * 60 * 1000, // 1 day
  expirationTime: 7 * 24 * 60 * 60 * 1000, // 7 days
  keysDirectory: "./secure-keys",
  controller: {
    path: "auth",
    endpoint: "keys",
    headers: {
      "Cache-Control": "public, max-age=3600",
    },
  },
});

Asynchronous Configuration

import { ConfigModule, ConfigService } from "@nestjs/config";

@Module({
  imports: [
    ConfigModule.forRoot(),
    JwksModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) => ({
        // 🔄 ASYNC: Service options loaded from environment variables via ConfigService
        algorithm: configService.get("JWKS_ALGORITHM"),
        modulusLength: configService.get<number>("JWKS_MODULUS_LENGTH"),
        rotationInterval: configService.get<number>("JWKS_ROTATION_INTERVAL"),
        expirationTime: configService.get<number>("JWKS_EXPIRATION_TIME"),
        keysDirectory: configService.get("JWKS_KEYS_DIRECTORY"),
      }),
      // ⚡ SYNC: Controller options provided directly (or using process.env directly)
      controller: {
        path: "auth",
        endpoint: "keys",
        headers: {
          "Cache-Control": "public, max-age=3600",
        },
      },
    }),
  ],
})
export class AppModule {}

Supported Algorithms

EdDSA (Recommended)

  • EdDSA / Ed25519 – Edwards-curve Digital Signature Algorithm

ECDSA

  • ES256 – ECDSA using P-256 and SHA-256
  • ES384 – ECDSA using P-384 and SHA-384
  • ES512 – ECDSA using P-521 and SHA-512

RSA

  • RS256 – RSASSA-PKCS1-v1_5 using SHA-256
  • RS384 – RSASSA-PKCS1-v1_5 using SHA-384
  • RS512 – RSASSA-PKCS1-v1_5 using SHA-512
  • PS256 – RSASSA-PSS using SHA-256
  • PS384 – RSASSA-PSS using SHA-384
  • PS512 – RSASSA-PSS using SHA-512

Note: RSA algorithms use the modulusLength option. The key length must be at least 2048 bits.

Key Management

The module automatically manages cryptographic keys through a complete lifecycle with persistent storage to ensure continuity across server restarts.

Key States

  1. Active: The current key used for signing new tokens
  2. Deprecated: Previous keys still valid for token verification
  3. Expired: Keys that are no longer valid and removed from JWKS

Key Storage

  • Public keys are stored as PEM files on disk
  • Private keys are non-extractable: they cannot be exported from memory, ensuring maximum security
  • On startup, key rotation is triggered to generate a fresh active private key
  • Non-expired public keys remain available for token verification after restarts
  • The complete lifecycle of keys is stored in metadata.json

Timeline Example

| Day | Key A | Key B | Key C | Key D | Key E | | --- | ---------- | ---------- | ---------- | ---------- | ---------- | | 0 | Active | | | | | | 7 | Deprecated | Active | | | | | 14 | Deprecated | Deprecated | Active | | | | 21 | Deprecated | Deprecated | Deprecated | Active | | | 28 | Expired | Deprecated | Deprecated | Deprecated | Active | | 35 | Expired | Expired | Deprecated | Deprecated | Deprecated | | 42 | Expired | Expired | Expired | Deprecated | Deprecated |

Legend

  • Active: Current signing key
  • Deprecated: Available for verification only
  • Expired: Removed from JWKS

Keys Directory Structure

keys/
├── metadata.json   # Keys metadata and lifecycle info
├── <uuid-1>.pem    # Public key file
├── <uuid-2>.pem    # Public key file
└── ...

API Reference

JwksService

Properties

  • alg: string – Algorithm used
  • kid: string – Current active key ID
  • privateKey: jose.CryptoKey – Current private key for signing
  • jwks: jose.JSONWebKeySet – The public JWKS object
  • getKey: Function – Key resolver function for verification

Methods

  • rotateKeys(): Promise<void> – Manually trigger key rotation (generates new active key and deprecates current one)
  • revokeKey(kid?: string): Promise<void> – Revoke a specific key or current active key (triggers rotation if active key is revoked)
  • revokeAllKeys(): Promise<void> – Revoke all non-expired keys (triggers rotation)
  • purgeKeyFiles(): void – Delete key files for expired/revoked keys (keeps metadata for audit)

The service automatically manages key rotation on startup and at configured intervals. Manual operations are available for security incidents or administrative needs.

Contributing

Contributions are welcome! Please feel free to submit Issues and Pull Requests.

License

This project is licensed under the MIT License – see the LICENSE file for details.


Related

  • NestJS – A progressive Node.js framework
  • JOSE – JavaScript implementation of JSON Web Signature and Encryption
  • RFC 7517 – JSON Web Key (JWK) specification