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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@science-and-humans/science-and-humans-auth-sdk

v2.0.1

Published

Authentication sdk for Science and Humans microservices with hybrid role system support

Downloads

195

Readme

Auth Middleware SDK

A lightweight, production-ready authentication middleware for NestJS applications with Clerk integration and Redis fallback support.

System Design

Auth Design

Auth Microservice Architecture

SDK Design

SDK Design

Features

  • 🔐 Clerk Authentication: Full integration with Clerk authentication service
  • 🚀 NestJS Native: Built specifically for NestJS with guards, decorators, and modules
  • Express Support: Drop-in middleware and helpers for Express apps
  • 🔄 Redis Fallback: Automatic fallback from Redis to HTTP when microservice is unavailable
  • 🛡️ Smart Error Handling: Distinguishes between authentication and infrastructure errors
  • 📦 Type Safety: Full TypeScript support with comprehensive type definitions
  • 🎯 Role-Based Access: Support for roles and permissions with decorators
  • 🏢 Organization Authorization: Advanced organization-level role and permission management
  • 📊 Health Monitoring: Built-in health checks and service monitoring

Installation

npm install @science-and-humans/science-and-humans-auth-sdk

Quick Start

1. Module Configuration

import { Module } from '@nestjs/common';
import { AuthMiddlewareModule } from '@science-and-humans/science-and-humans-auth-sdk';

@Module({
  imports: [
    AuthMiddlewareModule.forRoot({
      authServiceUrl: 'http://localhost:3000',
      serviceKey: 'your-service-key',
      serviceName: 'your-service-name',
      redis: {
        host: 'localhost',
        port: 6379,
        password: 'your-redis-password', // optional
        db: 0,
      },
      requestTimeout: 5000, // optional, default: 8000ms
    }),
  ],
})
export class AppModule {}

2. Using Guards and Decorators

import { Controller, Get, UseGuards } from '@nestjs/common';
import {
  AuthGuard,
  RolesGuard,
  RequireRoles,
  RequireOrgRole,
  CurrentUser,
  UserRoles,
  GetUserRoleInfo,
  Public,
  ClerkUser,
  UserRoleInfo,
} from '@science-and-humans/science-and-humans-auth-sdk';

@Controller('api')
@UseGuards(AuthGuard, RolesGuard)
export class ApiController {
  @Get('public')
  @Public() // Skip authentication
  getPublicData() {
    return { message: 'Public endpoint' };
  }

  @Get('protected')
  getProtectedData(@CurrentUser() user: ClerkUser) {
    return { message: 'Protected endpoint', user };
  }

  @Get('admin')
  @RequireRoles('admin', 'moderator')
  getAdminData(@CurrentUser() user: ClerkUser, @UserRoles() roles: string[]) {
    return { message: 'Admin only', user, roles };
  }

  @Get('organization/dashboard')
  @RequireOrgRole('org:admin', 'org:moderator')
  getOrgDashboard(
    @CurrentUser() user: ClerkUser,
    @GetUserRoleInfo() roleInfo: UserRoleInfo
  ) {
    return {
      message: 'Organization dashboard',
      user: user.id,
      organizationId: roleInfo.organizationId,
      orgRole: roleInfo.orgRole,
    };
  }
}

AuthGuard Behavior

The AuthGuard now works in an opt-in manner for better scalability:

  • By default: Routes are accessible without authentication
  • With @RequireAuth(): Routes require authentication
  • With @Public(): Routes are explicitly marked as public (overrides @RequireAuth())

Usage Examples

import { 
  AuthGuard, 
  RequireAuth, 
  Public, 
  RequireRoles,
  RequirePermissions 
} from '@science-and-humans/science-and-humans-auth-sdk';

@Controller('api')
@UseGuards(AuthGuard) // Apply guard to all routes in controller
export class ApiController {

  @Get('public')
  @Public() // Explicitly mark as public (overrides guard)
  getPublicData() {
    return { message: 'This is public data' };
  }

  @Get('protected')
  @RequireAuth() // Require authentication
  getProtectedData() {
    return { message: 'This is protected data' };
  }

  @Get('admin-only')
  @RequireAuth()
  @RequireRoles('admin') // Require specific role
  getAdminData() {
    return { message: 'Admin only data' };
  }

