Files
pos-system/services/iam-service-net/docs/en/ARCHITECTURE.md

23 KiB

IAM Service Architecture

Architecture documentation for IAM (Identity and Access Management) Service built with .NET 10, Duende IdentityServer, and Clean Architecture.

Architecture Overview

graph TB
    subgraph "Clients"
        WEB[Web App]
        MOB[Mobile App]
        SVC[Other Services]
        SOCIAL[Google/Facebook]
    end
    
    subgraph "API Layer"
        AUTH[AuthController]
        USR[UsersController]
        TOK[Token Endpoint]
    end
    
    subgraph "Application Layer - CQRS"
        CMD[Commands]
        QRY[Queries]
        VAL[Validators]
        BHV[Behaviors]
    end
    
    subgraph "Domain Layer"
        USER[User Aggregate]
        ROLE[Role Aggregate]
        EVT[Domain Events]
    end
    
    subgraph "Infrastructure"
        CTX[Identity DbContext]
        REPO[Repositories]
        IDSERVER[Duende IdentityServer]
        EMAIL[Email Service]
        TOTP[2FA Service]
        OAUTH[Social Login Service]
    end
    
    subgraph "External"
        DB[(PostgreSQL)]
        REDIS[(Redis)]
        SMTP[SMTP Server]
    end
    
    WEB --> AUTH
    MOB --> AUTH
    SVC --> TOK
    SOCIAL --> AUTH
    AUTH --> CMD
    AUTH --> QRY
    USR --> CMD
    USR --> QRY
    TOK --> IDSERVER
    CMD --> VAL
    CMD --> BHV
    CMD --> USER
    QRY --> REPO
    USER --> EVT
    REPO --> CTX
    IDSERVER --> CTX
    CTX --> DB
    CTX --> REDIS
    EMAIL --> SMTP
    OAUTH --> SOCIAL
    
    style AUTH fill:#4a90d9,stroke:#2d5986,color:#fff
    style USER fill:#50c878,stroke:#2d8659,color:#fff
    style DB fill:#ff6b6b,stroke:#c0392b,color:#fff
    style IDSERVER fill:#9b59b6,stroke:#7d3c98,color:#fff
    style EMAIL fill:#e67e22,stroke:#d35400,color:#fff
    style TOTP fill:#1abc9c,stroke:#16a085,color:#fff
    style OAUTH fill:#3498db,stroke:#2980b9,color:#fff

OAuth2 Authentication Flow

sequenceDiagram
    participant Client
    participant AuthController
    participant IdentityServer
    participant UserManager
    participant Database
    
    Note over Client,Database: Password Grant Flow (User Login)
    
    Client->>AuthController: POST /connect/token<br/>grant_type=password
    AuthController->>IdentityServer: Validate Request
    IdentityServer->>UserManager: FindByEmailAsync()
    UserManager->>Database: Query User
    Database-->>UserManager: User Data
    UserManager->>UserManager: CheckPasswordAsync()
    UserManager-->>IdentityServer: User Validated
    IdentityServer->>IdentityServer: Generate Tokens (JWT)
    IdentityServer-->>AuthController: Token Response
    AuthController-->>Client: access_token + refresh_token
    
    Note over Client,Database: Using Access Token
    
    Client->>AuthController: GET /api/v1/users/me<br/>Authorization: Bearer {token}
    AuthController->>IdentityServer: Validate JWT
    IdentityServer-->>AuthController: Claims Principal
    AuthController-->>Client: User Data

Token Types and Flows

graph LR
    subgraph "Grant Types"
        PWD[Password Grant]
        REF[Refresh Token]
        CC[Client Credentials]
    end
    
    subgraph "Tokens"
        AT[Access Token<br/>15 min]
        RT[Refresh Token<br/>7 days]
    end
    
    subgraph "Use Cases"
        USER[User Login]
        RENEW[Token Renewal]
        S2S[Service-to-Service]
    end
    
    PWD --> AT
    PWD --> RT
    REF --> AT
    CC --> AT
    
    USER --> PWD
    RENEW --> REF
    S2S --> CC
    
    style AT fill:#2ecc71,stroke:#27ae60,color:#fff
    style RT fill:#f39c12,stroke:#d68910,color:#fff
    style CC fill:#9b59b6,stroke:#7d3c98,color:#fff

