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

@sftech/nestjs-auth

v1.5.1

Published

Authentication and authorization library for NestJS applications with OAuth2/OIDC support, JWT validation, and CASL-based authorization.

Downloads

598

Readme

@sftech/nestjs-auth

Authentication and authorization library for NestJS applications with OAuth2/OIDC support, JWT validation, and CASL-based authorization.

Installation

npm install @sftech/nestjs-auth

Peer Dependencies

  • @sftech/nestjs-core >= 1.0.0
  • @sftech/nestjs-db >= 1.0.0
  • @nestjs/common
  • @casl/ability

Features

  • OAuth2/OIDC token validation
  • JWT token verification via JWKS (JSON Web Key Set)
  • CASL-based authorization
  • Auth-aware CRUD operations (extends nestjs-db)
  • User context injection throughout application layers
  • Guards and decorators for route protection

Configuration

import { NestjsAuthModule } from '@sftech/nestjs-auth';

@Module({
  imports: [
    NestjsAuthModule.register({
      authProvider: 'oauth',
      oauthValidationUrl: 'https://your-oauth-provider/userinfo',
    }),
  ],
})
export class AppModule {}

Configuration Options

Add to your app.config.json:

{
  "AUTH_PROVIDER": "oauth",
  "AUTH_OAUTH_VALIDATION_URL": "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/userinfo"
}

| Parameter | Type | Description | |-----------|------|-------------| | AUTH_PROVIDER | 'oauth' \| 'local' | Authentication provider type | | AUTH_OAUTH_VALIDATION_URL | string | OAuth userinfo endpoint for token validation |

API Reference

Application Layer

| Class | Purpose | |-------|---------| | BaseCrudAuthBundle<TDbModel> | Auth-aware CRUD bundle with user context | | AuthGetByIdUsecase | Get by ID with auth check | | AuthGetAllUsecase | Get all with auth filtering | | AuthGetByUserUsecase | Get entities for current user | | AuthInsertUsecase | Insert with user context | | AuthUpdateUsecase | Update with auth check | | AuthDeleteUsecase | Delete with auth check | | AuthBulkSaveUsecase | Bulk save with auth | | AuthOdataUsecase | OData with auth filtering |

Infrastructure Layer

| Class | Purpose | |-------|---------| | BaseAuthDbEntity | Base entity with userId field | | BaseAuthDbRepository<TDbModel, TDbEntity> | Repository with auth filtering | | BaseAuthDbMapper<TDbModel, TDbEntity> | Mapper with auth fields |

Presentation Layer

| Class | Purpose | |-------|---------| | BaseCrudAuthController | Controller with auth-aware CRUD |


Base Classes & Interfaces

BaseCrudAuthBundle

Auth-aware bundle containing all CRUD use cases with user context injection. Extends BaseCrudBundle with CASL authorization.

Generic type parameters:

  • TDbModel extends IBaseAuthDbModel - Your domain model (must include userId)

Provides out of the box:

  • All methods from BaseCrudBundle
  • getAllByUserUsecase - Get entities for current user only
  • User context automatically injected via RequestContextStorage
  • CASL ability checks on all operations

Constructor parameters:

  • _repo: IBaseAuthDbRepository<TDbModel> - Auth-aware repository
  • _abilityFactory: IBaseCaslFactory - CASL ability factory
  • _rcs: RequestContextStorage - Request context storage

Usage example:

import { BaseCrudAuthBundle, IBaseAuthDbModel } from '@sftech/nestjs-auth';
import { RequestContextStorage } from '@sftech/nestjs-core';
import { Injectable } from '@nestjs/common';

// 1. Define your domain model with userId
export class Article implements IBaseAuthDbModel {
    id?: number;
    createdAt?: Date;
    userId: string;  // Required for auth-aware entities

    title: string;
    content: string;
}

// 2. Create CASL ability factory
@Injectable()
export class ArticleAbilityFactory implements IBaseCaslFactory {
    createForUser(user: UserInfo) {
        const { can, cannot, build } = new AbilityBuilder(createMongoAbility);

        // Users can read all articles
        can('read', 'Article');

        // Users can only modify their own articles
        can(['update', 'delete'], 'Article', { userId: user.sub });

        return build();
    }
}

// 3. Create the auth bundle
@Injectable()
export class ArticleCrudAuthBundle extends BaseCrudAuthBundle<Article> {
    constructor(
        repository: ArticleRepository,
        abilityFactory: ArticleAbilityFactory,
        rcs: RequestContextStorage,
    ) {
        super(repository, abilityFactory, rcs);
    }
}

// 4. Use in controller
@Controller('articles')
@UseGuards(JwtAuthGuard)
export class ArticleController {
    constructor(private readonly articleBundle: ArticleCrudAuthBundle) {}