  @Get('user-data')
  @RequireAuth()
  @RequirePermissions('read:user') // Require specific permission
  getUserData() {
    return { message: 'User data' };
  }

  @Get('default')
  // No decorator - accessible without authentication by default
  getDefaultData() {
    return { message: 'Default accessible data' };
  }
}

Guard Priority

  1. @Public() - Always allows access (highest priority)
  2. @RequireAuth() - Requires authentication
  3. No decorator - Allows access by default (lowest priority)

This approach provides better scalability by allowing you to:

  • Start with open access and gradually secure routes as needed
  • Avoid accidentally exposing routes due to missing decorators
  • Have fine-grained control over authentication requirements

## API Reference

### Core Exports

#### AuthClient

The core authentication client that handles all communication with the auth service. Provides automatic fallback from Redis to HTTP and intelligent error handling.

```typescript
import { AuthClient } from '@science-and-humans/science-and-humans-auth-sdk';

const authClient = new AuthClient({
  authServiceUrl: 'http://localhost:3000',
  redis: { host: 'localhost', port: 6379 },
});

// Validate JWT token - Primary authentication method
// Verifies token signature, expiration, and retrieves user data
const validation = await authClient.validateToken({ token: 'jwt-token' });
// Returns: { isValid: boolean, user?: ClerkUser, roles?: string[], permissions?: string[] }

// Validate session - Alternative authentication using session ID
// Useful for session-based authentication flows
const session = await authClient.validateSession({ sessionId: 'session-id' });
// Returns: { isValid: boolean, user?: ClerkUser, roles?: string[], permissions?: string[] }

// Check user authorization - Fine-grained permission checking
// Verifies if user has permission to perform specific actions on resources
const auth = await authClient.authorizeUser({
  userId: 'user-123',
  resource: 'posts',
  action: 'create',
});
// Returns: { isAuthorized: boolean, error?: string }

// Health check - Monitor service connectivity and performance
// Checks Redis connection, HTTP fallback, and overall system health
const health = await authClient.healthCheck();
// Returns: { status: string, services: { redis: string, http: string } }

// Cleanup - Properly close connections and cleanup resources
// Important for graceful application shutdown
await authClient.disconnect();

AuthMiddlewareModule

NestJS module for authentication setup.

import { AuthMiddlewareModule } from '@science-and-humans/science-and-humans-auth-sdk';

// Static configuration
AuthMiddlewareModule.forRoot(config);

// Async configuration
AuthMiddlewareModule.forRootAsync({
  inject: [ConfigService],
  useFactory: async (configService: ConfigService) => ({
    authServiceUrl: configService.get('AUTH_SERVICE_URL'),
    redis: {
      host: configService.get('REDIS_HOST'),
      port: configService.get('REDIS_PORT'),
    },
  }),
});

Guards

AuthGuard

Core authentication guard that validates tokens and establishes user context. Automatically extracts tokens from Authorization header, validates them, and populates request.auth with user data.

import { AuthGuard } from '@science-and-humans/science-and-humans-auth-sdk';

@UseGuards(AuthGuard)
@Controller('protected')
export class ProtectedController {}
// All routes in this controller require valid authentication
// Populates @CurrentUser() with authenticated user data

RolesGuard

Role-based access control guard that works in conjunction with AuthGuard. Checks user roles and permissions against route requirements defined by decorators.

import { RolesGuard } from '@science-and-humans/science-and-humans-auth-sdk';

@UseGuards(AuthGuard, RolesGuard) // Must be used together
@Controller('admin')
export class AdminController {}
// Enforces role requirements set by @RequireRoles() decorator
// Automatically denies access if user lacks required roles

Decorators

Authentication Decorators

Route-level decorators that control authentication and authorization behavior. These work with guards to enforce security policies.

import {
  RequireAuth,
  RequireRoles,
  RequirePermissions,
  Public
} from '@science-and-humans/science-and-humans-auth-sdk';

@Public()
// Skip authentication entirely - accessible to anonymous users
// Overrides any guards applied at controller level

@RequireAuth()
// Explicitly require authentication (default behavior when AuthGuard is used)
// Redundant but useful for documentation purposes

@RequireRoles('admin', 'moderator')
// Require user to have one of the specified roles
// User must have 'admin' OR 'moderator' role to access

@RequirePermissions('read:posts', 'write:posts')
// Require specific permissions for fine-grained access control
// User must have 'read:posts' AND 'write:posts' permissions

#### Organization Authorization Decorators

Advanced decorators for organization-level access control. These decorators require users to be in an organization context and have specific organization roles or permissions.

```typescript
import {
  RequireOrgRole,
  RequireOrgPermission,
} from '@science-and-humans/science-and-humans-auth-sdk';