Domain Model

User Aggregate

classDiagram
    class ApplicationUser {
        +Guid Id
        +string Email
        +string FirstName
        +string LastName
        +UserStatus Status
        +DateTime CreatedAt
        +DateTime? LastLoginAt
        +UpdateProfile(firstName, lastName)
        +Disable()
        +RecordLogin()
    }
    
    class UserStatus {
        <<enumeration>>
        +Active
        +Locked
        +Disabled
        +PendingVerification
    }
    
    class ApplicationRole {
        +Guid Id
        +string Name
        +string Description
    }
    
    ApplicationUser --> UserStatus : has
    ApplicationUser "many" --> "many" ApplicationRole : belongs to

Database Schema

erDiagram
    AspNetUsers {
        uuid Id PK
        string Email UK
        string PasswordHash
        string FirstName
        string LastName
        int StatusId FK
        datetime CreatedAt
        datetime LastLoginAt
    }
    
    UserStatuses {
        int Id PK
        string Name
    }
    
    AspNetRoles {
        uuid Id PK
        string Name UK
        string Description
    }
    
    AspNetUserRoles {
        uuid UserId PK,FK
        uuid RoleId PK,FK
    }
    
    IdentityServerPersistedGrants {
        uuid Id PK
        string Key UK
        string Type
        string ClientId
        datetime CreationTime
        datetime Expiration
    }
    
    AspNetUsers ||--o{ UserStatuses : has
    AspNetUsers ||--o{ AspNetUserRoles : has
    AspNetRoles ||--o{ AspNetUserRoles : has

Phase 2: Organization & Group Aggregates

classDiagram
    class Organization {
        +Guid Id
        +string Name
        +string Slug
        +Guid? ParentId
        +OrganizationStatus Status
        +Create()
        +Update()
        +Archive()
    }
    
    class Group {
        +Guid Id
        +Guid OrganizationId
        +string Name
        +string Description
        +AddMember()
        +RemoveMember()
    }
    
    class GroupMember {
        +Guid GroupId
        +Guid UserId
        +GroupRole Role
        +DateTime JoinedAt
    }
    
    Organization "1" --> "*" Group : contains
    Group "1" --> "*" GroupMember : has

Phase 3A: Access Request Aggregate

classDiagram
    class AccessRequest {
        +Guid Id
        +Guid RequesterId
        +string ResourceType
        +Guid ResourceId
        +string RequestedPermission
        +AccessRequestStatus Status
        +AccessRequestPriority Priority
        +DateTime DueDate
        +Submit()
        +Approve()
        +Reject()
        +Cancel()
    }
    
    class AccessRequestApprover {
        +Guid RequestId
        +Guid UserId
        +int ApprovalOrder
        +ApproverStatus Status
        +string Comments
        +Approve()
        +Reject()
    }
    
    class AccessRequestStatus {
        <<enumeration>>
        +Draft
        +Pending
        +Approved
        +Rejected
        +Cancelled
        +Expired
    }
    
    AccessRequest "1" --> "*" AccessRequestApprover : has
    AccessRequest --> AccessRequestStatus : has

Phase 3B: Access Review & PAM Aggregates

classDiagram
    class AccessReview {
        +Guid Id
        +string Name
        +Guid OwnerId
        +string Scope
        +AccessReviewStatus Status
        +DateTime DueDate
        +Start()
        +Complete()
        +Cancel()
    }
    
    class AccessReviewItem {
        +Guid Id
        +Guid UserId
        +string ResourceType
        +Guid ResourceId
        +ReviewDecision Decision
        +Certify()
        +Revoke()
    }
    
    class PrivilegedAccessGrant {
        +Guid Id
        +Guid UserId
        +Guid RoleId
        +string ResourceScope
        +PrivilegedAccessStatus Status
        +DateTime StartsAt
        +DateTime ExpiresAt
        +Activate()
        +Revoke()
        +Extend()
    }
    
    AccessReview "1" --> "*" AccessReviewItem : contains

Phase 4A: Audit & Compliance Aggregates

classDiagram
    class AuditLog {
        +Guid Id
        +AuditEventType EventType
        +Guid? ActorId
        +string ResourceType
        +Guid? ResourceId
        +string Action
        +bool Success
        +DateTime Timestamp
        +LoginEvent()
        +AccessGrantedEvent()
    }
    
    class ComplianceReport {
        +Guid Id
        +string Name
        +ComplianceReportType ReportType
        +ComplianceReportStatus Status
        +int TotalChecks
        +int PassedChecks
        +double CompliancePercentage
        +StartGenerating()
        +Complete()
        +Fail()
    }
    
    class ComplianceViolation {
        +Guid Id
        +string Rule
        +ViolationSeverity Severity
        +string Description
        +bool Resolved
        +Resolve()
    }
    
    ComplianceReport "1" --> "*" ComplianceViolation : has

AuditEventType (18 Event Types)

Category Event Types
Authentication Login, Logout, LoginFailed, PasswordChanged, TwoFactorEnabled/Disabled
User Management UserCreated, UserUpdated, UserDeleted, UserLocked/Unlocked
Access Control AccessRequested, AccessGranted, AccessRevoked, AccessDenied, PrivilegedAccessGranted/Revoked
Organization OrganizationCreated/Updated, GroupMemberAdded/Removed
Policy PolicyCreated, PolicyActivated, PolicyDeactivated
Compliance ComplianceReportGenerated, ViolationDetected, ViolationResolved

CQRS Pipeline

sequenceDiagram
    participant Controller
    participant MediatR
    participant LoggingBehavior
    participant ValidatorBehavior
    participant TransactionBehavior
    participant CommandHandler
    participant Repository
    
    Controller->>MediatR: Send(Command)
    MediatR->>LoggingBehavior: Handle
    LoggingBehavior->>ValidatorBehavior: Next()
    ValidatorBehavior->>TransactionBehavior: Next()
    TransactionBehavior->>CommandHandler: Next()
    CommandHandler->>Repository: Save
    Repository-->>CommandHandler: Result
    CommandHandler-->>Controller: Response

Pipeline Behaviors

Order Behavior Purpose
1 LoggingBehavior Log request/response with timing
2 ValidatorBehavior FluentValidation
3 TransactionBehavior Database transaction wrapper

Security Architecture

graph TD
    subgraph "Authentication"
        JWT[JWT Bearer Tokens]
        RS256[RS256 Signing]
        OIDC[IdentityServer]
        MFA[2FA/TOTP]
        SOCIAL[Social OAuth]
    end
    
    subgraph "Authorization"
        RBAC[Role-Based Access]
        CLAIMS[Claims-Based]
        POLICY[Policy Enforcement]
    end
    
    subgraph "Protection"
        HASH[bcrypt Password Hash]
        HTTPS[HTTPS/TLS]
        CORS[CORS Policy]
        EMAIL[Email Verification]
    end
    
    JWT --> RS256
    RS256 --> OIDC
    RBAC --> CLAIMS
    CLAIMS --> POLICY
    MFA --> JWT
    SOCIAL --> JWT
    
    style JWT fill:#3498db,stroke:#2980b9,color:#fff
    style RBAC fill:#e74c3c,stroke:#c0392b,color:#fff
    style HASH fill:#2ecc71,stroke:#27ae60,color:#fff
    style MFA fill:#9b59b6,stroke:#7d3c98,color:#fff
    style SOCIAL fill:#e67e22,stroke:#d35400,color:#fff

Authorization Policies

Overview

IAM Service uses Policy-Based Authorization to protect API endpoints:

graph TB
    subgraph "Request Flow"
        REQ[HTTP Request] --> AUTH[Authentication<br/>JWT Bearer]
        AUTH --> POLICY[Policy Check]
        POLICY --> HANDLER[Authorization Handler]
    end
    
    subgraph "Policies"
        SUPER[RequireSuperAdmin]
        ADMIN[RequireAdmin]
        AUDITOR[RequireAuditor]
        OWNER[OwnerOrAdmin]
    end
    
    subgraph "Roles"
        R_SUPER[SuperAdmin]
        R_ADMIN[Admin]
        R_AUDITOR[Auditor]
        R_USER[User]
    end
    
    HANDLER --> SUPER
    HANDLER --> ADMIN
    HANDLER --> AUDITOR
    HANDLER --> OWNER
    
    SUPER --> R_SUPER
    ADMIN --> R_SUPER
    ADMIN --> R_ADMIN
    AUDITOR --> R_SUPER
    AUDITOR --> R_ADMIN
    AUDITOR --> R_AUDITOR
    OWNER --> R_SUPER
    OWNER --> R_ADMIN
    OWNER --> R_USER
    
    style SUPER fill:#c0392b,stroke:#922b21,color:#fff
    style ADMIN fill:#e74c3c,stroke:#c0392b,color:#fff
    style AUDITOR fill:#9b59b6,stroke:#7d3c98,color:#fff
    style OWNER fill:#3498db,stroke:#2980b9,color:#fff

Policy Definitions

Policy Required Roles Description
RequireSuperAdmin SuperAdmin Highest level - PAM, system config
RequireAdmin SuperAdmin, Admin User, role, organization management
RequireAuditor SuperAdmin, Admin, Auditor Audit logs, compliance reports
OwnerOrAdmin Admin or resource owner User self-service profile

Authorization Flow

sequenceDiagram
    participant Client
    participant Controller
    participant AuthorizationService
    participant PolicyHandler
    participant ClaimsPrincipal
    
    Client->>Controller: Request with JWT
    Controller->>AuthorizationService: Authorize(Policy)
    AuthorizationService->>PolicyHandler: EvaluatePolicy()
    PolicyHandler->>ClaimsPrincipal: GetRoles()
    ClaimsPrincipal-->>PolicyHandler: ["Admin", "User"]
    
    alt Role Match
        PolicyHandler-->>AuthorizationService: Success
        AuthorizationService-->>Controller: Authorized
        Controller-->>Client: 200 OK
    else Role Not Match
        PolicyHandler-->>AuthorizationService: Fail
        AuthorizationService-->>Controller: Forbidden
        Controller-->>Client: 403 Forbidden
    end

Controller Policy Mapping

Controller Policy Endpoints
UsersController RequireAdmin / OwnerOrAdmin GET /users (Admin), GET/PUT /{id} (Owner or Admin)
RolesController RequireAdmin All endpoints
OrganizationsController RequireAdmin All endpoints
GroupsController RequireAdmin All endpoints
AccessRequestsController RequireAdmin All endpoints
AccessReviewsController RequireAdmin All endpoints
PrivilegedAccessController RequireSuperAdmin All endpoints (most sensitive)
AuditController RequireAuditor GET /audit/logs
ComplianceController RequireAuditor All endpoints
VerificationsController RequireAdmin All endpoints

Email Verification Flow

sequenceDiagram
    participant User
    participant AuthController
    participant EmailService
    participant SMTP
    participant Database
    
    Note over User,Database: Send Verification Email
    
    User->>AuthController: POST /send-verification-email
    AuthController->>Database: Generate Token
    Database-->>AuthController: Confirmation Token
    AuthController->>EmailService: SendVerificationEmail()
    EmailService->>SMTP: Send Email with Link
    SMTP-->>User: Email with Verification Link
    
    Note over User,Database: Confirm Email
    
    User->>AuthController: POST /confirm-email<br/>(userId, token)
    AuthController->>Database: Validate Token
    Database-->>AuthController: Token Valid
    AuthController->>Database: Set EmailConfirmed = true
    AuthController-->>User: Email Confirmed

Two-Factor Authentication Flow

sequenceDiagram
    participant User
    participant AuthController
    participant TwoFactorService
    participant Database
    participant AuthenticatorApp
    
    Note over User,AuthenticatorApp: Enable 2FA
    
    User->>AuthController: POST /2fa/enable
    AuthController->>TwoFactorService: GenerateSecretKey()
    TwoFactorService-->>AuthController: Secret Key
    AuthController->>TwoFactorService: GenerateQrCode()
    TwoFactorService-->>AuthController: QR Code (Base64)
    AuthController-->>User: Secret + QR Code + Recovery Codes
    User->>AuthenticatorApp: Scan QR Code
    
    Note over User,AuthenticatorApp: Verify & Activate
    
    User->>AuthController: POST /2fa/verify (code)
    AuthController->>TwoFactorService: ValidateCode(secret, code)
    TwoFactorService-->>AuthController: Valid
    AuthController->>Database: Store Secret & Enable 2FA
    AuthController-->>User: 2FA Enabled

Social Login Flow

sequenceDiagram
    participant User
    participant Client
    participant AuthController
    participant OAuthProvider
    participant SocialLoginService
    participant Database
    
    Note over User,Database: OAuth Flow
    
    User->>Client: Click "Login with Google"
    Client->>AuthController: GET /external-login/Google
    AuthController->>OAuthProvider: Redirect to OAuth
    OAuthProvider->>User: Login & Consent
    User->>OAuthProvider: Approve
    OAuthProvider->>AuthController: GET /external-callback (code)
    AuthController->>SocialLoginService: ProcessExternalLogin()
    SocialLoginService->>Database: Find/Create User
    Database-->>SocialLoginService: User
    SocialLoginService-->>AuthController: User + Tokens
    AuthController-->>Client: Redirect with tokens

Health Checks

graph TD
    HC[Health Check Endpoints]
    HC -->|/health/live| L[Liveness Probe]
    HC -->|/health/ready| R[Readiness Probe]
    HC -->|/health| F[Full Status]
    
    R --> PG[(PostgreSQL Check)]
    R --> RD[(Redis Check)]
    
    style HC fill:#3498db,stroke:#2980b9,color:#fff
    style L fill:#2ecc71,stroke:#27ae60,color:#fff
    style R fill:#f39c12,stroke:#d68910,color:#fff

Deployment Architecture

Docker Compose (Local/Development)

services:
  iam-service:
    build: .
    ports: ["5001:8080"]
    depends_on:
      - postgres
      - redis
    environment:
      - DATABASE_URL=Host=postgres;...
      
  postgres:
    image: postgres:16-alpine
    
  redis:
    image: redis:7-alpine

Kubernetes (Production)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: iam-service
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: iam-service
        image: goodgo/iam-service:latest
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /health/live
            port: 8080
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 8080

Error Handling

Exception Hierarchy

Exception
└── DomainException
    └── (Custom domain exceptions)

Problem Details (RFC 7807)

All errors return Problem Details format:

{
  "type": "https://tools.ietf.org/html/rfc7807",
  "title": "Validation Error",
  "status": 400,
  "detail": "One or more validation errors occurred.",
  "errors": {
    "Email": ["Email is required"]
  }
}

Distributed Caching Architecture

Caching Overview

graph TB
    subgraph "Application"
        SVC[Services]
        CACHE[ICacheService]
    end
    
    subgraph "Caching Layer"
        REDIS[(Redis Server)]
        TOKEN[Token Cache]
        SESSION[Session Cache]
        DATA[Data Cache]
    end
    
    SVC --> CACHE
    CACHE --> REDIS
    REDIS --> TOKEN
    REDIS --> SESSION
    REDIS --> DATA
    
    style CACHE fill:#e74c3c,stroke:#c0392b,color:#fff
    style REDIS fill:#d35400,stroke:#a04000,color:#fff
    style TOKEN fill:#9b59b6,stroke:#7d3c98,color:#fff
    style SESSION fill:#3498db,stroke:#2980b9,color:#fff
    style DATA fill:#2ecc71,stroke:#27ae60,color:#fff

ICacheService Interface

The service implements a generic ICacheService interface for distributed caching:

Method Purpose
GetAsync<T> Retrieve cached item by key
SetAsync<T> Store item with optional TTL
RemoveAsync Delete cached item
ExistsAsync Check if key exists
GetOrSetAsync<T> Cache-aside pattern
BlacklistAsync Add to token blacklist
IsBlacklistedAsync Check token blacklist

Token Caching Strategy

sequenceDiagram
    participant Client
    participant AuthController
    participant CacheService
    participant Redis
    participant Database
    
    Note over Client,Database: Token Validation with Cache
    
    Client->>AuthController: Request with JWT
    AuthController->>CacheService: IsBlacklistedAsync(tokenId)
    CacheService->>Redis: GET blacklist:token:{id}
    Redis-->>CacheService: null (not blacklisted)
    CacheService-->>AuthController: false
    AuthController->>AuthController: Validate JWT Claims
    AuthController-->>Client: Response
    
    Note over Client,Database: Token Revocation (Logout)
    
    Client->>AuthController: POST /logout
    AuthController->>CacheService: BlacklistAsync(tokenId, 7 days)
    CacheService->>Redis: SETEX blacklist:token:{id} 604800 "1"
    Redis-->>CacheService: OK
    AuthController-->>Client: 200 OK

Token Cache Keys

Key Pattern Purpose TTL
blacklist:token:{tokenId} Revoked tokens Token remaining lifetime
blacklist:refresh:{tokenId} Revoked refresh tokens 7 days
user:{userId}:tokens User's active tokens 15 minutes

Session Caching Strategy

graph LR
    subgraph "Session Data"
        UID[User ID]
        ROLES[User Roles]
        PERMS[Permissions]
        CLAIMS[Claims]
    end
    
    subgraph "Cache Keys"
        K1[session:{userId}]
        K2[user:{userId}:roles]
        K3[user:{userId}:permissions]
    end
    
    UID --> K1
    ROLES --> K2
    PERMS --> K3
    
    style K1 fill:#3498db,stroke:#2980b9,color:#fff
    style K2 fill:#9b59b6,stroke:#7d3c98,color:#fff
    style K3 fill:#2ecc71,stroke:#27ae60,color:#fff

Session Cache Keys

Key Pattern Purpose TTL
session:{userId} User session data 30 minutes
user:{userId}:roles Cached user roles 15 minutes
user:{userId}:permissions Computed permissions 15 minutes
user:{userId}:profile User profile data 10 minutes

Cache-Aside Pattern

sequenceDiagram
    participant Service
    participant CacheService
    participant Redis
    participant Database
    
    Service->>CacheService: GetOrSetAsync(key, factory)
    CacheService->>Redis: GET key
    
    alt Cache Hit
        Redis-->>CacheService: cached data
        CacheService-->>Service: return data
    else Cache Miss
        Redis-->>CacheService: null
        CacheService->>Database: factory() - fetch data
        Database-->>CacheService: data
        CacheService->>Redis: SETEX key ttl data
        CacheService-->>Service: return data
    end

Cache Invalidation Strategies

Strategy When to Use Implementation
Time-based (TTL) General data SetAsync(..., TimeSpan.FromMinutes(15))
Event-based User updates Remove cache on update event
Pattern-based Bulk invalidation RemoveByPatternAsync("user:*")

Performance Considerations

  1. Connection Pooling: EF Core with Npgsql resilience
  2. Token Caching: Redis for token validation and blacklist
  3. Session Caching: User sessions and permissions cached
  4. Async Operations: All I/O operations are async
  5. Database Indexes: Configured in EntityConfigurations
  6. Cache-Aside Pattern: Reduce database load for frequent reads

References