Auth Service
Auth Service
Section titled “Auth Service”The Auth Service manages user authentication, authorization, roles, permissions, and session management for the platform.
Overview
Section titled “Overview”The Auth Service provides:
- User Management - Create, update, and delete user accounts
- Authentication - Email/password and external provider (OAuth, SAML)
- Authorization - Role-based and permission-based access control
- Session Management - Stateful sessions with refresh tokens
- JWT Token Generation - Signed tokens with user context
- Password Security - Bcrypt hashing with configurable rounds
- Event Sourcing - Complete audit trail of all auth operations
Architecture
Section titled “Architecture”┌─────────────────────────────────────────────────────────┐│ Auth Service ││ ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ││ │ Commands │ │ Queries │ │ Events │ ││ │ │ │ │ │ │ ││ │ CreateUser │ │ GetUser │ │UserCreated │ ││ │ Authenticate│ │ ListUsers │ │UserUpdated │ ││ │ AssignRole │ │ GetRole │ │RoleAssigned │ ││ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ ││ │ │ │ ││ └─────────┬───────┴─────────┬───────┘ ││ │ │ ││ ┌─────────▼─────────────────▼─────────┐ ││ │ Event-Sourced Aggregates │ ││ │ - UserAggregate │ ││ │ - RoleAggregate │ ││ │ - SessionAggregate │ ││ └─────────┬──────────────────┘ ││ │ ││ ┌─────────▼─────────────┐ ││ │ Event Store (PG) │ ││ └───────────────────────┘ │└───────────────────────────────────────────────────┘Configuration
Section titled “Configuration”Environment Variables
Section titled “Environment Variables”# Port ConfigurationPORT=3001 # HTTP port
# Database (Event Store)DATABASE_HOST=postgres # PostgreSQL hostDATABASE_NAME=eventstore # Database nameDATABASE_USER=actor_user # Database userDATABASE_PASSWORD=actor_pass123 # Database passwordDATABASE_PORT=5432 # Database port
# Legacy database URL (for compatibility)AUTH_DATABASE_URL=postgresql://actor_user:actor_pass123@postgres:5432/eventstore
# Message BusMESSAGE_BUS_URL=amqp://admin:admin123@rabbitmq:5672
# JWT ConfigurationJWT_SECRET=your-secret-key # JWT signing secret (CHANGE IN PRODUCTION)JWT_EXPIRATION=3600 # Access token expiration (seconds)REFRESH_TOKEN_EXPIRATION=604800 # Refresh token expiration (7 days)
# Password SecurityBCRYPT_ROUNDS=12 # Bcrypt hashing rounds
# Bootstrap ConfigurationADMIN_EMAIL=admin@example.com # Default admin user emailADMIN_PASSWORD=AdminPassword123! # Default admin password (CHANGE!)
# TelemetryJAEGER_ENDPOINT=http://jaeger:4318/v1/tracesDocker Compose
Section titled “Docker Compose”auth-service: build: ./platform/services/auth-service ports: - "3001:3001" environment: - PORT=3001 - DATABASE_HOST=postgres - DATABASE_NAME=eventstore - DATABASE_USER=actor_user - DATABASE_PASSWORD=actor_pass123 - MESSAGE_BUS_URL=amqp://admin:admin123@rabbitmq:5672 - JWT_SECRET=${JWT_SECRET} - BCRYPT_ROUNDS=12 - ADMIN_EMAIL=admin@example.com depends_on: postgres: condition: service_healthy rabbitmq: condition: service_healthyCommands
Section titled “Commands”CreateUser
Section titled “CreateUser”Create a new user account.
Permission Required: auth:create-user
Input:
{ email: "user@example.com", password: "SecurePassword123!", profile: { firstName: "John", lastName: "Doe", displayName: "John Doe", timezone: "America/New_York", locale: "en-US" }, initialRoles: ["user"], skipEmailVerification: false}Output:
{ success: true, userId: "user-123", email: "user@example.com", error: null, validationErrors: null}Events Published:
UserCreated- User account createdRoleAssigned- If initial roles provided
AuthenticateUser
Section titled “AuthenticateUser”Authenticate with email and password.
Permission Required: None (public)
Input:
{ email: "user@example.com", password: "SecurePassword123!"}Output:
{ success: true, accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", expiresIn: 3600, user: { id: "user-123", email: "user@example.com", permissions: ["users:read", "orders:read"] }}Events Published:
SessionCreated- New session established
RefreshToken
Section titled “RefreshToken”Refresh an expired access token.
Permission Required: None (requires valid refresh token)
Input:
{ refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}Output:
{ success: true, accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", expiresIn: 3600}CreateRole
Section titled “CreateRole”Create a new role with permissions.
Permission Required: auth:manage-roles
Input:
{ roleName: "editor", description: "Content editor role", permissions: ["users:read", "content:edit", "content:publish"]}Output:
{ success: true, roleName: "editor", description: "Content editor role", permissions: ["users:read", "content:edit", "content:publish"]}Events Published:
RoleCreated- New role created
AssignRole
Section titled “AssignRole”Assign a role to a user.
Permission Required: auth:assign-roles
Input:
{ userId: "user-123", roleName: "editor"}Output:
{ success: true, userId: "user-123", roleName: "editor"}Events Published:
RoleAssigned- Role assigned to user
UpdateRolePermissions
Section titled “UpdateRolePermissions”Update permissions for an existing role.
Permission Required: auth:manage-roles
Input:
{ roleName: "editor", permissions: ["users:read", "content:edit", "content:publish", "content:delete"]}Events Published:
RolePermissionsUpdated- Role permissions modified
Queries
Section titled “Queries”GetUser
Section titled “GetUser”Retrieve a user by ID.
Permission Required: auth:view-users
Input:
{ userId: "user-123"}Output:
{ success: true, user: { id: "user-123", email: "user@example.com", profile: { firstName: "John", lastName: "Doe", displayName: "John Doe", timezone: "America/New_York" }, isActive: true, emailVerified: true, lastLogin: "2025-11-15T10:30:00Z", createdAt: "2025-01-01T00:00:00Z", updatedAt: "2025-11-15T10:30:00Z" }}ListUsers
Section titled “ListUsers”List all users with pagination.
Permission Required: auth:view-users
Input:
{ page: 1, pageSize: 20, filter: "active"}Output:
{ users: [ { id: "user-123", email: "user@example.com", profile: { firstName: "John", lastName: "Doe" }, isActive: true } ], totalCount: 42, page: 1, pageSize: 20, hasMore: true}GetUserPermissions
Section titled “GetUserPermissions”Get all permissions for a user (direct + role-based).
Permission Required: auth:view-permissions
Input:
{ userId: "user-123"}Output:
{ userId: "user-123", permissions: ["users:read", "users:create", "content:edit"], roles: [ { roleName: "editor", permissions: ["users:read", "content:edit"] }, { roleName: "user", permissions: ["users:read"] } ]}GetRole
Section titled “GetRole”Get role details by name.
Permission Required: auth:view-roles
Input:
{ roleName: "editor"}Output:
{ roleName: "editor", description: "Content editor role", permissions: ["users:read", "content:edit", "content:publish"], createdAt: "2025-01-01T00:00:00Z", updatedAt: "2025-01-15T10:00:00Z"}ListRoles
Section titled “ListRoles”List all roles.
Permission Required: auth:view-roles
Output:
{ roles: [ { roleName: "admin", description: "Administrator", permissions: ["admin:all"] }, { roleName: "editor", description: "Content editor", permissions: ["users:read", "content:edit"] } ], totalCount: 2}Events
Section titled “Events”UserCreated
Section titled “UserCreated”Published when a new user account is created.
{ userId: "user-123", email: "user@example.com", profile: { firstName: "John", lastName: "Doe" }, createdAt: "2025-11-15T14:30:00Z", timestamp: "2025-11-15T14:30:00.123Z"}UserUpdated
Section titled “UserUpdated”Published when user profile is updated.
{ userId: "user-123", email: "user@example.com", updatedFields: ["profile.firstName", "profile.timezone"], updatedAt: "2025-11-15T14:35:00Z", updatedBy: "user-123", timestamp: "2025-11-15T14:35:00.456Z"}RoleAssigned
Section titled “RoleAssigned”Published when a role is assigned to a user.
{ userId: "user-123", roleName: "editor", assignedBy: "admin-user", timestamp: "2025-11-15T14:40:00.789Z"}SessionCreated
Section titled “SessionCreated”Published when a user authenticates.
{ sessionId: "session-456", userId: "user-123", deviceInfo: "Mozilla/5.0...", ipAddress: "192.168.1.100", createdAt: "2025-11-15T14:30:00Z", expiresAt: "2025-11-22T14:30:00Z", timestamp: "2025-11-15T14:30:00.123Z"}Security Features
Section titled “Security Features”Password Hashing
Section titled “Password Hashing”// Bcrypt with configurable roundsconst hashedPassword = await bcrypt.hash(password, BCRYPT_ROUNDS);
// Default: 12 rounds (production)// Dev: Can use lower for faster testsJWT Token Generation
Section titled “JWT Token Generation”// Access tokenconst accessToken = jwt.sign( { sub: user.userId, email: user.email, name: user.profile.displayName, permissions: user.getAllPermissions() }, JWT_SECRET, { expiresIn: JWT_EXPIRATION });
// Refresh tokenconst refreshToken = jwt.sign( { sub: user.userId, type: 'refresh' }, JWT_SECRET, { expiresIn: REFRESH_TOKEN_EXPIRATION });Session Management
Section titled “Session Management”- Sessions stored in event store
- Automatic cleanup of expired sessions
- Support for multiple concurrent sessions
- Force logout by revoking all sessions
External Authentication
Section titled “External Authentication”OAuth Support:
- GitHub
- Custom OAuth providers
SAML Support:
- Enterprise SSO integration
- Configurable identity providers
Data Model
Section titled “Data Model”User Aggregate
Section titled “User Aggregate”class UserAggregate extends AggregateRoot { id: string; email: string; passwordHash: string; profile: UserProfile; roles: string[]; directPermissions: string[]; isActive: boolean; emailVerified: boolean; lastLogin?: Date;
// Domain logic assignRole(roleName: string): void; updateProfile(profile: UserProfile): void; verifyEmail(): void; deactivate(): void;}Role Aggregate
Section titled “Role Aggregate”class RoleAggregate extends AggregateRoot { roleName: string; description?: string; permissions: string[]; createdAt: Date; updatedAt: Date;
// Domain logic updatePermissions(permissions: string[]): void; addPermission(permission: string): void; removePermission(permission: string): void;}Session Aggregate
Section titled “Session Aggregate”class SessionAggregate extends AggregateRoot { sessionId: string; userId: string; refreshToken: string; deviceInfo?: string; ipAddress?: string; createdAt: Date; expiresAt: Date; revokedAt?: Date;
// Domain logic refresh(): void; revoke(): void; isExpired(): boolean;}Event Store Schema
Section titled “Event Store Schema”-- Events table (append-only)CREATE TABLE events ( id SERIAL PRIMARY KEY, aggregate_id VARCHAR(255) NOT NULL, aggregate_type VARCHAR(100) NOT NULL, event_type VARCHAR(100) NOT NULL, event_data JSONB NOT NULL, metadata JSONB, version INTEGER NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(aggregate_id, version));
-- Snapshots table (performance optimization)CREATE TABLE snapshots ( aggregate_id VARCHAR(255) PRIMARY KEY, aggregate_type VARCHAR(100) NOT NULL, snapshot_data JSONB NOT NULL, version INTEGER NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW());Read Models
Section titled “Read Models”The Auth Service maintains read models for efficient querying:
UserReadModel
Section titled “UserReadModel”CREATE TABLE user_read_model ( id VARCHAR(255) PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, profile JSONB NOT NULL, roles JSONB NOT NULL, direct_permissions JSONB NOT NULL, is_active BOOLEAN NOT NULL DEFAULT true, email_verified BOOLEAN NOT NULL DEFAULT false, last_login TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE NOT NULL, updated_at TIMESTAMP WITH TIME ZONE NOT NULL);
CREATE INDEX idx_user_email ON user_read_model(email);CREATE INDEX idx_user_active ON user_read_model(is_active);RoleReadModel
Section titled “RoleReadModel”CREATE TABLE role_read_model ( role_name VARCHAR(255) PRIMARY KEY, description TEXT, permissions JSONB NOT NULL, created_at TIMESTAMP WITH TIME ZONE NOT NULL, updated_at TIMESTAMP WITH TIME ZONE NOT NULL);SessionReadModel
Section titled “SessionReadModel”CREATE TABLE session_read_model ( session_id VARCHAR(255) PRIMARY KEY, user_id VARCHAR(255) NOT NULL, refresh_token VARCHAR(500) NOT NULL, device_info TEXT, ip_address VARCHAR(50), created_at TIMESTAMP WITH TIME ZONE NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, revoked_at TIMESTAMP WITH TIME ZONE, FOREIGN KEY (user_id) REFERENCES user_read_model(id));
CREATE INDEX idx_session_user ON session_read_model(user_id);CREATE INDEX idx_session_expires ON session_read_model(expires_at);Monitoring and Observability
Section titled “Monitoring and Observability”Metrics
Section titled “Metrics”# Authentication metricsauth_service_authentications_total{status="success"}auth_service_authentications_total{status="failed"}auth_service_token_refreshes_total
# User metricsauth_service_users_totalauth_service_active_users_totalauth_service_user_registrations_total
# Session metricsauth_service_active_sessions_totalauth_service_session_expirations_totalHealth Check
Section titled “Health Check”curl http://localhost:3001/healthResponse:
{ "status": "healthy", "service": "auth-service", "version": "1.0.0", "dependencies": { "database": "healthy", "messageBus": "healthy", "eventStore": "healthy" }}Troubleshooting
Section titled “Troubleshooting”User Cannot Authenticate
Section titled “User Cannot Authenticate”Symptoms:
- Login fails with valid credentials
- “Invalid credentials” error
Checks:
# Check user existsdocker exec -it postgres psql -U actor_user -d eventstore \ -c "SELECT id, email, is_active FROM user_read_model WHERE email='user@example.com';"
# Check password hash# Verify BCRYPT_ROUNDS matches between registration and login
# Check logsdocker logs auth-service | grep "Authentication failed"JWT Token Invalid
Section titled “JWT Token Invalid”Symptoms:
- Token rejected by API Gateway
- “Invalid token” errors
Checks:
# Verify JWT_SECRET matches between servicesdocker exec api-gateway env | grep JWT_SECRETdocker exec auth-service env | grep JWT_SECRET
# Decode token (without verification)echo "eyJhbGci..." | base64 -d
# Check expiration# Token exp claim should be in the futurePermissions Not Working
Section titled “Permissions Not Working”Symptoms:
- User has permission but still gets 403
- Permissions not reflected in token
Checks:
# Check user permissionscurl -X GET http://localhost:3003/api/users/user-123/permissions \ -H "Authorization: Bearer <admin-token>"
# Check role permissionscurl -X GET http://localhost:3003/api/roles/editor \ -H "Authorization: Bearer <admin-token>"
# Re-authenticate to get fresh tokencurl -X POST http://localhost:3003/api/auth/authenticate \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":"password"}'Next Steps
Section titled “Next Steps”- API Gateway - Protocol translation service
- Service Discovery - Service registry
- Authentication Reference - Complete auth guide
- Event Sourcing Package - Event store implementation