@RequireOrgRole('org:admin', 'org:moderator')
// Require user to have one of the specified organization roles
// User must be in organization context AND have 'org:admin' OR 'org:moderator' role

@RequireOrgPermission('org:manage_members', 'org:view_analytics')
// Require specific organization permissions for fine-grained access control
// User must be in organization context AND have 'org:manage_members' AND 'org:view_analytics' permissions

Parameter Decorators

Extractors that inject authentication data into route handler parameters. These provide clean access to user context.

import {
  CurrentUser,
  UserRoles,
  AuthInfo
} from '@science-and-humans/science-and-humans-auth-sdk';

getUser(@CurrentUser() user: ClerkUser)
// Inject authenticated user object with full profile data
// Returns null if user is not authenticated

getRoles(@UserRoles() roles: string[])
// Inject array of user's roles
// Returns empty array if no roles or not authenticated

getAuthContext(@AuthInfo() auth: AuthContext)
// Inject complete authentication context
// Includes user, roles, permissions, token info, and auth status

getUserRoleInfo(@GetUserRoleInfo() roleInfo: UserRoleInfo)
// Inject detailed role information including organization context
// Returns role context (user/organization), org role, permissions, and organization ID

Middleware

AuthMiddleware

Core NestJS middleware that processes every request to extract and validate authentication tokens. Automatically populates request.auth with authentication context.

import { AuthMiddleware } from '@science-and-humans/science-and-humans-auth-sdk';

// Automatically registered when using AuthMiddlewareModule
// Runs on every request to:
// 1. Extract JWT tokens from Authorization header
// 2. Validate tokens using AuthClient
// 3. Populate request.auth with user context
// 4. Handle authentication errors gracefully

Express Support

Factory functions for integrating with Express applications. Provides the same authentication capabilities outside of NestJS.

import {
  createAuthMiddleware,
  requireAuth,
} from '@science-and-humans/science-and-humans-auth-sdk';

const app = express();
const authClient = new AuthClient(config);

// Apply auth middleware - Processes all requests
app.use(createAuthMiddleware(authClient));
// Extracts tokens and populates req.auth for all routes

// Protect specific routes - Declarative authentication
app.get('/protected', requireAuth(), (req, res) => {
  res.json({ user: req.auth?.user });
});
// Ensures user is authenticated, throws 401 if not

// Role-based protection - Fine-grained authorization
app.get('/admin', requireAuth({ roles: ['admin'] }), (req, res) => {
  res.json({ message: 'Admin only' });
});
// Requires both authentication AND admin role

Exception Filters

AuthExceptionFilter

Specialized exception filter that transforms authentication errors into proper HTTP responses. Provides consistent error formatting and logging.

import { AuthExceptionFilter } from '@science-and-humans/science-and-humans-auth-sdk';

@UseFilters(AuthExceptionFilter)
@Controller()
export class MyController {}
// Catches authentication-related exceptions and formats them properly
// Converts internal auth errors to appropriate HTTP status codes (401, 403)
// Provides detailed error messages while protecting sensitive information

GlobalAuthAwareExceptionFilter

Enhanced global exception filter that understands authentication context. Provides better error responses based on user's auth status.

import { GlobalAuthAwareExceptionFilter } from '@science-and-humans/science-and-humans-auth-sdk';

// In main.ts
app.useGlobalFilters(new GlobalAuthAwareExceptionFilter());
// Handles all exceptions with awareness of user authentication state
// Provides different error responses for authenticated vs anonymous users
// Logs security-relevant exceptions for monitoring and debugging

Health Monitoring

RedisHealthService

Dedicated service for monitoring Redis connection health and performance. Provides real-time status checks and connection management.

