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

@zola_do/authorization

v0.2.8

Published

JWT auth, guards, strategies for NestJS

Readme

@zola_do/authorization

npm version npm downloads License: ISC

JWT authentication, API key validation, guards, and strategies for NestJS applications.

Overview

@zola_do/authorization provides a complete authentication and authorization solution:

  • JWT Guards — Bearer token authentication
  • Permission Guards — Role-based access control (RBAC)
  • API Key Guard — External API authentication
  • Strategies — Passport JWT strategies (access + refresh tokens)
  • Decorators@CurrentUser(), @AllowAnonymous()
  • Auth Helper — Token generation, password hashing, OTP

By default, JwtGuard is registered as a global guard, protecting all routes.

Installation

# Install individually
npm install @zola_do/authorization

# Or via meta package
npm install @zola_do/nestjs-shared

Dependencies

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt jsonwebtoken

Optional Dependencies

# For ThrottlerBehindProxyGuard
npm install @nestjs/throttler

Quick Start

1. Configure Environment

# .env
JWT_ACCESS_TOKEN_SECRET=your-access-token-secret-min-32-chars
JWT_ACCESS_TOKEN_EXPIRES=15m
JWT_REFRESH_TOKEN_SECRET=your-refresh-token-secret-min-32-chars
JWT_REFRESH_TOKEN_EXPIRES=7d

2. Register Module

import { Module } from "@nestjs/common";
import { AuthorizationModule } from "@zola_do/authorization";

@Module({
  imports: [AuthorizationModule],
})
export class AppModule {}

3. Access User in Controller

import { Controller, Get } from "@nestjs/common";
import { CurrentUser, JwtGuard, UseGuards } from "@zola_do/authorization";

@Controller("profile")
export class ProfileController {
  @Get()
  @UseGuards(JwtGuard) // Optional - global guard is active by default
  getProfile(@CurrentUser() user: any) {
    return {
      id: user.id,
      email: user.email,
      organization: user.organization,
    };
  }
}

JWT Flow

┌─────────────────────────────────────────────────────────────────────┐
│                         Login Flow                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Client          Backend                    JWT Payload              │
│    │                │                            │                   │
│    │  POST /login   │                            │                   │
│    │  {email,pass}  │                            │                   │
│    │───────────────>│                            │                   │
│    │                │                            │                   │
│    │         ┌─────▼─────┐                      │                   │
│    │         │ Validate   │                      │                   │
│    │         │ credentials│                      │                   │
│    │         └─────┬─────┘                      │                   │
│    │               │                             │                   │
│    │         ┌─────▼───────────────┐             │                   │
│    │         │ Generate tokens:    │             │                   │
│    │         │ - accessToken       │─────────────┼─────────────────>│
│    │         │ - refreshToken      │             │ {                 │
│    │         └────────────────────┘             │   sub: userId,    │
│    │                │                            │   email,          │
│    │                │                            │   organization,  │
│    │                │                            │   permissions     │
│    │<───────────────│                            │ }                 │
│    │  { accessToken,│                                               │
│    │    refreshToken│                                               │
│    │  }             │                                               │
│    │                │                                               │
└────┼────────────────┼───────────────────────────────────────────────┘
     │                │
     │                │
     ▼                ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    Authenticated Request                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  Client          Backend                    JwtGuard                 │
│    │                │                            │                   │
│    │  GET /profile  │                            │                   │
│    │  Authorization:│                            │                   │
│    │  Bearer <token>│                            │                   │
│    │───────────────>│                            │                   │
│    │                │                            │                   │
│    │                │  ┌────▼─────┐              │                   │
│    │                │  │ Extract  │              │                   │
│    │                │  │ token    │              │                   │
│    │                │  └────┬─────┘              │                   │
│    │                │       │                    │                   │
│    │                │  ┌────▼────────────┐       │                   │
│    │                │  │ Verify with     │       │                   │
│    │                │  │ JWT_ACCESS_     │       │                   │
│    │                │  │ TOKEN_SECRET    │       │                   │
│    │                │  └────┬────────────┘       │                   │
│    │                │       │                    │                   │
│    │                │       │ set request.user   │                   │
│    │                │       ▼                    │                   │
│    │                │  req.user = payload        │                   │
│    │                │                            │                   │
│    │<───────────────│  Controller runs           │                   │
│    │  { user data } │  @CurrentUser() = user    │                   │
│    │                │                            │                   │
└────┴────────────────┴───────────────────────────────────────────────┘

Guards

JwtGuard

Default global guard that validates Bearer tokens:

@UseGuards(JwtGuard)
@Controller("protected")
export class ProtectedController {}

OptionalJwtGuard

Allows anonymous access but extracts user if token is present:

