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

@neoatalaya/auth-common

v1.0.1

Published

Common authentication modules for NestJS microservices (guards, strategies, JWT validation)

Readme

@neoatalaya/auth-common

NestJS TypeScript Passport License: EPL-2.0 Version

Plug-and-play JWT authentication infrastructure for NestJS microservices. One module registration wires up guards, strategies, and a runtime guard orchestrator — so your services share a consistent auth layer without repeating boilerplate.


Table of Contents


Overview

@neoatalaya/auth-common solves a common problem in NestJS microservice architectures: every service needs JWT auth, but implementing guards, strategies, and per-route overrides from scratch in each service leads to drift and duplicated code.

This library provides a single AuthCommonModule.forRoot() call that:

  • Registers a JwtStrategy backed by passport-jwt.
  • Provides a JwtGuard extending NestJS's AuthGuard('jwt').
  • Wires an OrchestratorGuard — a smart global guard that reads route metadata and delegates to the correct named guard at runtime.
  • Exposes clean decorators (@PublicGuard(), @UseGuards()) for per-route control.

Features

  • 🔐 JWT out of the box — strategy + guard configured from a single secret.
  • 🎯 Guard orchestration — one global guard, multiple named strategies resolved per route via metadata.
  • 🌐 Public route bypass — mark any endpoint with @PublicGuard() to skip auth entirely.
  • 🔌 Extensible — register any number of custom guards (api-key, roles, subscription) alongside JWT.
  • 🧩 Custom payload validation — inject your own validate() function to enrich or reject the decoded token payload.
  • 📦 Minimal peer dependencies — only requires the standard NestJS core packages.

Installation

npm install @neoatalaya/auth-common

Peer Dependencies

Make sure the following packages are present in your project:

npm install @nestjs/common @nestjs/core reflect-metadata rxjs

Quick Start

Import AuthCommonModule in your root AppModule using forRoot():

// app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthCommonModule, OrchestratorGuard } from '@neoatalaya/auth-common';

@Module({
  imports: [
    AuthCommonModule.forRoot({
      jwtSecret: process.env.JWT_SECRET,
    }),
  ],
  providers: [
    // Apply OrchestratorGuard globally — it handles public routes and per-route overrides automatically
    {
      provide: APP_GUARD,
      useClass: OrchestratorGuard,
    },
  ],
})
export class AppModule {}

That's it. Every route is now JWT-protected by default. Use the decorators below to opt individual routes in or out.


Module Configuration

AuthCommonModule.forRoot(options)

Returns a DynamicModule that configures and exports the entire auth infrastructure.

AuthCommonModule.forRoot<T>(options: AuthCommonOptions<T>): DynamicModule

| Parameter | Type | Required | Description | | --------- | --------------------- | -------- | ---------------------------------------- | | options | AuthCommonOptions<T> | ✅ | Configuration object — see table below. |


AuthCommonOptions

export interface AuthCommonOptions<T = any> {
  jwtSecret: string;
  defaultGuard?: string;
  guards?: Record<string, Type<CanActivate> | CanActivate>;
  validate?: (payload: T & TokenIssues) => (T & TokenIssues) | Promise<T & TokenIssues>;
}

| Property | Type | Default | Description | | -------------- | ----------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------- | | jwtSecret | string | — | Required. Secret used to verify incoming JWT tokens. | | defaultGuard | string | 'jwt' | Key of the guard the OrchestratorGuard falls back to when no @UseGuards() metadata is present on a route. | | guards | Record<string, Type<CanActivate> \| CanActivate> | {} | Named custom guards to register alongside JWT. See Custom Guards. | | validate | (payload: T & TokenIssues) => T & TokenIssues | identity| Optional function to enrich or validate the decoded token payload. See Custom Payload Validation. |


Guards

OrchestratorGuard

The central piece of the library. Designed to be registered as a global guard via APP_GUARD, it intercepts every request and decides which guard(s) to execute based on route metadata:

| Condition | Behaviour | | --------- | --------- | | Route is decorated with @PublicGuard() | Passes immediately — no guard is executed. | | Route is decorated with @UseGuards('key1', 'key2') | Executes the named guards sequentially. | | No decorator is present | Falls back to the defaultGuard configured in forRoot(). |

// main.ts — alternative: apply globally via useGlobalGuards
const app = await NestFactory.create(AppModule);
// If not using APP_GUARD provider, apply manually:
// app.useGlobalGuards(app.get(OrchestratorGuard));

Note: Using APP_GUARD is preferred because it participates in NestJS's dependency injection, allowing the guard to have its own injected services.


JwtGuard

A standard Passport-backed JWT guard. It is automatically registered by AuthCommonModule and is available under the 'jwt' key in the guard registry.

You can also inject and use it directly:

import { UseGuards } from '@nestjs/common';
import { JwtGuard } from '@neoatalaya/auth-common';

@Controller('protected')
@UseGuards(JwtGuard) // NestJS native UseGuards — bypasses the orchestrator
export class ProtectedController {}

Decorators

@PublicGuard()

Marks a route or an entire controller as publicly accessible, bypassing the OrchestratorGuard entirely.

import { Controller, Get } from '@nestjs/common';
import { PublicGuard } from '@neoatalaya/auth-common';

@Controller('auth')
export class AuthController {

  @PublicGuard()
  @Get('login')
  login() {
    // No JWT required
  }

  @Get('me')
  getProfile() {
    // JWT required (falls back to defaultGuard)
  }
}

Can also be applied at controller level to make all routes public:

@PublicGuard()
@Controller('public')
export class PublicController {}

@UseGuards()

⚠️ This is not NestJS's built-in @UseGuards(). Import it from @neoatalaya/auth-common to work with the orchestrator.

Specifies which named guard(s) the OrchestratorGuard should execute for a given route. Guards are executed sequentially — all must pass for the request to proceed.

import { Controller, Get } from '@nestjs/common';
import { UseGuards } from '@neoatalaya/auth-common';

@Controller('admin')
export class AdminController {

  @UseGuards('jwt', 'roles')
  @Get('dashboard')
  getDashboard() {
    // Both 'jwt' and 'roles' guards must pass
  }

  @UseGuards('api-key')
  @Get('webhook')
  handleWebhook() {
    // Only the 'api-key' guard is executed
  }
}

Named keys must match the keys provided in the guards option of forRoot().


Custom Payload Validation

By default, JwtStrategy returns the decoded JWT payload as-is. Provide a validate function to enrich the payload (e.g. fetch the user from a database) or reject it by throwing an exception.

// app.module.ts
import { AuthCommonModule } from '@neoatalaya/auth-common';
import { UnauthorizedException } from '@nestjs/common';

interface MyPayload {
  sub: string;
  email: string;
}

AuthCommonModule.forRoot<MyPayload>({
  jwtSecret: process.env.JWT_SECRET,
  validate: async (payload) => {
    const user = await userRepository.findOne(payload.sub);

    if (!user || !user.isActive) {
      throw new UnauthorizedException('User not found or inactive');
    }

    return { ...payload, roles: user.roles };
  },
});

The return value of validate() becomes the value attached to request.user in your controllers.


Custom Guards

Register any additional guard under a named key to make it available to @UseGuards() and OrchestratorGuard:

// api-key.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return request.headers['x-api-key'] === process.env.API_KEY;
  }
}
// app.module.ts
AuthCommonModule.forRoot({
  jwtSecret: process.env.JWT_SECRET,
  defaultGuard: 'jwt',
  guards: {
    'api-key': ApiKeyGuard,          // pass class — module instantiates it
    'custom':  new SomeGuardInstance(), // or pass an already-created instance
  },
});
// any controller
@UseGuards('api-key')
@Post('ingest')
handleIngestion() {}

Interfaces & Types

AuthCommonOptions<T>

Configuration interface for forRoot(). See Module Configuration.


TokenIssues

Standard JWT claims automatically merged into every decoded payload:

export interface TokenIssues {
  iat: number; // Issued at (Unix timestamp)
  exp: number; // Expiration (Unix timestamp)
}