import { RedisHealthService } from '@science-and-humans/science-and-humans-auth-sdk';

const healthService = new RedisHealthService({
  host: 'localhost',
  port: 6379,
});

// Quick health check - Returns boolean status
const isHealthy = await healthService.isHealthy();
// true if Redis is reachable and responsive

// Detailed health status - Returns comprehensive health information
const status = await healthService.getHealthStatus();
// Returns: { status: 'healthy'|'degraded'|'unhealthy', lastCheck: Date, responseTime: number }

// Automatically handles:
// - Connection pooling and cleanup
// - Timeout management
// - Retry logic for transient failures
// - Performance monitoring

Organization Authorization

The SDK provides advanced organization-level authorization capabilities that extend beyond basic user authentication. This feature allows you to manage complex multi-tenant applications with organization-specific roles and permissions.

Organization Context

Organization authorization operates in a distinct context from user-level authorization. When a user is part of an organization, they can have:

  • Organization Roles: Roles specific to their position within the organization (e.g., org:admin, org:member, org:moderator)
  • Organization Permissions: Fine-grained permissions for organization-specific actions (e.g., org:manage_members, org:view_analytics, org:edit_settings)

Organization Authorization Flow

import { Controller, Get, UseGuards } from '@nestjs/common';
import {
  AuthGuard,
  RolesGuard,
  RequireOrgRole,
  RequireOrgPermission,
  CurrentUser,
  GetUserRoleInfo,
  UserRoleInfo,
} from '@science-and-humans/science-and-humans-auth-sdk';

@Controller('organization')
@UseGuards(AuthGuard, RolesGuard)
export class OrganizationController {
  @Get('dashboard')
  @RequireOrgRole('org:admin', 'org:moderator')
  getOrgDashboard(
    @CurrentUser() user: ClerkUser,
    @GetUserRoleInfo() roleInfo: UserRoleInfo
  ) {
    return {
      message: 'Organization dashboard',
      user: user.id,
      organizationId: roleInfo.organizationId,
      orgRole: roleInfo.orgRole,
      context: roleInfo.context,
    };
  }

  @Get('members')
  @RequireOrgPermission('org:manage_members')
  getOrgMembers(@GetUserRoleInfo() roleInfo: UserRoleInfo) {
    return {
      message: 'Organization members list',
      organizationId: roleInfo.organizationId,
      permissions: roleInfo.permissions,
    };
  }

  @Get('analytics')
  @RequireOrgPermission('org:view_analytics', 'org:admin')
  getOrgAnalytics() {
    return { message: 'Organization analytics' };
  }
}

Organization Authorization with AuthClient

The AuthClient supports organization context in all authorization operations:

import { AuthClient } from '@science-and-humans/science-and-humans-auth-sdk';

const authClient = new AuthClient({
  authServiceUrl: 'http://localhost:3000',
  organizationId: 'org_30yWUKhkqRl4aexwv3aJelcoRbT', // Default org context
});

// Validate token with organization context
const validation = await authClient.validateToken({
  token: 'jwt-token',
  organizationId: 'org_30yWUKhkqRl4aexwv3aJelcoRbT',
  context: 'organization',
});

// Authorize user for organization-specific actions
const auth = await authClient.authorizeUser({
  userId: 'user_123',
  resource: 'organization_members',
  action: 'manage',
  organizationId: 'org_30yWUKhkqRl4aexwv3aJelcoRbT',
  context: 'organization',
});

Organization Types

import {
  ClerkOrganization,
  OrganizationRole,
  OrganizationPermission,
  UserRoleInfo,
} from '@science-and-humans/science-and-humans-auth-sdk';

interface UserRoleInfo {
  context: 'user' | 'organization';
  organizationId?: string;
  orgRole?: string;
  permissions?: string[];
}

interface ClerkOrganization {
  id: string;
  name: string;
  slug?: string;
  imageUrl: string;
  hasImage: boolean;
  membersCount?: number;
  maxAllowedMemberships: number;
  adminDeleteEnabled: boolean;
  publicMetadata: Record<string, unknown>;
  privateMetadata?: Record<string, unknown>;
  createdAt: Date;
  updatedAt: Date;
}