@UseGuards(OptionalJwtGuard)
@Controller("optional")
export class OptionalController {
  @Get()
  getData(@CurrentUser() user?: any) {
    if (user) {
      return { message: "Authenticated", user };
    }
    return { message: "Anonymous" };
  }
}

PermissionsGuard

Requires specific permissions. Supports pipe-separated OR logic:

@UseGuards(JwtGuard, PermissionsGuard("product:create"))
@Controller("products")
export class ProductsController {
  @Post()
  createProduct() {
    // Requires 'product:create' permission
  }

  @Patch(":id")
  @UseGuards(PermissionsGuard("product:update|product:manage"))
  updateProduct() {
    // Requires 'product:update' OR 'product:manage'
  }
}

ApiKeyGuard

Validates X-API-Key header against process.env.API_KEY:

@UseGuards(ApiKeyGuard)
@Controller("external")
export class ExternalController {}

VendorGuard

Checks vendor registration status (custom implementation required):

@UseGuards(JwtGuard, VendorGuard())
@Controller("vendor")
export class VendorController {}

Decorators

@CurrentUser()

Extract user from request:

@Get('profile')
getProfile(@CurrentUser() user: UserPayload) {
  return user;
}

// With property extraction
@Get('profile')
getProfile(@CurrentUser('email') email: string) {
  return { email };
}

@AllowAnonymous()

Skip authentication for specific routes:

@Get('public')
@AllowAnonymous()
getPublicData() {
  return { message: 'Public data' };
}

JWT Payload

Default payload structure:

interface JwtPayload {
  sub: string; // User ID
  email: string; // User email
  organization?: {
    // Organization context
    organizationId: string;
    organizationName: string;
  };
  permissions?: string[]; // User permissions
  iat?: number; // Issued at
  exp?: number; // Expiration
}

AuthHelper

Utility service for token operations:

import { AuthHelper } from "@zola_do/authorization";

@Injectable()
export class AuthService {
  constructor(private readonly authHelper: AuthHelper) {}

  async login(user: User) {
    // Generate tokens
    const tokens = this.authHelper.generateTokens(user);

    // Hash password
    const hashedPassword = await this.authHelper.hashPassword(plainPassword);

    // Verify password
    const isValid = await this.authHelper.verifyPassword(
      plainPassword,
      hashedPassword,
    );

    // Generate OTP
    const otp = this.authHelper.generateOtp();

    return { tokens, hashedPassword, isValid, otp };
  }

  async refreshTokens(refreshToken: string) {
    return this.authHelper.refreshTokens(refreshToken);
  }
}

AuthHelper Methods

| Method | Description | Returns | | -------------------------------- | ------------------------------ | ------------------------------- | | generateTokens(user) | Create access + refresh tokens | { accessToken, refreshToken } | | refreshTokens(refreshToken) | Refresh using refresh token | { accessToken, refreshToken } | | hashPassword(password) | bcrypt hash | string | | verifyPassword(password, hash) | Compare password to hash | boolean | | generateOtp(length) | Generate numeric OTP | string | | decodeToken(token) | Decode without verification | JwtPayload |

Token Generation Example

import { AuthHelper } from "@zola_do/authorization";
import { JwtPayload } from "@zola_do/authorization/helper";

@Controller("auth")
export class AuthController {
  constructor(private readonly authHelper: AuthHelper) {}

  @Post("login")
  async login(@Body() dto: LoginDto) {
    const user = await this.userService.validateUser(dto);

    const payload: JwtPayload = {
      sub: user.id,
      email: user.email,
      organization: {
        organizationId: user.organizationId,
        organizationName: user.organization.name,
      },
      permissions: user.permissions,
    };

    return this.authHelper.generateTokens(payload);
  }

  @Post("refresh")
  async refresh(@Body("refreshToken") refreshToken: string) {
    return this.authHelper.refreshTokens(refreshToken);
  }
}

Custom JwtGuard Override

Override the default JWT validation logic:

import { Injectable, ExecutionContext } from "@nestjs/common";
import { JwtGuard } from "@zola_do/authorization";

@Injectable()
export class AppJwtGuard extends JwtGuard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    // Run default validation
    const canActivate = await super.canActivate(context);
    if (!canActivate) return false;

    // Add custom logic
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    // Check additional conditions
    if (user.status === "suspended") {
      throw new ForbiddenException("Account suspended");
    }

    return true;
  }
}

Register in module:

import { APP_GUARD } from "@nestjs/core";
import { AuthorizationModule, JwtGuard } from "@zola_do/authorization";

