New:Socket for Asana Is Now Available.Learn more
Get Started

@edirect/auth

Package Overview
Dependencies
Maintainers
29
Versions
152
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@edirect/auth

beta
npmnpm
Version
11.0.67-beta.0
Version published
Weekly downloads
558
-3.29%
Maintainers
29
Weekly downloads
 
Created
Source

@edirect/auth

Authentication and authorization module for eDirect NestJS applications. Supports two auth providers — Keycloak (OIDC/JWT) and a custom Auth Service — with guards, middleware, decorators, and multi-tenant realm support.

Features

  • Two provider strategies: Keycloak and custom Auth Service
  • NestJS Guards and Middleware for request-level auth enforcement
  • Role, Permission, and Resource decorators for fine-grained access control
  • Token Exchange middleware for cross-service impersonation flows
  • Multi-tenant support: per-realm environment variable overrides
  • Token caching via AuthCacheService
  • JWKS-based token validation with OIDC well-known discovery
  • Type-safe request interfaces (AuthenticatedRequestInterface, UserInterface, TokenInterface)

Installation

pnpm add @edirect/auth
# or
npm install @edirect/auth

Provider Options

Option A: Keycloak

import { Module } from '@nestjs/common';
import { KeycloakAuthModule } from '@edirect/auth';

@Module({
  imports: [KeycloakAuthModule],
})
export class AppModule {}
import { Controller, Get, UseGuards } from '@nestjs/common';
import { KeycloakAuthGuard, Permissions, Roles } from '@edirect/auth';

@Controller('policies')
export class PoliciesController {
  @Get()
  @UseGuards(KeycloakAuthGuard)
  @Roles('agent', 'admin')
  @Permissions('read:policy')
  findAll() {
    return [];
  }
}

Option B: Custom Auth Service

import { Module } from '@nestjs/common';
import { AuthServiceAuthModule } from '@edirect/auth';

@Module({
  imports: [AuthServiceAuthModule],
})
export class AppModule {}
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard, Roles } from '@edirect/auth';

@Controller('quotes')
export class QuotesController {
  @Get()
  @UseGuards(AuthGuard)
  @Roles('agent')
  findAll() {
    return [];
  }
}

Decorators

DecoratorDescription
@Roles(...roles)Require one of the specified roles
@Permissions(...perms)Require one of the specified permissions
@Resources(...resources)Require access to specific resources

Middleware

For Express/NestJS middleware usage (without guards):

// Keycloak
import { KeycloakAuthMiddleware } from '@edirect/auth';

consumer
  .apply(KeycloakAuthMiddleware)
  .forRoutes({ path: '/**', method: RequestMethod.ALL });
// Token Exchange (for service-to-service delegation)
import { KeycloakAuthTokenExchangeMiddleware } from '@edirect/auth';

consumer
  .apply(KeycloakAuthTokenExchangeMiddleware)
  .forRoutes({ path: '/internal/**', method: RequestMethod.ALL });

Authenticated Request

Once authentication middleware or guard runs, req.user is populated:

import { AuthenticatedRequestInterface } from '@edirect/auth';

@Get('me')
@UseGuards(KeycloakAuthGuard)
getProfile(@Req() req: AuthenticatedRequestInterface) {
  return req.user; // UserInterface
}

Environment Variables

Keycloak

VariableDescription
KEYCLOAK_BASE_URLKeycloak server base URL
KEYCLOAK_REALMRealm name
KEYCLOAK_CLIENT_IDClient ID
KEYCLOAK_CLIENT_SECRETClient secret
KEYCLOAK_TIMEOUTRequest timeout (ms, optional)

Multi-Tenant Realm Override

For multi-tenant deployments, override per realm using the pattern KEYCLOAK_<REALM>_<VAR>:

KEYCLOAK_TH_BROKER_CLIENT_ID=my-client-th
KEYCLOAK_TH_BROKER_CLIENT_SECRET=secret-th
KEYCLOAK_TH_BROKER_BASE_URL=https://keycloak.th.example.com

Custom Auth Service

VariableDescription
AUTH_SERVICE_URLBase URL of the auth service
AUTH_SERVICE_TOKENService token for internal auth

Exports

// Modules
export {
  AuthModule,
  AuthServiceAuthModule,
  KeycloakAuthModule,
} from '@edirect/auth';

// Guards
export { AuthGuard, KeycloakAuthGuard } from '@edirect/auth';

// Middleware
export {
  AuthMiddleware,
  KeycloakAuthMiddleware,
  KeycloakAuthTokenExchangeMiddleware,
} from '@edirect/auth';

// Decorators
export { Permissions, Roles, Resources } from '@edirect/auth';

// Services
export { AuthService, ServerAuthService } from '@edirect/auth';

// Interfaces
export type {
  UserInterface,
  TokenInterface,
  EntityInterface,
  AuthenticatedRequestInterface,
  AuthServiceInterface,
  ServerAuthServiceInterface,
} from '@edirect/auth';

Design Decisions

Guard behavior on endpoints without decorators

Endpoints without @Roles(), @Permissions(), or @Resources() decorators are not validated by KeycloakAuthGuard or AuthServiceAuthGuard. The guard returns true immediately, treating the absence of decorators as an explicit signal that the endpoint does not require authorization.

Rationale: Authentication is enforced at the middleware layer (which now performs full JWT cryptographic verification via JWKS). The guard's responsibility is authorization (roles, permissions, resources), not authentication. If no authorization requirements are declared, there is nothing for the guard to enforce. Changing this behavior would be a breaking change affecting all consuming services that rely on the current opt-in model.

Authenticated endpoints without role requirements

If you need an endpoint that requires a valid token (authenticated user) but does not enforce specific roles or permissions, apply the authentication middleware without adding decorators to the route:

@Module({})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(KeycloakAuthMiddleware)
      .forRoutes('/api/profile'); // Requires valid token, no specific roles
  }
}

In this setup, the middleware rejects invalid/expired/forged tokens with 401, and the guard (if applied) passes through without additional checks. This gives you authentication without authorization.

Important: The middleware rejects requests without a token (401) regardless of decorators. If you apply the middleware broadly (e.g., to an entire controller) and have public routes in the same scope, exclude them explicitly:

consumer
  .apply(KeycloakAuthMiddleware)
  .exclude({ path: '/health', method: RequestMethod.GET })
  .forRoutes(AppController);

This is not a new behavior — the middleware has always required a Bearer token in requests it processes. The guard's return true on undecorated endpoints only applies after the middleware has already validated the token.

Future direction

In a future major version, we intend to adopt a fail-closed model with a @Public() decorator. Under that model, all endpoints will require a valid token by default, and developers must explicitly mark public endpoints with @Public(). This aligns with security best practices (NestJS docs, Spring Security @PermitAll, ASP.NET [AllowAnonymous]) and eliminates the risk of accidentally exposing endpoints without authentication.

FAQs

Package last updated on 26 Aug 2026

Related posts