interface OrganizationRole {
  id: string;
  key: string;
  name: string;
  description?: string;
  permissions: OrganizationPermission[];
  createdAt: Date;
  updatedAt: Date;
}

interface OrganizationPermission {
  id: string;
  key: string;
  name: string;
  description?: string;
  type: 'system' | 'custom';
  createdAt: Date;
  updatedAt: Date;
}

Best Practices for Organization Authorization

  1. Context Awareness: Always check if the user is in the correct context (user vs organization)
  2. Role Hierarchy: Design organization roles with clear hierarchies (admin > moderator > member)
  3. Permission Granularity: Use specific permissions for fine-grained access control
  4. Default Organization: Set a default organization ID in your configuration for consistent behavior
  5. Error Handling: Handle organization context errors gracefully with appropriate user feedback

Type Definitions

Core Types

import {
  ClerkUser,
  ClerkSession,
  AuthConfig,
  AuthValidationRequest,
  AuthValidationResponse,
  User,
  Role,
  Permission,
  ClerkOrganization,
  OrganizationRole,
  OrganizationPermission,
  UserRoleInfo,
} from '@science-and-humans/science-and-humans-auth-sdk';

Configuration Types

interface AuthConfig {
  authServiceUrl: string;
  serviceKey?: string;
  serviceName?: string;
  organizationId?: string; // Default organization context for all requests
  requestTimeout?: number;
  redis: {
    host: string;
    port: number;
    password?: string;
    db?: number;
  };
}

User Types

interface User {
  id: string;
  email: string;
  firstName?: string;
  lastName?: string;
  role: string;
  isVerified: boolean;
  createdAt: Date;
  updatedAt: Date;
  metadata?: Record<string, any>;
}

interface Role {
  id: string;
  name: string;
  permissions: Permission[];
  description?: string;
}

interface Permission {
  id: string;
  name: string;
  resource: string;
  action: string;
  description?: string;
}

Configuration Examples

Environment-based Configuration

AuthMiddlewareModule.forRoot({
  authServiceUrl: process.env.AUTH_SERVICE_URL || 'http://localhost:3000',
  serviceKey: process.env.AUTH_SERVICE_KEY,
  serviceName: process.env.SERVICE_NAME || 'my-service',
  organizationId: process.env.ORGANIZATION_ID, // Default organization context
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: parseInt(process.env.REDIS_PORT || '6379'),
    password: process.env.REDIS_PASSWORD,
    db: parseInt(process.env.REDIS_DB || '0'),
  },
  requestTimeout: parseInt(process.env.AUTH_TIMEOUT || '5000'),
});

Async Configuration with ConfigService

AuthMiddlewareModule.forRootAsync({
  inject: [ConfigService],
  useFactory: async (configService: ConfigService) => ({
    authServiceUrl: configService.get('auth.serviceUrl'),
    serviceKey: configService.get('auth.serviceKey'),
    serviceName: configService.get('app.name'),
    redis: {
      host: configService.get('redis.host'),
      port: configService.get('redis.port'),
      password: configService.get('redis.password'),
      db: configService.get('redis.db'),
    },
  }),
});

Error Handling

The middleware distinguishes between different types of errors:

  • Authentication Errors: Invalid/expired tokens → Returns 401 immediately
  • Infrastructure Errors: Redis/network issues → Triggers HTTP fallback
  • Authorization Errors: Insufficient permissions → Returns 403
try {
  // Protected operation
} catch (error) {
  if (error instanceof UnauthorizedException) {
    // Handle authentication failure
  } else if (error instanceof ForbiddenException) {
    // Handle authorization failure
  }
}

Health Monitoring

// Check overall health
const health = await authClient.healthCheck();
console.log(health);
// { status: 'healthy', services: { redis: 'up', http: 'up' } }

// Monitor Redis specifically
const redisHealth = new RedisHealthService(redisConfig);
const isHealthy = await redisHealth.isHealthy();

Best Practices

  1. Always use environment variables for sensitive configuration
  2. Combine AuthGuard with RolesGuard for role-based access control
  3. Use @Public() decorator to explicitly mark public endpoints
  4. Monitor health status in production environments
  5. Handle errors gracefully with proper exception filters

License

MIT License