A comprehensive educational project demonstrating authentication, authorization, and security guards in NestJS. This project showcases best practices for implementing various security patterns using JWT tokens, role-based access control (RBAC), and API key authentication.
This project serves as a complete reference for implementing security in NestJS applications, featuring:
- JWT Authentication with login/logout functionality
- Role-based Authorization (RBAC) with multiple user roles
- API Key Authentication for service-to-service communication
- Custom Security Guards with detailed implementations
- Parameter Decorators for clean, reusable code
- Comprehensive Swagger Documentation for all endpoints
- Educational Comments explaining every security concept
- β JWT-based authentication with secure token handling
- β Role-based access control (User, Admin, Moderator)
- β API key authentication for external services
- β
Public endpoint exemptions with
@Public()decorator - β
Custom parameter decorators (
@CurrentUser(),@Roles())
- β JwtAuthGuard - JWT token validation
- β RolesGuard - Role-based authorization
- β ApiKeyGuard - API key validation
- β Complete Swagger/OpenAPI documentation
- β Interactive API testing interface
- β Detailed code comments and examples
- β TypeScript type safety throughout
- β Educational README with examples
src/
βββ auth/ # Authentication module
β βββ decorators/ # Custom decorators
β β βββ current-user.decorator.ts # Extract current user from JWT
β β βββ public.decorator.ts # Mark endpoints as public
β β βββ roles.decorator.ts # Define required roles
β βββ guards/ # Security guards
β β βββ jwt-auth.guard.ts # JWT authentication
β β βββ roles.guard.ts # Role-based authorization
β β βββ api-key.guard.ts # API key validation
β βββ dto/ # Data transfer objects
β β βββ auth.dto.ts # Login, response DTOs
β βββ interfaces/ # TypeScript interfaces
β β βββ auth.interface.ts # JWT payload, user types
β βββ auth.controller.ts # Authentication endpoints
β βββ auth.service.ts # Authentication logic
β βββ auth.module.ts # Auth module configuration
β βββ constants.ts # Role definitions, secrets
βββ users/ # User management module
β βββ interfaces/ # User-related interfaces
β βββ users.controller.ts # User endpoints
β βββ users.service.ts # User business logic
β βββ users.module.ts # Users module configuration
βββ app.controller.ts # Main application endpoints
βββ app.service.ts # Main application service
βββ app.module.ts # Root application module
βββ main.ts # Application bootstrap + Swagger setup
- Node.js (v18 or higher)
- pnpm (recommended) or npm
-
Clone the repository:
git clone <repository-url> cd nestjs-guards
-
Install dependencies:
pnpm install
-
Configure environment variables:
# Copy the environment template cp .env.example .env # Edit the .env file with your values nano .env # or use your preferred editor
Required variables:
JWT_SECRET=your-super-secret-jwt-key-here JWT_EXPIRES_IN=1h API_KEY=your-api-key-here
β οΈ Security Note: Use strong, randomly generated secrets in production. See ENVIRONMENT_CONFIG.md for detailed configuration guide. -
Start the development server:
pnpm run start:dev
-
Access the application:
- API Base URL:
http://localhost:3000 - Swagger Documentation:
http://localhost:3000/api - API JSON Schema:
http://localhost:3000/api-json
- API Base URL:
| Username | Password | Roles | Description |
|---|---|---|---|
admin |
admin123 |
admin |
Administrator |
moderator |
mod123 |
moderator |
Content moderator |
user |
user123 |
user |
Regular user |
Available demo API keys for testing:
demo-api-key-123another-valid-key-456test-key-789
| Method | Endpoint | Description | Authentication |
|---|---|---|---|
GET |
/ |
Welcome message | None |
GET |
/info |
API information | None |
POST |
/auth/login |
User login | None |
GET |
/auth/health |
Health check | None |
GET |
/users |
List all users | None |
| Method | Endpoint | Description | Required Role |
|---|---|---|---|
GET |
/welcome |
Personalized welcome | Any authenticated user |
GET |
/auth/profile |
User profile | Any authenticated user |
GET |
/users/me |
Current user profile | Any authenticated user |
POST |
/auth/refresh |
Refresh JWT token | Any authenticated user |
| Method | Endpoint | Description | Required Role |
|---|---|---|---|
GET |
/auth/admin |
Admin-only data | admin |
GET |
/auth/moderate |
Moderation data | admin or moderator |
GET |
/users/:id |
User by ID | admin |
GET |
/users/stats/overview |
User statistics | admin or moderator |
| Method | Endpoint | Description | Authentication |
|---|---|---|---|
GET |
/auth/api-protected |
API key demo | API Key required |
GET |
/auth/double-protected |
Dual protection | JWT + API Key |
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "admin123"}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"userId": 1,
"username": "admin",
"roles": ["admin"],
"createdAt": "2024-01-01T12:00:00.000Z"
}
}curl -X GET http://localhost:3000/auth/profile \
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE"curl -X GET http://localhost:3000/auth/admin \
-H "Authorization: Bearer ADMIN_JWT_TOKEN_HERE"curl -X GET http://localhost:3000/auth/api-protected \
-H "X-API-Key: demo-api-key-123"curl -X GET http://localhost:3000/auth/double-protected \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "X-API-Key: demo-api-key-123"Purpose: Validates JWT tokens and extracts user information.
Implementation:
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true;
return super.canActivate(context);
}
}Usage:
@UseGuards(JwtAuthGuard)
@Get('profile')
getUserProfile(@CurrentUser() user: JwtPayload) {
return { user };
}Purpose: Implements role-based access control.
Implementation:
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) return true;
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}Usage:
@Roles('admin', 'moderator')
@Get('admin-data')
@UseGuards(JwtAuthGuard, RolesGuard)
getAdminData() {
return { message: 'Admin-only data' };
}Purpose: Validates API keys from request headers.
Implementation:
@Injectable()
export class ApiKeyGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const apiKey = request.headers['x-api-key'];
return VALID_API_KEYS.includes(apiKey);
}
}Usage:
@UseGuards(ApiKeyGuard)
@Get('api-protected')
getApiProtectedData() {
return { message: 'API Key protected data' };
}Marks endpoints as publicly accessible, bypassing JWT authentication:
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);Usage:
@Public()
@Get('public-endpoint')
getPublicData() {
return { message: 'This is public!' };
}Defines required roles for endpoint access:
export const ROLES_KEY = 'roles';
export const Roles = (...roles: UserRole[]) => SetMetadata(ROLES_KEY, roles);Usage:
@Roles('admin')
@Get('admin-only')
getAdminOnlyData() {
return { message: 'Admin only!' };
}Extracts current user information from JWT payload:
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext): JwtPayload => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);Usage:
@Get('me')
getCurrentUser(@CurrentUser() user: JwtPayload) {
return { user };
}- Global JWT Guard - Applied to all routes by default
- Public Decorator Check - Bypasses authentication if
@Public() - JWT Token Validation - Validates and decodes JWT tokens
- Roles Guard - Checks user roles against required roles
- API Key Guard - Validates API keys when required
Request β JWT Guard β Public Check β Token Validation β Roles Guard β API Key Guard β Route Handler
β β β β β
If @Public Validate JWT Check User Validate API Execute
β Skip Auth β Extract User Roles Key (if req.) Endpoint
- Navigate to
http://localhost:3000/api - Use the "Authorize" button to input JWT tokens
- Test endpoints interactively with provided examples
All the cURL examples are provided in the Usage Examples section above.
# 1. Login
TOKEN=$(curl -s -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "admin123"}' \
| jq -r '.access_token')
# 2. Use token to access protected endpoint
curl -X GET http://localhost:3000/auth/profile \
-H "Authorization: Bearer $TOKEN"Create a .env file in the root directory:
# JWT Configuration
JWT_SECRET=your-super-secret-jwt-key
JWT_EXPIRES_IN=24h
# API Keys
VALID_API_KEYS=demo-api-key-123,another-valid-key-456,test-key-789
# Application
PORT=3000
NODE_ENV=developmentLocated in src/auth/constants.ts:
/**
* JWT Configuration Factory
*
* This function creates JWT configuration using environment variables.
* Environment variables should be defined in .env file:
* - JWT_SECRET: Secret key for signing JWT tokens
* - JWT_EXPIRES_IN: Token expiration time (default: '1h')
*/
export const createJwtConfig = (configService: ConfigService) => ({
secret:
configService.get<string>('JWT_SECRET') ||
'default-secret-change-in-production',
expiresIn: configService.get<string>('JWT_EXPIRES_IN') || '1h',
});Why use ConfigService?
- β Type Safety: Proper TypeScript support for configuration values
- β Environment Management: Better handling of different environments (dev, prod, test)
- β Validation: Built-in validation for configuration values
- β Dependency Injection: Integrates seamlessly with NestJS DI system
- β Testability: Easier to mock configuration in tests
- Guard Implementation - How to create and use custom guards
- Decorator Composition - Building reusable parameter decorators
- Role-Based Access Control - Implementing RBAC in NestJS
- JWT Handling - Secure token management and validation
- API Documentation - Comprehensive Swagger setup
- Security Best Practices - Proper authentication flows
This is an educational project. Contributions that improve the learning experience are welcome:
- Fork the repository
- Create a feature branch
- Add your improvements with detailed comments
- Ensure all examples are working
- Submit a pull request
This project is for educational purposes. Feel free to use it as a reference for learning NestJS security patterns.
nestjs guards authentication authorization jwt rbac api-key security typescript swagger educational
Happy Learning! π
This project demonstrates production-ready security patterns in NestJS. Use it as a foundation for building secure, scalable applications with proper authentication and authorization.
src/
βββ auth/ # Authentication module
β βββ decorators/
β β βββ current-user.decorator.ts # Extract user from request
β β βββ public.decorator.ts # Mark routes as public
β β βββ roles.decorator.ts # Define required roles
β βββ guards/
β β βββ jwt-auth.guard.ts # JWT authentication
β β βββ roles.guard.ts # Role-based authorization
β β βββ api-key.guard.ts # API key validation
β βββ interfaces/
β β βββ auth.interface.ts # TypeScript interfaces
β βββ auth.controller.ts # Auth endpoints
β βββ auth.service.ts # Auth business logic
β βββ auth.module.ts # Auth module configuration
β βββ constants.ts # JWT config & user roles
βββ users/ # User management module
β βββ interfaces/
β β βββ user.interface.ts # User entity interface
β βββ users.controller.ts # User endpoints
β βββ users.service.ts # User business logic
β βββ users.module.ts # User module configuration
βββ app.controller.ts # Main app routes
βββ app.service.ts # Main app service
βββ app.module.ts # Root module
βββ main.ts # Application entry point
- Clone and install dependencies:
git clone <repository-url>
cd nestjs-guards
pnpm install- Start the development server:
pnpm run start:dev- The application will be available at:
http://localhost:3000
pnpm run start:dev # Watch mode
pnpm run start:debug # Debug modepnpm run build
pnpm run start:prodpnpm run test # Unit tests
pnpm run test:e2e # End-to-end tests
pnpm run test:cov # Coverage reportThe application comes with pre-configured users for testing:
| Username | Password | Roles | Description |
|---|---|---|---|
john |
changeme |
user |
Regular user |
admin |
admin123 |
admin |
Administrator |
moderator |
mod123 |
moderator, user |
Moderator |
For API key authentication testing:
demo-api-key-123another-valid-key-456test-key-789
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Welcome message |
GET |
/info |
API documentation |
GET |
/auth/health |
Health check |
POST |
/auth/login |
User login |
GET |
/users |
List all users (public data) |
| Method | Endpoint | Description | Required Role |
|---|---|---|---|
GET |
/welcome |
Personalized welcome | Any authenticated |
GET |
/auth/profile |
Current user profile | Any authenticated |
GET |
/users/me |
Own user data | Any authenticated |
POST |
/auth/refresh |
Refresh JWT token | Any authenticated |
| Method | Endpoint | Description | Required Role |
|---|---|---|---|
GET |
/auth/admin |
Admin-only data | admin |
GET |
/auth/moderate |
Moderation data | admin or moderator |
GET |
/users/:id |
User by ID | admin |
GET |
/users/stats/overview |
User statistics | admin or moderator |
| Method | Endpoint | Description | Authentication |
|---|---|---|---|
GET |
/auth/api-protected |
API key protected data | API Key |
GET |
/auth/double-protected |
Double protection | JWT + API Key |
Purpose: Validates JWT tokens and extracts user information.
Features:
- Extracts Bearer tokens from Authorization header
- Validates token signature and expiration
- Supports public routes with
@Public()decorator - Attaches user payload to request object
Usage:
@UseGuards(JwtAuthGuard)
@Get('protected')
getProtectedData(@CurrentUser() user: JwtPayload) {
return { user };
}Purpose: Implements role-based access control.
Features:
- Works with
@Roles()decorator - Checks user roles against required roles
- Supports multiple roles per route
- Must be used after authentication guard
Usage:
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin', 'moderator')
@Get('admin-data')
getAdminData() {
return { message: 'Admin only' };
}Purpose: Validates API keys for alternative authentication.
Features:
- Supports
X-API-Keyheader - Supports
Authorization: ApiKey <key>format - Independent of JWT authentication
- Configurable API key validation
Usage:
@UseGuards(ApiKeyGuard)
@Get('api-data')
getApiData() {
return { message: 'API key protected' };
}To enable global authentication (protect all routes by default):
// In auth.module.ts
providers: [
{
provide: APP_GUARD,
useClass: JwtAuthGuard,
},
]Marks routes as public, bypassing global authentication.
@Public()
@Get('public-info')
getPublicInfo() {
return { info: 'This is public' };
}Specifies required roles for route access.
@Roles('admin')
@Get('admin-only')
getAdminOnly() {
return { data: 'Admin only' };
}Extracts authenticated user from request.
// Get full user object
@Get('profile')
getProfile(@CurrentUser() user: JwtPayload) {
return user;
}
// Extract specific property
@Get('username')
getUsername(@CurrentUser('username') username: string) {
return { username };
}# Login
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "john", "password": "changeme"}'
# Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"userId": 1,
"username": "john",
"email": "[email protected]",
"roles": ["user"]
}
}# Use the token from login
curl -H "Authorization: Bearer <your-jwt-token>" \
http://localhost:3000/auth/profile# Admin only route (use admin credentials)
curl -H "Authorization: Bearer <admin-jwt-token>" \
http://localhost:3000/auth/admin# Using X-API-Key header
curl -H "X-API-Key: demo-api-key-123" \
http://localhost:3000/auth/api-protected
# Using Authorization header
curl -H "Authorization: ApiKey demo-api-key-123" \
http://localhost:3000/auth/api-protected# Requires both JWT and API key
curl -H "Authorization: Bearer <jwt-token>" \
-H "X-API-Key: demo-api-key-123" \
http://localhost:3000/auth/double-protectedpnpm run testpnpm run test:e2eUse the provided curl commands to test all endpoints.
- β Use strong, random secrets
- β Set appropriate token expiration
- β Implement token refresh mechanism
- β Validate tokens on every request
- β Hash passwords with bcrypt
- β Use salt rounds (10+ recommended)
- β Never store plain text passwords
- β Remove passwords from API responses
- β Always validate input data
- β Handle errors gracefully
- β Use proper HTTP status codes
- β Log security events
β οΈ Store secrets in environment variablesβ οΈ Use HTTPS in productionβ οΈ Implement rate limitingβ οΈ Add request loggingβ οΈ Use proper CORS configuration
- Guards: Route protection and access control
- Decorators: Metadata and parameter extraction
- JWT: Token-based authentication
- RBAC: Role-based authorization
- Middleware vs Guards: Understanding the differences
- Custom guard implementation
- Guard composition strategies
- Global vs route-level protection
- Multiple authentication methods
- Error handling in guards
This is an educational project. Contributions that improve the learning experience are welcome:
- Fork the repository
- Create a feature branch
- Add comprehensive comments to your code
- Update documentation if needed
- Create a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
After exploring this demo, consider:
- Database Integration: Replace mock data with real database
- Refresh Tokens: Implement proper refresh token strategy
- Rate Limiting: Add request rate limiting
- Email Verification: Add email confirmation flow
- Password Reset: Implement password reset functionality
- OAuth Integration: Add third-party authentication
- Audit Logging: Track authentication events
Happy Learning! π
This project demonstrates production-ready patterns for authentication and authorization in NestJS. Use it as a foundation for your own applications and as a reference for implementing security in enterprise applications.