Access Control
NestLens provides comprehensive access control mechanisms to secure your dashboard and protect sensitive application data.
Overview
Access control in NestLens operates on multiple layers:
- Environment Restrictions - Limit access to specific environments
- IP Whitelisting - Restrict access by IP address
- Custom Authorization - Implement custom authentication logic
- Role-Based Access - Control access based on user roles
Guard Evaluation Order
NestLensGuard evaluates these checks in a fixed order. The first failing check denies access:
- Rate limit - per-IP request limit (checked before anything else; returns
429 Too Many Requestswhen exceeded) - Environment - current environment must be in
allowedEnvironments - IP whitelist - client IP must match
allowedIps(only enforced whenallowedIpsis non-empty) canAccess- the custom function must not returnfalserequiredRoles- the user must have all required roles
Important:
requiredRolesis only enforced whencanAccessreturns anAuthUserobject. IfcanAccessreturns a booleantrue(or is not defined), the attached user isundefinedand the role check is skipped entirely. To use role-based access, yourcanAccessfunction must return anAuthUserobject, not justtrue.
Environment Restrictions
The simplest form of access control limits NestLens to specific environments.
Default Behavior
By default, NestLens only runs in development environments:
// Default configuration
{
authorization: {
allowedEnvironments: ['development', 'local', 'test']
}
}
Custom Environments
Configure which environments can access NestLens:
NestLensModule.forRoot({
authorization: {
allowedEnvironments: ['development', 'staging'],
environmentVariable: 'NODE_ENV', // default
},
})
Allow All Environments
Set to null to allow any environment:
NestLensModule.forRoot({
authorization: {
allowedEnvironments: null, // Allow all environments
},
})
Warning: Only use in production if you have other security measures in place.
Disable All Environments
Set to empty array to completely disable:
NestLensModule.forRoot({
authorization: {
allowedEnvironments: [], // Disable completely
},
})
Custom Environment Variable
Use a different environment variable:
NestLensModule.forRoot({
authorization: {
environmentVariable: 'APP_ENV',
allowedEnvironments: ['dev', 'qa'],
},
})
Custom Authorization
Implement custom authentication and authorization logic.
Basic Authorization Function
import { Request } from 'express';
NestLensModule.forRoot({
authorization: {
canAccess: (req: Request) => {
// Check for API key
const apiKey = req.headers['x-api-key'];
return apiKey === process.env.NESTLENS_API_KEY;
},
},
})
Async Authorization
Use async functions for database or external checks:
NestLensModule.forRoot({
authorization: {
canAccess: async (req: Request) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return false;
}
// Verify with external service
const isValid = await authService.verifyToken(token);
return isValid;
},
},
})
Session-Based Authorization
Check user session:
NestLensModule.forRoot({
authorization: {
canAccess: (req: Request) => {
// Require authenticated session
return req.session?.user?.isAuthenticated === true;
},
},
})
JWT-Based Authorization
Verify JWT tokens:
import { JwtService } from '@nestjs/jwt';
NestLensModule.forRoot({
authorization: {
canAccess: async (req: Request) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return false;
}
try {
const jwtService = new JwtService({
secret: process.env.JWT_SECRET,
});
await jwtService.verifyAsync(token);
return true;
} catch {
return false;
}
},
},
})
Role-Based Access Control
Restrict access based on user roles.
Return User Object
Return an AuthUser object from canAccess to enable role checking:
import { AuthUser } from 'nestlens';
NestLensModule.forRoot({
authorization: {
canAccess: async (req: Request): Promise<AuthUser | boolean> => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return false;
}
const user = await authService.verifyToken(token);
if (!user) {
return false;
}
// Return user object with roles
return {
id: user.id,
name: user.name,
email: user.email,
roles: user.roles, // e.g., ['admin', 'developer']
};
},
requiredRoles: ['admin'], // User must have ALL specified roles
},
})
AuthUser Interface
The AuthUser object structure:
interface AuthUser {
id: string | number;
name?: string;
email?: string;
roles?: string[];
[key: string]: unknown; // Additional custom fields
}
Required Roles
Specify which roles can access NestLens:
NestLensModule.forRoot({
authorization: {
canAccess: (req) => {
// Return user with roles
return {
id: req.user.id,
roles: req.user.roles,
};
},
requiredRoles: ['admin'], // Only admins
},
})
Multiple Roles (AND Logic)
Users must have ALL of the specified roles:
NestLensModule.forRoot({
authorization: {
requiredRoles: ['admin', 'developer'], // User must have BOTH roles
},
})
If you need OR logic (user needs at least one role), implement it in your canAccess function:
NestLensModule.forRoot({
authorization: {
canAccess: async (req) => {
const user = await getUser(req);
if (!user) return false;
// OR logic: user needs at least one of these roles
const allowedRoles = ['admin', 'developer', 'qa'];
const hasAnyRole = user.roles.some(role => allowedRoles.includes(role));
if (!hasAnyRole) return false;
return { id: user.id, roles: user.roles };
},
// Don't use requiredRoles here - we handled it in canAccess
},
})
Accessing User Context
The authenticated user is available in the request:
// In custom API endpoints or middleware
const nestlensUser = (req as any).nestlensUser;
console.log('User accessing NestLens:', nestlensUser);
Combining Security Layers
Use multiple security layers together:
NestLensModule.forRoot({
authorization: {
// Layer 1: Environment
allowedEnvironments: ['development', 'staging', 'production'],
// Layer 2: IP Whitelist
allowedIps: ['192.168.1.*', '10.0.0.*'],
// Layer 3: Custom Auth
canAccess: async (req: Request) => {
// Check authentication
const user = await authService.getCurrentUser(req);
if (!user) {
return false;
}
// Return user for role checking
return {
id: user.id,
name: user.name,
roles: user.roles,
};
},
// Layer 4: Required Roles
requiredRoles: ['admin'],
},
})
All layers must pass for access to be granted.
Production Considerations
Recommended Production Setup
NestLensModule.forRoot({
// Only enable if needed in production
enabled: process.env.NESTLENS_ENABLED === 'true',
authorization: {
// Strict environment control
allowedEnvironments: process.env.NESTLENS_ENVIRONMENTS?.split(','),
// IP whitelist for office/VPN
allowedIps: process.env.NESTLENS_ALLOWED_IPS?.split(','),
// Require authentication
canAccess: async (req) => {
const user = await authService.authenticate(req);
return user ? {
id: user.id,
roles: user.roles,
} : false;
},
// Admin only
requiredRoles: ['admin', 'super-admin'],
},
})
Environment Variables
# .env.production
NESTLENS_ENABLED=false
NESTLENS_ENVIRONMENTS=production
NESTLENS_ALLOWED_IPS=192.168.1.0/24,10.0.0.0/8
Disable in Production
Safest approach for production:
NestLensModule.forRoot({
enabled: process.env.NODE_ENV !== 'production',
})
Rate Limiting
Prevent abuse with built-in rate limiting:
NestLensModule.forRoot({
rateLimit: {
windowMs: 60 * 1000, // 1 minute
maxRequests: 100, // 100 requests per minute per IP
},
})
Disable Rate Limiting
NestLensModule.forRoot({
rateLimit: false,
})
Custom Rate Limits
NestLensModule.forRoot({
rateLimit: {
windowMs: 15 * 60 * 1000, // 15 minutes
maxRequests: 1000, // 1000 requests per 15 minutes
},
})
Security Best Practices
1. Never Expose in Production Without Auth
// BAD - No authentication
NestLensModule.forRoot({
enabled: true,
authorization: {
allowedEnvironments: null, // Allows all
},
})
// GOOD - Strict authentication
NestLensModule.forRoot({
enabled: process.env.NODE_ENV !== 'production',
authorization: {
canAccess: async (req) => {
return await authService.isAdmin(req);
},
},
})
2. Use Environment Variables
Store sensitive config in environment variables:
NestLensModule.forRoot({
authorization: {
canAccess: (req) => {
const apiKey = req.headers['x-api-key'];
return apiKey === process.env.NESTLENS_API_KEY;
},
},
})
3. Combine Multiple Layers
Don't rely on a single security measure:
NestLensModule.forRoot({
authorization: {
allowedEnvironments: ['staging', 'production'],
allowedIps: process.env.OFFICE_IPS?.split(','),
canAccess: async (req) => {
return await authService.isAuthorized(req);
},
requiredRoles: ['admin'],
},
})
4. Audit Access
Log access attempts:
NestLensModule.forRoot({
authorization: {
canAccess: async (req) => {
const user = await authService.authenticate(req);
// Log access attempt
await auditLog.log({
action: 'nestlens_access',
user: user?.id,
ip: req.ip,
allowed: !!user,
});
return user ? { id: user.id, roles: user.roles } : false;
},
},
})
5. Use HTTPS
Always use HTTPS in production:
// In your main.ts
if (process.env.NODE_ENV === 'production') {
app.use((req, res, next) => {
if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
next();
} else {
res.redirect(`https://${req.headers.host}${req.url}`);
}
});
}
Troubleshooting
Access Denied Issues
Issue: Can't access dashboard despite correct credentials
Debug Steps:
-
Check Environment:
console.log('Current ENV:', process.env.NODE_ENV);
console.log('Allowed ENVs:', config.authorization.allowedEnvironments); -
Check IP:
console.log('Client IP:', req.ip);
console.log('Allowed IPs:', config.authorization.allowedIps); -
Test canAccess:
const result = await config.authorization.canAccess(req);
console.log('canAccess result:', result); -
Check Roles:
console.log('User roles:', user.roles);
console.log('Required roles:', config.authorization.requiredRoles);
Rate Limit Errors
Issue: Getting 429 Too Many Requests
Solutions:
-
Increase Limits:
rateLimit: {
maxRequests: 500, // Increase limit
} -
Disable Rate Limiting:
rateLimit: false, -
Check IP Binding:
- Multiple users behind same IP will share limit
- Consider per-user rate limiting if needed
Next Steps
- Configure IP Whitelisting
- Learn about Data Masking
- Review Production Usage Best Practices