    @Get()
    async getAll() {
        // Returns all articles (filtered by CASL abilities)
        return this.articleBundle.getAllUsecase.execute();
    }

    @Get('my')
    async getMyArticles() {
        // Returns only current user's articles
        return this.articleBundle.getAllByUserUsecase.execute();
    }

    @Post()
    async create(@Body() dto: CreateArticleDto) {
        const article = new Article();
        article.title = dto.title;
        article.content = dto.content;
        // userId is automatically set from RequestContextStorage
        return this.articleBundle.insertUsecase.execute(article);
    }

    @Delete(':id')
    async delete(@Param('id') id: number) {
        // CASL checks if user owns this article
        return this.articleBundle.deleteUsecase.execute(id);
    }
}

BaseAuthDbRepository<TDbModel, TDbEntity>

Abstract repository extending BaseDbRepository with auth-aware filtering. Extend this for entities that belong to users.

Generic type parameters:

  • TDbModel extends IBaseAuthDbModel - Domain model with userId
  • TDbEntity extends IBaseAuthDbEntity - TypeORM entity with userId

Provides out of the box:

  • All methods from BaseDbRepository
  • getAllByUser(userId) - Get all entities for a specific user
  • getByIdAndUser(id, userId) - Get by ID with user ownership check

You must implement:

  • _mapper: IBaseAuthDbMapper<TDbModel, TDbEntity> - Auth-aware mapper

Usage example:

import { BaseAuthDbRepository, IBaseAuthDbMapper } from '@sftech/nestjs-auth';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class ArticleRepository extends BaseAuthDbRepository<Article, ArticleEntity> {
    protected readonly _mapper: IBaseAuthDbMapper<Article, ArticleEntity>;

    constructor(
        @InjectRepository(ArticleEntity)
        repo: Repository<ArticleEntity>,
        mapper: ArticleMapper,
    ) {
        super(repo);
        this._mapper = mapper;
    }

    // Custom auth-aware queries
    async findPublishedByUser(userId: string): Promise<Article[]> {
        return this.findBy({
            where: { userId, publishedAt: Not(IsNull()) },
        });
    }
}

BaseAuthDbEntity

Base TypeORM entity with userId field. Extend this for all user-owned entities.

Provides:

  • id?: number - Primary key (from BaseDbEntity)
  • createdAt?: Date - Creation timestamp (from BaseDbEntity)
  • userId: string - Owner user ID

Usage example:

import { BaseAuthDbEntity } from '@sftech/nestjs-auth';
import { Entity, Column } from 'typeorm';

@Entity('articles')
export class ArticleEntity extends BaseAuthDbEntity {
    @Column()
    title: string;

    @Column('text')
    content: string;

    @Column({ nullable: true })
    publishedAt?: Date;
}

BaseAuthDbMapper<TDbModel, TDbEntity>

Abstract mapper extending BaseDbMapper with auth field handling.

You must implement:

  • mapFromDb(from: TDbEntity, to?: TDbModel): TDbModel
  • mapToDb(from: TDbModel, to?: TDbEntity): TDbEntity

Usage example:

import { BaseAuthDbMapper } from '@sftech/nestjs-auth';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ArticleMapper extends BaseAuthDbMapper<Article, ArticleEntity> {
    mapFromDb(from: ArticleEntity, to?: Article): Article {
        const article = to ?? new Article();
        article.id = from.id;
        article.createdAt = from.createdAt;
        article.userId = from.userId;
        article.title = from.title;
        article.content = from.content;
        return article;
    }

    mapToDb(from: Article, to?: ArticleEntity): ArticleEntity {
        const entity = to ?? new ArticleEntity();
        if (from.id) entity.id = from.id;
        entity.userId = from.userId;
        entity.title = from.title;
        entity.content = from.content;
        return entity;
    }
}

BaseCrudAuthController<TDbModel, TResponseDto, TCreateDto, TUpdateDto>

Abstract controller extending BaseCrudController with authentication. Extend for auth-protected CRUD APIs.

Provides out of the box:

  • All endpoints from BaseCrudController
  • GET /my - Get current user's entities

Usage example:

import { BaseCrudAuthController } from '@sftech/nestjs-auth';
import { Controller, UseGuards } from '@nestjs/common';

@Controller('articles')
@UseGuards(JwtAuthGuard)
export class ArticleController extends BaseCrudAuthController<
    Article,
    ArticleResponseDto,
    CreateArticleDto,
    UpdateArticleDto
> {
    protected readonly _converter: ArticleDtoConverter;

    constructor(
        articleBundle: ArticleCrudAuthBundle,
        converter: ArticleDtoConverter,
    ) {
        super(articleBundle);
        this._converter = converter;
    }
}

IBaseAuthDbModel