Your custom payload type is always intersected with TokenIssues when received by validate().


Exceptions

FailedDependencyException

An HttpException that produces a 424 Failed Dependency response. Useful inside validate() when the auth failure is caused by a downstream dependency (e.g. user service unavailable) rather than an invalid token.

import { FailedDependencyException } from '@neoatalaya/auth-common';

validate: async (payload) => {
  const user = await userService.find(payload.sub).catch(() => {
    throw new FailedDependencyException('User service unreachable');
  });

  return { ...payload, ...user };
}

| Constructor | Default message | | --- | --- | | new FailedDependencyException() | 'Failed Dependency' | | new FailedDependencyException('Custom message') | 'Custom message' |


Token Reference

The following DI injection tokens are exported for advanced scenarios where you need to inject library internals into your own providers:

| Token | Type | Description | | ----- | ---- | ----------- | | JWT_SECRET | string | The secret passed to forRoot(). | | DEFAULT_GUARD | string | The defaultGuard key passed to forRoot(). | | GUARD_REGISTRY | Record<string, CanActivate> | Map of all registered named guards, including 'jwt'. | | IS_PUBLIC_KEY | string | Metadata key used by @PublicGuard(). | | USE_GUARDS_KEY | string | Metadata key used by @UseGuards(). |

import { Inject } from '@nestjs/common';
import { GUARD_REGISTRY } from '@neoatalaya/auth-common';

@Injectable()
export class MyService {
  constructor(
    @Inject(GUARD_REGISTRY) private registry: Record<string, CanActivate>
  ) {}
}

Development Scripts

| Command | Description | |---------|-------------| | npm run test | Run unit tests once. | | npm run test:watch | Run tests in watch mode (auto‑rerun on changes). | | npm run test:cov | Run tests with coverage report (fails if coverage < 90%). | | npm run build | Compile TypeScript to dist/ using NestJS builder. | | npm run pack | Create a .tgz archive of the package (dry‑run for publishing). | | npm run output | Print absolute path of the generated .tgz file (used after pack). | | npm run compile | Full local build pipeline: test:covbuildpackoutput. | | npm run lint | Run ESLint and auto‑fix issues. | | npm run format | Format code with Prettier. |

🐳 Docker (containerized tests)

The repository includes a Dockerfile and docker-compose.yml to run the test suite inside a consistent Node.js environment.

# Build the test image
docker compose build test

# Run unit tests inside the container
docker compose run --rm test

# Run a specific test file (example)
docker compose run --rm test npm run test -- src/common/tokens.spec.ts

Coverage reports are written at ./coverage/lcov-report/index.html directory file when using the provided compose setup.

🏃‍♂️ Testing GitHub Actions locally with act

You can simulate the exact CI/CD pipeline on your local machine using act. The repository already contains two event files under .act/:

  • push-develop.json – used to simulate a push to develop

  • push-main.json – used to simulate a push to main (including the publish job)

Install act

# On windows
choco install act-cli
# or download from https://github.com/nektos/act/releases

# List all available workflows
act -l

To run the full CI workflow (lint, test, build) for a develop branch push:

act push -e .act/push-develop.json

The push-main.json event file triggers the publish job as well (it requires a valid npm token). The token is read from the .secrets file located at the repository root.

Example .secrets file content:

NPM_TOKEN=npm_your_actual_token_here

💡 act uses Docker containers internally, so ensure Docker is installed and running. The provided event files match your GitHub Actions workflow (ci-cd.yml) exactly.

🖥️ Local compilation & packaging

The compile script is the recommended one‑shot build command:

npm run compile

It enforces the 90% coverage threshold, builds the library, creates a .tgz archive, and prints its absolute path – perfect for CI or for local installation in another project:

npm install /absolute/path/to/neoatalaya-auth-common-#.#.#.tgz

📄 License

This project is licensed under the Eclipse Public License 2.0 (EPL-2.0).
You may obtain a copy of the License at

https://www.eclipse.org/legal/epl-2.0/

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.


Made with ❤️ by NeoAtalaya