@Module({
  imports: [AuthorizationModule],
  providers: [
    AppJwtGuard,
    { provide: JwtGuard, useExisting: AppJwtGuard },
    { provide: APP_GUARD, useExisting: AppJwtGuard },
  ],
})
export class AppModule {}

Strategies

JwtStrategy

Passport strategy for access token validation:

import { JwtStrategy } from "@zola_do/authorization";

// Already registered by AuthorizationModule
// Customization via override (see above)

JwtRefreshTokenStrategy

Passport strategy for refresh token validation:

import { JwtRefreshTokenStrategy } from "@zola_do/authorization";

// Used for token refresh endpoints
// Validates against JWT_REFRESH_TOKEN_SECRET

Environment Variables

| Variable | Description | Default | | --------------------------- | ---------------------------- | -------- | | JWT_ACCESS_TOKEN_SECRET | Access token signing secret | Required | | JWT_ACCESS_TOKEN_EXPIRES | Access token TTL | 15m | | JWT_REFRESH_TOKEN_SECRET | Refresh token signing secret | Required | | JWT_REFRESH_TOKEN_EXPIRES | Refresh token TTL | 7d | | API_KEY | API key for ApiKeyGuard | Optional |

Token Expiration Format

15m    - 15 minutes
1h     - 1 hour
7d     - 7 days
30d    - 30 days

Permission System

Permissions use pipe-separated OR logic:

// Require any of these permissions
@UseGuards(PermissionsGuard('admin:write|manager:write|product:create'))

Common Permission Patterns

| Pattern | Description | | ----------------- | ----------------------------------------- | | resource:action | Basic CRUD (product:create, product:read) | | resource:* | All actions on resource | | *:read | Read all resources | | admin:* | Admin access |

Recommended Imports

Subpath imports are recommended for tree-shaking:

import { AuthorizationModule } from "@zola_do/authorization/module";
import {
  JwtGuard,
  PermissionsGuard,
  ApiKeyGuard,
} from "@zola_do/authorization/guards";
import { AllowAnonymous, CurrentUser } from "@zola_do/authorization/decorators";
import { AuthHelper } from "@zola_do/authorization/helper";
import {
  JwtStrategy,
  JwtRefreshTokenStrategy,
} from "@zola_do/authorization/strategy";

Root import is supported for backward compatibility:

import {
  AuthorizationModule,
  JwtGuard,
  AllowAnonymous,
  AuthHelper,
} from "@zola_do/authorization";

Optional Features

ThrottlerBehindProxyGuard

Rate limiting guard for applications behind a reverse proxy:

import { ThrottlerBehindProxyGuard } from "@zola_do/authorization/optional/throttler";

@UseGuards(ThrottlerBehindProxyGuard)
@Controller("api")
export class ApiController {}

Requires @nestjs/throttler peer dependency.

API Reference

Guards

| Guard | Description | Auth Required | | ------------------------------- | ----------------------- | -------------------- | | JwtGuard | Bearer token validation | Yes (global default) | | OptionalJwtGuard | Bearer token if present | No | | PermissionsGuard(permissions) | Permission check | Yes | | ApiKeyGuard | API key validation | Yes | | VendorGuard() | Vendor status check | Yes | | ThrottlerBehindProxyGuard | Rate limiting | Configurable |

Decorators

| Decorator | Description | | ------------------------ | ----------------------------- | | @CurrentUser() | Get current user from request | | @CurrentUser(property) | Get specific property | | @AllowAnonymous() | Skip authentication |

Strategies

| Strategy | Token Type | Secret | | ------------------------- | ---------- | -------------------------- | | JwtStrategy | Access | JWT_ACCESS_TOKEN_SECRET | | JwtRefreshTokenStrategy | Refresh | JWT_REFRESH_TOKEN_SECRET |

AuthHelper

class AuthHelper {
  generateTokens(payload: JwtPayload): {
    accessToken: string;
    refreshToken: string;
  };
  refreshTokens(
    refreshToken: string,
  ): Promise<{ accessToken: string; refreshToken: string }>;
  hashPassword(password: string): Promise<string>;
  verifyPassword(password: string, hashedPassword: string): Promise<boolean>;
  generateOtp(length?: number): string;
  decodeToken(token: string): JwtPayload;
}

Troubleshooting

Q: Token expired errors?

Check JWT_ACCESS_TOKEN_EXPIRES environment variable and ensure clocks are synchronized.

Q: User is undefined in @CurrentUser()?

Ensure JwtGuard is active and token is valid:

@UseGuards(JwtGuard) // Add explicit guard if global is not configured

Q: Custom token payload?

Override JwtStrategy and JwtGuard to implement custom validation.

Q: Skip guard for specific routes?

@AllowAnonymous() // Works even with global JwtGuard

Related Packages

License

ISC

Community