Interface for auth-aware domain models. Implement for all user-owned models.

Required properties:

  • id?: number - Entity ID
  • createdAt?: Date - Creation timestamp
  • userId: string - Owner user ID

Usage example:

import { IBaseAuthDbModel } from '@sftech/nestjs-auth';

export class Comment implements IBaseAuthDbModel {
    id?: number;
    createdAt?: Date;
    userId: string;

    articleId: number;
    text: string;
}

IBaseCaslFactory

Interface for CASL ability factories. Implement to define authorization rules.

You must implement:

  • createForUser(user: UserInfo): MongoAbility - Create abilities for a user

Usage example:

import { IBaseCaslFactory } from '@sftech/nestjs-auth';
import { AbilityBuilder, createMongoAbility, MongoAbility } from '@casl/ability';
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppAbilityFactory implements IBaseCaslFactory {
    createForUser(user: UserInfo): MongoAbility {
        const { can, cannot, build } = new AbilityBuilder(createMongoAbility);

        if (user.roles.includes('admin')) {
            // Admins can do everything
            can('manage', 'all');
        } else {
            // Regular users
            can('read', 'Article');
            can('create', 'Article');
            can(['update', 'delete'], 'Article', { userId: user.sub });

            can('read', 'Comment');
            can('create', 'Comment');
            can(['update', 'delete'], 'Comment', { userId: user.sub });
        }

        return build();
    }
}

Complete Example

// === DOMAIN ===

// article.model.ts
import { IBaseAuthDbModel } from '@sftech/nestjs-auth';

export class Article implements IBaseAuthDbModel {
    id?: number;
    createdAt?: Date;
    userId: string;
    title: string;
    content: string;
}

// === INFRASTRUCTURE ===

// article.entity.ts
import { BaseAuthDbEntity } from '@sftech/nestjs-auth';
import { Entity, Column } from 'typeorm';

@Entity('articles')
export class ArticleEntity extends BaseAuthDbEntity {
    @Column()
    title: string;

    @Column('text')
    content: string;
}

// article.mapper.ts
import { BaseAuthDbMapper } from '@sftech/nestjs-auth';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ArticleMapper extends BaseAuthDbMapper<Article, ArticleEntity> {
    mapFromDb(from: ArticleEntity): Article {
        const a = new Article();
        a.id = from.id;
        a.createdAt = from.createdAt;
        a.userId = from.userId;
        a.title = from.title;
        a.content = from.content;
        return a;
    }

    mapToDb(from: Article, to?: ArticleEntity): ArticleEntity {
        const e = to ?? new ArticleEntity();
        if (from.id) e.id = from.id;
        e.userId = from.userId;
        e.title = from.title;
        e.content = from.content;
        return e;
    }
}

// article.repository.ts
import { BaseAuthDbRepository } from '@sftech/nestjs-auth';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class ArticleRepository extends BaseAuthDbRepository<Article, ArticleEntity> {
    protected readonly _mapper: ArticleMapper;

    constructor(
        @InjectRepository(ArticleEntity) repo: Repository<ArticleEntity>,
        mapper: ArticleMapper,
    ) {
        super(repo);
        this._mapper = mapper;
    }
}

// === APPLICATION ===

// article-ability.factory.ts
import { IBaseCaslFactory } from '@sftech/nestjs-auth';
import { AbilityBuilder, createMongoAbility } from '@casl/ability';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ArticleAbilityFactory implements IBaseCaslFactory {
    createForUser(user: UserInfo) {
        const { can, build } = new AbilityBuilder(createMongoAbility);
        can('read', 'Article');
        can(['create', 'update', 'delete'], 'Article', { userId: user.sub });
        return build();
    }
}

// article.bundle.ts
import { BaseCrudAuthBundle } from '@sftech/nestjs-auth';
import { RequestContextStorage } from '@sftech/nestjs-core';
import { Injectable } from '@nestjs/common';

@Injectable()
export class ArticleCrudAuthBundle extends BaseCrudAuthBundle<Article> {
    constructor(
        repo: ArticleRepository,
        abilityFactory: ArticleAbilityFactory,
        rcs: RequestContextStorage,
    ) {
        super(repo, abilityFactory, rcs);
    }
}

// === MODULE ===

@Module({
    imports: [TypeOrmModule.forFeature([ArticleEntity])],
    providers: [
        ArticleMapper,
        ArticleRepository,
        ArticleAbilityFactory,
        ArticleCrudAuthBundle,
    ],
    exports: [ArticleCrudAuthBundle],
})
export class ArticleModule {}

Development

# Build
npx nx build nestjs-auth

# Test
npx nx test nestjs-auth

# Lint
npx nx lint nestjs-auth

See Also

Changelog

See CHANGELOG.md

License

MIT