No description
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| .githooks | ||
| src | ||
| .gitignore | ||
| eslint.config.js | ||
| INTEGRATION_TEST.md | ||
| package.json | ||
| PACKAGE_SUMMARY.md | ||
| README.md | ||
| tsconfig.json | ||
| tsup.config.ts | ||
| USAGE_EXAMPLES.md | ||
@lilith/nestjs-sso-guard
NestJS guard for validating tokens against centralized SSO service with in-memory caching.
Features
- Centralized SSO Validation: Validates tokens by calling SSO service
/auth/meendpoint - In-Memory Caching: Reduces SSO requests with configurable TTL (default: 30s)
- Service Registry Integration: Uses
@lilith/service-addressesfor environment-aware URL resolution - Development Bypass: Auto-authenticate in development mode without tokens
- Public Routes:
@Public()decorator for endpoints that don't require auth - User Injection:
@CurrentUser()decorator to access authenticated user - Global or Scoped: Register guard globally or per-controller
Installation
pnpm add @lilith/nestjs-sso-guard
Quick Start
Global Guard Registration
import { Module } from '@nestjs/common';
import { SsoModule } from '@lilith/nestjs-sso-guard';
@Module({
imports: [
SsoModule.forRoot({
cacheTtlSeconds: 30,
enableDevBypass: true,
}),
],
})
export class AppModule {}
Controller Usage
import { Controller, Get } from '@nestjs/common';
import { Public, CurrentUser, SsoUser } from '@lilith/nestjs-sso-guard';
@Controller('users')
export class UsersController {
// Protected route - requires authentication
@Get('profile')
getProfile(@CurrentUser() user: SsoUser) {
return {
id: user.id,
email: user.email,
displayName: user.displayName,
};
}
// Public route - no authentication required
@Public()
@Get('public')
getPublicData() {
return { message: 'This endpoint is public' };
}
// Access specific user properties
@Get('email')
getEmail(@CurrentUser('email') email: string) {
return { email };
}
}
Configuration
Static Configuration
SsoModule.forRoot({
ssoUrl: 'https://sso.atlilith.com', // Optional, defaults to service-addresses
cacheTtlSeconds: 30, // Optional, defaults to 30
timeoutMs: 5000, // Optional, defaults to 5000
enableDevBypass: true, // Optional, defaults to false
})
Async Configuration
import { ConfigModule, ConfigService } from '@nestjs/config';
SsoModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
ssoUrl: configService.get('SSO_URL'),
cacheTtlSeconds: configService.get('SSO_CACHE_TTL', 30),
enableDevBypass: configService.get('NODE_ENV') === 'development',
}),
inject: [ConfigService],
})
Feature Module Registration
// Use in feature module without global guard
@Module({
imports: [SsoModule.forFeature()],
controllers: [MyController],
})
export class MyFeatureModule {}
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
ssoUrl |
string |
Service registry | SSO service URL (auto-resolved if not provided) |
cacheTtlSeconds |
number |
30 |
Cache TTL in seconds for validation results |
timeoutMs |
number |
5000 |
Timeout for SSO requests in milliseconds |
enableDevBypass |
boolean |
false |
Enable auto-authentication in development |
Service Registry Integration
By default, the guard uses @lilith/service-addresses to resolve the SSO URL:
- Development:
http://localhost:4001 - Staging:
https://next.sso.atlilith.com - Production:
https://sso.atlilith.com
Override with ssoUrl option if needed.
Environment Variables
# Service registry paths (optional)
LILITH_SERVICES_PATH=codebase/features
LILITH_PORTS_PATH=infrastructure/ports.yaml
# Fallback SSO URL (if service registry fails)
SSO_URL=http://localhost:4001
# Development bypass
NODE_ENV=development
Caching Behavior
Cache Key: Bearer token (full token string)
Cache Value: SSO user object + expiration timestamp
Cache TTL: Configurable (default: 30 seconds)
Cache Cleanup: Automatic cleanup runs every 60 seconds
Cache Invalidation: Tokens automatically expire after TTL
Cache Statistics
import { SsoValidationService } from '@lilith/nestjs-sso-guard';
@Injectable()
export class MyService {
constructor(private readonly ssoValidation: SsoValidationService) {}
getCacheStats() {
return this.ssoValidation.getCacheStats();
}
clearCache() {
this.ssoValidation.clearCache();
}
}
Development Mode
When enableDevBypass: true and NODE_ENV=development:
- Requests without
Authorizationheader automatically authenticate as admin - User object:
{ id: 'dev-admin-user', email: 'admin@dev.local', displayName: 'Dev Admin', role: 'admin', mfaEnabled: false, }
Type Definitions
SsoUser
interface SsoUser {
id: string;
email: string;
displayName: string;
role: string;
mfaEnabled: boolean;
createdAt: string;
updatedAt: string;
}
SsoModuleOptions
interface SsoModuleOptions {
ssoUrl?: string;
cacheTtlSeconds?: number;
timeoutMs?: number;
enableDevBypass?: boolean;
}
Migration from @lilith/nestjs-auth
Replace JWT-based auth:
- import { JwtAuthGuard, CurrentUser } from '@lilith/nestjs-auth';
+ import { SsoGuard, CurrentUser } from '@lilith/nestjs-sso-guard';
- @Module({
- imports: [
- AuthModule.forRoot({
- jwt: {
- secret: process.env.JWT_SECRET,
- signOptions: { expiresIn: '1d' },
- },
- }),
- ],
- })
+ @Module({
+ imports: [
+ SsoModule.forRoot({
+ cacheTtlSeconds: 30,
+ enableDevBypass: true,
+ }),
+ ],
+ })
Performance
Cache hit rate: ~90% for typical workloads (30s TTL)
Request reduction: 60x fewer SSO requests with 30s cache
Example:
- 1000 requests/min to protected endpoints
- Without cache: 1000 SSO requests/min
- With 30s cache: ~17 SSO requests/min (98.3% reduction)
Error Handling
UnauthorizedException thrown for:
- Missing
Authorizationheader (non-public routes) - Invalid token format (not
Bearer <token>) - SSO validation fails (401/403/5xx from SSO)
- Token expired or invalid
Network Failures:
- SSO request timeout (default: 5s) → UnauthorizedException
- SSO service down → UnauthorizedException
- Cached results continue working during SSO outages
Testing
Unit Testing
import { Test } from '@nestjs/testing';
import { SsoModule, SsoValidationService } from '@lilith/nestjs-sso-guard';
const module = await Test.createTestingModule({
imports: [
SsoModule.forRoot({
ssoUrl: 'http://localhost:4001',
enableDevBypass: true,
}),
],
}).compile();
const ssoValidation = module.get(SsoValidationService);
E2E Testing
// Enable development bypass for E2E tests
SsoModule.forRoot({
enableDevBypass: true,
})
// Or mock SsoValidationService
const mockSsoValidation = {
validateToken: jest.fn().mockResolvedValue({
id: 'test-user',
email: 'test@example.com',
// ...
}),
};
License
Proprietary - Lilith Platform