Authentication and Authorization
Authentication and Authorization
Section titled “Authentication and Authorization”Complete reference for authentication and authorization in the Banyan Platform.
Overview
Section titled “Overview”The platform implements a comprehensive two-layer security model:
- Layer 1: Permission-Based (API Gateway) - Who can call what
- Layer 2: Policy-Based (Service Handlers) - Business rule enforcement
JWT Token Structure
Section titled “JWT Token Structure”Token Claims
Section titled “Token Claims”{ "sub": "user-123", "email": "user@example.com", "name": "John Doe", "permissions": [ "users:create", "users:read", "users:update", "orders:read" ], "iat": 1700056800, "exp": 1700060400}Required Claims
Section titled “Required Claims”| Claim | Type | Description |
|---|---|---|
sub | string | User ID (subject) |
email | string | User’s email address |
name | string | User’s display name |
permissions | string[] | Array of permission strings |
exp | number | Token expiration timestamp |
Token Generation
Section titled “Token Generation”import * as jwt from 'jsonwebtoken';
const token = jwt.sign( { sub: user.userId, email: user.email, name: user.name, permissions: user.permissions, }, process.env.JWT_SECRET, { expiresIn: '1h', algorithm: 'HS256' });Configuration:
- Algorithm: HS256 (HMAC with SHA-256)
- Secret: Must match
JWT_SECRETenvironment variable - Expiration: Typically 1 hour
- Subject: User ID for identification
Token Validation
Section titled “Token Validation”The platform automatically validates:
- ✅ Signature using
JWT_SECRET - ✅ Expiration time
- ✅ Required claims presence
- ✅ Token format and structure
Invalid tokens result in:
{ "error": "Unauthorized", "message": "Invalid or expired token", "code": "INVALID_TOKEN"}Permission Model
Section titled “Permission Model”Permission Format
Section titled “Permission Format”Structure: resource:action
Examples:
users:create # Create usersusers:read # Read user datausers:update # Update usersusers:delete # Delete usersorders:read # Read ordersorders:approve # Approve ordersadmin:all # All admin permissionsPermission Strings
Section titled “Permission Strings”// Resource-based permissions'users:create''users:read''users:update''users:delete'
// Action-specific permissions'orders:approve''orders:cancel''orders:refund'
// Administrative permissions'admin:all''admin:users''admin:reports'
// Read-only permissions'reports:read''analytics:read'Declaring Required Permissions
Section titled “Declaring Required Permissions”In command/query contracts:
import { Command } from '@banyanai/platform-contract-system';
@Command({ description: 'Create a new user account', permissions: ['users:create']})export class CreateUserCommand { email!: string; name!: string;}Multiple permissions (OR logic):
@Command({ description: 'Update any user account (admin)', permissions: ['users:update', 'admin:all']})export class UpdateAnyUserCommand { // User needs EITHER users:update OR admin:all}Two-Layer Authorization
Section titled “Two-Layer Authorization”Layer 1: Permission-Based (API Gateway)
Section titled “Layer 1: Permission-Based (API Gateway)”When: Before message creation Where: API Gateway What: Validates user has required permissions
@Command({ permissions: ['users:update']})export class UpdateUserCommand { userId!: string; name!: string;}API Gateway checks:
- Extract permissions from JWT token
- Compare with
permissionsin contract - Reject request if permissions missing
- Create message if permissions match
Failure response:
{ "error": "Unauthorized", "message": "Missing required permission: users:update", "code": "INSUFFICIENT_PERMISSIONS"}Layer 2: Policy-Based (Service Handler)
Section titled “Layer 2: Policy-Based (Service Handler)”When: After message received Where: Service handler What: Validates business rules
// Policy class (co-located with handler)export class UpdateOwnProfilePolicy { static canExecute(user: AuthenticatedUser, command: UpdateUserCommand): boolean { // Users can only update their own profile (unless admin) return user.permissions.includes('admin:all') || user.userId === command.userId; }}
// Handler with policy@CommandHandler(UpdateUserCommand)@RequiresPermissions('users:update')@RequirePolicy('UpdateOwnProfilePolicy')export class UpdateUserHandler { async handle(command: UpdateUserCommand, context: CommandContext): Promise<UserDto> { // Policy already validated - safe to proceed }}Failure response:
{ "error": "Unauthorized", "message": "Policy violation: User cannot update another user's profile", "code": "POLICY_VIOLATION"}Authentication Modes
Section titled “Authentication Modes”Production Mode (JWT Tokens)
Section titled “Production Mode (JWT Tokens)”Default mode for production environments:
Authorization: Bearer <jwt-token>JWT token contains:
- User ID (subject claim)
- Email address
- Display name
- Permissions array
- Expiration time
Development Mode
Section titled “Development Mode”CRITICAL SECURITY WARNING
Development mode (
DEVELOPMENT_AUTH_ENABLED=true) BYPASSES ALL AUTHENTICATION AND AUTHORIZATION.
- ❌ NO JWT validation
- ❌ NO token verification
- ❌ NO permission enforcement
- ❌ NO security checks of any kind
- ⚠️ Anyone can impersonate ANY user with ANY permissions
NEVER enable in production. NEVER commit
.envwith this enabled.
For local development only:
# Set user identity and permissions via headersX-Dev-User-Id: user-123X-Dev-Permissions: users:create,users:read,orders:createDevelopment mode behavior:
- Bypasses all JWT validation
- Accepts any user ID from headers
- Grants any permissions from headers
- Should ONLY be used on localhost
- Must be explicitly enabled via environment variable
Production Security Checklist:
- ✅ NEVER set
DEVELOPMENT_AUTH_ENABLED=truein production - ✅ NEVER commit this setting to version control
- ✅ Set up CI/CD checks to block dev mode deployment
- ✅ Use environment-specific configuration
- ✅ Audit logs to detect unauthorized dev mode usage
Authentication Flow
Section titled “Authentication Flow”Production Flow (JWT)
Section titled “Production Flow (JWT)”1. Client obtains JWT token from auth service └─> Token contains: userId, email, name, permissions
2. Client includes token in request └─> Authorization: Bearer <token>
3. API Gateway extracts token └─> Validates signature and expiration
4. Gateway extracts permissions from token └─> permissions: ['users:create', 'users:read']
5. Gateway validates against contract └─> requiredPermissions: ['users:create'] └─> Check: Does user have 'users:create'? Yes ✓
6. Gateway creates authenticated message └─> Embeds auth context in message metadata
7. Message sent to service via RabbitMQ └─> Service receives message with auth context
8. Service handler evaluates policy (if present) └─> @RequirePolicy checks business rules
9. Handler executes business logic └─> Returns result to gateway
10. Gateway returns HTTP response └─> 200 OK with result dataDevelopment Flow (Headers)
Section titled “Development Flow (Headers)”1. Client sends request with dev headers └─> X-Dev-User-Id: user-123 └─> X-Dev-Permissions: users:create,users:read
2. Gateway checks DEVELOPMENT_AUTH_ENABLED └─> Must be explicitly enabled
3. Gateway extracts user from headers └─> Creates mock AuthenticatedUser
4. Gateway validates permissions └─> Same as production flow
5. Message created and sent to service └─> Same as production flow
6. Handler executes with mock user └─> Same as production flowToken Refresh Patterns
Section titled “Token Refresh Patterns”Short-Lived Access Tokens
Section titled “Short-Lived Access Tokens”Recommended pattern:
// Access token: 1 hourconst accessToken = jwt.sign(payload, secret, { expiresIn: '1h' });
// Refresh token: 7 daysconst refreshToken = jwt.sign({ sub: user.userId }, secret, { expiresIn: '7d' });Refresh Token Endpoint
Section titled “Refresh Token Endpoint”POST /api/refresh-tokenContent-Type: application/json
{ "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}Response:
{ "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expiresIn": 3600}Client-Side Token Management
Section titled “Client-Side Token Management”class AuthService { private accessToken: string | null = null; private refreshToken: string | null = null;
async request(url: string, options: RequestInit) { // Add current access token const headers = { ...options.headers, Authorization: `Bearer ${this.accessToken}` };
let response = await fetch(url, { ...options, headers });
// If token expired, refresh and retry if (response.status === 401) { await this.refreshAccessToken();
headers.Authorization = `Bearer ${this.accessToken}`; response = await fetch(url, { ...options, headers }); }
return response; }
private async refreshAccessToken() { const response = await fetch('/api/refresh-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: this.refreshToken }) });
const data = await response.json(); this.accessToken = data.accessToken; }}Common Patterns
Section titled “Common Patterns”Public Endpoints
Section titled “Public Endpoints”Endpoints with no authentication:
@Query({ description: 'Health check endpoint', permissions: [] // No permissions required})export class GetHealthQuery {}Admin-Only Endpoints
Section titled “Admin-Only Endpoints”@Command({ description: 'Permanently delete user account', permissions: ['admin:all']})export class DeleteUserCommand { userId!: string;}Self-Service Endpoints
Section titled “Self-Service Endpoints”@Command({ description: 'Update own profile', permissions: ['users:update']})export class UpdateOwnProfileCommand { userId!: string; name!: string;}
// Policy ensures users can only update their own profileexport class UpdateOwnProfilePolicy { static canExecute(user: AuthenticatedUser, command: UpdateOwnProfileCommand): boolean { return user.userId === command.userId; }}Security Best Practices
Section titled “Security Best Practices”Token Security
Section titled “Token Security”✅ DO:
- Use HTTPS in production (wss:// for WebSocket)
- Set short expiration times (1 hour)
- Use refresh tokens for session management
- Rotate JWT secrets periodically
- Store tokens securely (httpOnly cookies)
❌ DON’T:
- Use HTTP in production
- Set long expiration times (> 24 hours)
- Store tokens in localStorage (XSS risk)
- Share JWT secrets across environments
- Log JWT tokens
Permission Management
Section titled “Permission Management”✅ DO:
- Use specific permissions (
users:create) - Follow resource:action naming convention
- Grant minimal required permissions
- Validate permissions at both layers
- Log permission violations
❌ DON’T:
- Use broad permissions (
admin:allfor everyone) - Use inconsistent naming patterns
- Grant permissions without review
- Skip permission checks
- Ignore authorization logs
Development Mode
Section titled “Development Mode”✅ DO:
- Use development mode for local testing
- Document required permissions
- Test with minimal permissions
- Disable before deployment
❌ DON’T:
- Enable in production
- Skip permission testing
- Use development mode in CI/CD
- Commit DEVELOPMENT_AUTH_ENABLED=true
Error Handling
Section titled “Error Handling”Invalid Token Signature
Section titled “Invalid Token Signature”Check: Is JWT_SECRET correct?Check: Was token generated with same secret?Check: Is token format correct?Fix: Ensure JWT_SECRET matches between auth service and gatewayToken Expired
Section titled “Token Expired”Check: Is token expiration time appropriate?Check: Are server clocks synchronized?Fix: Implement token refresh flowFix: Use refresh tokens for session managementMissing Permission
Section titled “Missing Permission”Check: Is permission string correct?Check: Does user have permission in JWT?Check: Is permissions in contract correct?Fix: Grant permission to userFix: Update contract permissionsDevelopment Mode Not Working
Section titled “Development Mode Not Working”Check: Is DEVELOPMENT_AUTH_ENABLED=true?Check: Are headers correctly formatted?Check: Is X-Dev-Permissions comma-separated?Fix: Set environment variableFix: Use correct header names (X-Dev-User-Id, X-Dev-Permissions)Next Steps
Section titled “Next Steps”- REST API - HTTP API authentication
- GraphQL API - GraphQL authentication
- WebSocket API - WebSocket authentication
- Authorization Architecture - Deep dive on two-layer model