25 KiB
25 KiB
Kiến Trúc IAM Service
Tài liệu kiến trúc cho IAM Service (Quản lý Danh tính và Truy cập) xây dựng với .NET 10, Duende IdentityServer, và Clean Architecture.
Tổng Quan Kiến Trúc
graph TB
subgraph "Clients"
WEB[Web App]
MOB[Mobile App]
SVC[Các Services khác]
SOCIAL[Google/Facebook]
end
subgraph "Lớp API"
AUTH[AuthController]
USR[UsersController]
TOK[Token Endpoint]
end
subgraph "Lớp Application - CQRS"
CMD[Commands]
QRY[Queries]
VAL[Validators]
BHV[Behaviors]
end
subgraph "Lớp Domain"
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
Luồng Xác Thực OAuth2
sequenceDiagram
participant Client
participant AuthController
participant IdentityServer
participant UserManager
participant Database
Note over Client,Database: Password Grant Flow (Đăng nhập)
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: Tạo Tokens (JWT)
IdentityServer-->>AuthController: Token Response
AuthController-->>Client: access_token + refresh_token
Note over Client,Database: Sử dụng 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
Các Loại Token và Grant Types
graph LR
subgraph "Grant Types"
PWD[Password Grant]
REF[Refresh Token]
CC[Client Credentials]
end
subgraph "Tokens"
AT[Access Token<br/>15 phút]
RT[Refresh Token<br/>7 ngày]
end
subgraph "Use Cases"
USER[Đăng nhập User]
RENEW[Làm mới Token]
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 : có
ApplicationUser "nhiều" --> "nhiều" ApplicationRole : thuộc về
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 : có
AspNetUsers ||--o{ AspNetUserRoles : có
AspNetRoles ||--o{ AspNetUserRoles : có
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
Thứ Tự Pipeline Behaviors
| Thứ tự | Behavior | Mục đích |
|---|---|---|
| 1 | LoggingBehavior | Ghi log request/response với timing |
| 2 | ValidatorBehavior | FluentValidation |
| 3 | TransactionBehavior | Bao database transaction |
Kiến Trúc Bảo Mật
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
Tổng Quan Authorization
IAM Service sử dụng Policy-Based Authorization để phân quyền 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
Bảng Policies
| Policy | Required Roles | Mô tả |
|---|---|---|
RequireSuperAdmin |
SuperAdmin | Quyền cao nhất - PAM, system config |
RequireAdmin |
SuperAdmin, Admin | Quản lý users, roles, organizations |
RequireAuditor |
SuperAdmin, Admin, Auditor | Xem audit logs, compliance reports |
OwnerOrAdmin |
Admin hoặc chính user | User tự quản lý profile |
Luồng Authorization
sequenceDiagram
participant Client
participant Controller
participant AuthorizationService
participant PolicyHandler
participant ClaimsPrincipal
Client->>Controller: Request với 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
Áp Dụng Theo Controller
| Controller | Policy | Endpoints |
|---|---|---|
| UsersController | RequireAdmin / OwnerOrAdmin | GET /users (Admin), GET/PUT /{id} (Owner or Admin) |
| RolesController | RequireAdmin | Tất cả endpoints |
| OrganizationsController | RequireAdmin | Tất cả endpoints |
| GroupsController | RequireAdmin | Tất cả endpoints |
| AccessRequestsController | RequireAdmin | Tất cả endpoints |
| AccessReviewsController | RequireAdmin | Tất cả endpoints |
| PrivilegedAccessController | RequireSuperAdmin | Tất cả endpoints (nhạy cảm nhất) |
| AuditController | RequireAuditor | GET /audit/logs |
| ComplianceController | RequireAuditor | Tất cả endpoints |
| VerificationsController | RequireAdmin | Tất cả endpoints |
Custom Authorization Handler: OwnerOrAdmin
// EN: OwnerOrAdmin allows:
// 1. Admin/SuperAdmin - always allowed
// 2. User accessing their own resource (userId in route matches current user)
// VI: OwnerOrAdmin cho phép:
// 1. Admin/SuperAdmin - luôn được phép
// 2. User truy cập resource của chính mình
public class OwnerOrAdminHandler : AuthorizationHandler<OwnerOrAdminRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
OwnerOrAdminRequirement requirement)
{
// Admin/SuperAdmin always allowed
if (context.User.IsInRole("Admin") || context.User.IsInRole("SuperAdmin"))
{
context.Succeed(requirement);
return Task.CompletedTask;
}
// Check if user accessing own resource
var currentUserId = context.User.FindFirst("sub")?.Value;
var routeUserId = httpContext.GetRouteValue("id")?.ToString();
if (currentUserId == routeUserId)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Luồng Xác Thực Email
sequenceDiagram
participant User
participant AuthController
participant EmailService
participant SMTP
participant Database
Note over User,Database: Gửi Email Xác Thực
User->>AuthController: POST /send-verification-email
AuthController->>Database: Tạo Token
Database-->>AuthController: Confirmation Token
AuthController->>EmailService: SendVerificationEmail()
EmailService->>SMTP: Gửi Email với Link
SMTP-->>User: Email với Link Xác Thực
Note over User,Database: Xác Nhận Email
User->>AuthController: POST /confirm-email<br/>(userId, token)
AuthController->>Database: Kiểm tra Token
Database-->>AuthController: Token hợp lệ
AuthController->>Database: Set EmailConfirmed = true
AuthController-->>User: Email đã xác nhận
Luồng Xác Thực Hai Yếu Tố (2FA)
sequenceDiagram
participant User
participant AuthController
participant TwoFactorService
participant Database
participant AuthenticatorApp
Note over User,AuthenticatorApp: Bật 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: Quét QR Code
Note over User,AuthenticatorApp: Xác Minh & Kích Hoạt
User->>AuthController: POST /2fa/verify (code)
AuthController->>TwoFactorService: ValidateCode(secret, code)
TwoFactorService-->>AuthController: Hợp lệ
AuthController->>Database: Lưu Secret & Bật 2FA
AuthController-->>User: 2FA đã được bật
Luồng Đăng Nhập Mạng Xã Hội
sequenceDiagram
participant User
participant Client
participant AuthController
participant OAuthProvider
participant SocialLoginService
participant Database
Note over User,Database: OAuth Flow
User->>Client: Click "Đăng nhập với Google"
Client->>AuthController: GET /external-login/Google
AuthController->>OAuthProvider: Redirect đến OAuth
OAuthProvider->>User: Login & Đồng ý
User->>OAuthProvider: Chấp thuận
OAuthProvider->>AuthController: GET /external-callback (code)
AuthController->>SocialLoginService: ProcessExternalLogin()
SocialLoginService->>Database: Tìm/Tạo User
Database-->>SocialLoginService: User
SocialLoginService-->>AuthController: User + Tokens
AuthController-->>Client: Redirect với 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
Kiến Trúc Deployment
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
Xử Lý Lỗi
Phân Cấp Exception
Exception
└── DomainException
└── (Custom domain exceptions)
Problem Details (RFC 7807)
Tất cả lỗi trả về định dạng Problem Details:
{
"type": "https://tools.ietf.org/html/rfc7807",
"title": "Lỗi Validation",
"status": 400,
"detail": "Một hoặc nhiều lỗi validation đã xảy ra.",
"errors": {
"Email": ["Email là bắt buộc"]
}
}
Kiến Trúc Distributed Caching
Tổng Quan Caching
graph TB
subgraph "Application"
SVC[Services]
CACHE[ICacheService]
end
subgraph "Tầng Caching"
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
Service implement interface ICacheService cho distributed caching:
| Method | Mục Đích |
|---|---|
GetAsync<T> |
Lấy item từ cache theo key |
SetAsync<T> |
Lưu item với TTL tùy chọn |
RemoveAsync |
Xóa item từ cache |
ExistsAsync |
Kiểm tra key tồn tại |
GetOrSetAsync<T> |
Pattern cache-aside |
BlacklistAsync |
Thêm vào blacklist token |
IsBlacklistedAsync |
Kiểm tra blacklist token |
Chiến Lược Token Caching
sequenceDiagram
participant Client
participant AuthController
participant CacheService
participant Redis
participant Database
Note over Client,Database: Validate Token với Cache
Client->>AuthController: Request với JWT
AuthController->>CacheService: IsBlacklistedAsync(tokenId)
CacheService->>Redis: GET blacklist:token:{id}
Redis-->>CacheService: null (không bị blacklist)
CacheService-->>AuthController: false
AuthController->>AuthController: Validate JWT Claims
AuthController-->>Client: Response
Note over Client,Database: Thu Hồi Token (Logout)
Client->>AuthController: POST /logout
AuthController->>CacheService: BlacklistAsync(tokenId, 7 ngày)
CacheService->>Redis: SETEX blacklist:token:{id} 604800 "1"
Redis-->>CacheService: OK
AuthController-->>Client: 200 OK
Token Cache Keys
| Mẫu Key | Mục Đích | TTL |
|---|---|---|
blacklist:token:{tokenId} |
Tokens đã thu hồi | Thời gian còn lại của token |
blacklist:refresh:{tokenId} |
Refresh tokens đã thu hồi | 7 ngày |
user:{userId}:tokens |
Tokens đang hoạt động của user | 15 phút |
Chiến Lược Session Caching
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
| Mẫu Key | Mục Đích | TTL |
|---|---|---|
session:{userId} |
Dữ liệu session user | 30 phút |
user:{userId}:roles |
Cached user roles | 15 phút |
user:{userId}:permissions |
Computed permissions | 15 phút |
user:{userId}:profile |
Dữ liệu profile user | 10 phút |
Pattern Cache-Aside
sequenceDiagram
participant Service
participant CacheService
participant Redis
participant Database
Service->>CacheService: GetOrSetAsync(key, factory)
CacheService->>Redis: GET key
alt Cache Hit
Redis-->>CacheService: dữ liệu cached
CacheService-->>Service: trả về dữ liệu
else Cache Miss
Redis-->>CacheService: null
CacheService->>Database: factory() - lấy dữ liệu
Database-->>CacheService: dữ liệu
CacheService->>Redis: SETEX key ttl data
CacheService-->>Service: trả về dữ liệu
end
Chiến Lược Invalidation Cache
| Chiến Lược | Khi Nào Sử Dụng | Implementation |
|---|---|---|
| Time-based (TTL) | Dữ liệu chung | SetAsync(..., TimeSpan.FromMinutes(15)) |
| Event-based | Cập nhật user | Xóa cache khi có event cập nhật |
| Pattern-based | Invalidation hàng loạt | RemoveByPatternAsync("user:*") |
Cân Nhắc Hiệu Năng
- Connection Pooling: EF Core với Npgsql resilience
- Token Caching: Redis cho token validation và blacklist
- Session Caching: User sessions và permissions được cache
- Async Operations: Tất cả I/O operations đều async
- Database Indexes: Cấu hình trong EntityConfigurations
- Cache-Aside Pattern: Giảm tải database cho các truy vấn thường xuyên