From 4ae24a7bc87f451d2df45fbb2bead0bf3d8a52ff Mon Sep 17 00:00:00 2001 From: Ho Ngoc Hai Date: Mon, 12 Jan 2026 16:37:31 +0700 Subject: [PATCH] feat(docs): Revamp IAM service documentation and authentication flow - Updated README and architecture documentation to reflect the new authentication flow, including user registration, login, token management, and logout processes. - Enhanced API documentation with detailed examples for each step of the authentication process, including curl commands and expected responses. - Improved clarity in the architecture diagrams, outlining the interaction between clients, API, application, and infrastructure layers. - Added sections on OAuth2 grant types and user management functionalities to provide comprehensive guidance for developers. - Streamlined Vietnamese documentation to ensure consistency with English updates and improve accessibility for users. --- services/iam-service-net/README.md | 80 +++- .../iam-service-net/docs/en/ARCHITECTURE.md | 442 +++++++++++------- services/iam-service-net/docs/en/README.md | 362 +++++++------- .../iam-service-net/docs/vi/ARCHITECTURE.md | 440 ++++++++++------- services/iam-service-net/docs/vi/README.md | 80 +++- 5 files changed, 873 insertions(+), 531 deletions(-) diff --git a/services/iam-service-net/README.md b/services/iam-service-net/README.md index c807224a..884b0960 100644 --- a/services/iam-service-net/README.md +++ b/services/iam-service-net/README.md @@ -74,22 +74,88 @@ dotnet run --project src/IamService.API | `PUT` | `/api/v1/users/{id}` | Update user | ✅ | | `DELETE` | `/api/v1/users/{id}` | Delete user (soft delete) | ✅ | -### Token Request Examples +## Authentication Flow -**Password Grant (Login):** +### Step 1: Register a New User ```bash -curl -X POST http://localhost:5001/connect/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=password&username=user@example.com&password=Password123!&scope=openid profile email roles api" +curl -X POST http://localhost:5001/api/v1/auth/register \ + -H "Content-Type: application/json" \ + -d '{ + "email": "user@example.com", + "password": "Password123!", + "firstName": "John", + "lastName": "Doe" + }' ``` -**Refresh Token:** +**Response:** +```json +{ + "success": true, + "data": { + "userId": "550e8400-e29b-41d4-a716-446655440000", + "email": "user@example.com" + } +} +``` + +### Step 2: Login (Password Grant) ```bash curl -X POST http://localhost:5001/connect/token \ -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN" + -d "grant_type=password" \ + -d "username=user@example.com" \ + -d "password=Password123!" \ + -d "scope=openid profile email offline_access" +``` + +**Response:** +```json +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9...", + "token_type": "Bearer", + "expires_in": 900, + "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9...", + "scope": "openid profile email offline_access" +} +``` + +### Step 3: Use Access Token + +Use the `access_token` in `Authorization` header for protected APIs: + +```bash +curl http://localhost:5001/api/v1/users/me \ + -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9..." +``` + +### Step 4: Refresh Token (When Access Token Expires) + +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=refresh_token" \ + -d "refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9..." +``` + +### Step 5: Logout + +```bash +curl -X POST http://localhost:5001/api/v1/auth/logout \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" +``` + +### Client Credentials (Service-to-Service) + +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=client_credentials" \ + -d "client_id=goodgo-service" \ + -d "client_secret=service-secret" \ + -d "scope=api" ``` ### Health Checks diff --git a/services/iam-service-net/docs/en/ARCHITECTURE.md b/services/iam-service-net/docs/en/ARCHITECTURE.md index 7aecf3ad..149c038e 100644 --- a/services/iam-service-net/docs/en/ARCHITECTURE.md +++ b/services/iam-service-net/docs/en/ARCHITECTURE.md @@ -1,86 +1,223 @@ -# Architecture Documentation +# IAM Service Architecture -> Detailed architecture documentation for the .NET 10 Microservice Template. +> Architecture documentation for IAM (Identity and Access Management) Service built with .NET 10, OpenIddict, and Clean Architecture. ## Architecture Overview ```mermaid graph TB + subgraph "Clients" + WEB[Web App] + MOB[Mobile App] + SVC[Other Services] + end + subgraph "API Layer" - C[Controllers] + AUTH[AuthController] + USR[UsersController] + TOK[Token Endpoint] + end + + subgraph "Application Layer - CQRS" CMD[Commands] - Q[Queries] - B[Behaviors] - V[Validations] + QRY[Queries] + VAL[Validators] + BHV[Behaviors] end subgraph "Domain Layer" - AR[Aggregate Roots] - E[Entities] - VO[Value Objects] - DE[Domain Events] - DX[Domain Exceptions] + USER[User Aggregate] + ROLE[Role Aggregate] + EVT[Domain Events] end - subgraph "Infrastructure Layer" + subgraph "Infrastructure" + CTX[Identity DbContext] + REPO[Repositories] + OIDDICT[OpenIddict] + end + + subgraph "External" DB[(PostgreSQL)] - R[Repositories] - CTX[DbContext] - ID[Idempotency] + REDIS[(Redis)] end - C --> CMD - C --> Q - CMD --> B --> V - CMD --> AR - Q --> R - R --> CTX --> DB - AR --> DE - R --> AR + WEB --> AUTH + MOB --> AUTH + SVC --> TOK + AUTH --> CMD + AUTH --> QRY + USR --> CMD + USR --> QRY + TOK --> OIDDICT + CMD --> VAL + CMD --> BHV + CMD --> USER + QRY --> REPO + USER --> EVT + REPO --> CTX + OIDDICT --> CTX + CTX --> DB + CTX --> REDIS - style C fill:#4a90d9,stroke:#2d5986,color:#fff - style AR fill:#50c878,stroke:#2d8659,color:#fff + 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 OIDDICT fill:#9b59b6,stroke:#7d3c98,color:#fff ``` -## Layer Responsibilities - -### 1. Domain Layer (IamService.Domain) - -The heart of the application containing pure business logic. This layer: -- Has **ZERO** external dependencies (except MediatR.Contracts for events) -- Contains only POCO classes -- Implements DDD tactical patterns - -#### Components - -| Component | Purpose | -|-----------|---------| -| **SeedWork** | Base classes: Entity, ValueObject, Enumeration, IAggregateRoot | -| **AggregatesModel** | Aggregate roots with their entities and value objects | -| **Events** | Domain events for cross-aggregate communication | -| **Exceptions** | Domain-specific exceptions for business rule violations | - -### 2. Infrastructure Layer (IamService.Infrastructure) - -Technical implementations and external concerns: -- Database access (EF Core) -- Repository implementations -- External service integrations - -### 3. API Layer (IamService.API) - -Application entry point and CQRS implementation: -- Controllers for HTTP handling -- Commands for write operations -- Queries for read operations -- MediatR behaviors for cross-cutting concerns - -## CQRS Flow +## OAuth2 Authentication Flow ```mermaid sequenceDiagram participant Client + participant AuthController + participant OpenIddict + participant UserManager + participant Database + + Note over Client,Database: Password Grant Flow (User Login) + + Client->>AuthController: POST /connect/token
grant_type=password + AuthController->>OpenIddict: Validate Request + OpenIddict->>UserManager: FindByEmailAsync() + UserManager->>Database: Query User + Database-->>UserManager: User Data + UserManager->>UserManager: CheckPasswordAsync() + UserManager-->>OpenIddict: User Validated + OpenIddict->>OpenIddict: Generate Tokens (JWT) + OpenIddict-->>AuthController: Token Response + AuthController-->>Client: access_token + refresh_token + + Note over Client,Database: Using Access Token + + Client->>AuthController: GET /api/v1/users/me
Authorization: Bearer {token} + AuthController->>OpenIddict: Validate JWT + OpenIddict-->>AuthController: Claims Principal + AuthController-->>Client: User Data +``` + +## Token Types and Flows + +```mermaid +graph LR + subgraph "Grant Types" + PWD[Password Grant] + REF[Refresh Token] + CC[Client Credentials] + end + + subgraph "Tokens" + AT[Access Token
15 min] + RT[Refresh Token
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 + +```mermaid +classDiagram + class ApplicationUser { + +Guid Id + +string Email + +string FirstName + +string LastName + +UserStatus Status + +DateTime CreatedAt + +DateTime? LastLoginAt + +UpdateProfile(firstName, lastName) + +Disable() + +RecordLogin() + } + + class UserStatus { + <> + +Active + +Locked + +Disabled + +PendingVerification + } + + class ApplicationRole { + +Guid Id + +string Name + +string Description + } + + ApplicationUser --> UserStatus : has + ApplicationUser "many" --> "many" ApplicationRole : belongs to +``` + +### Database Schema + +```mermaid +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 + } + + OpenIddictTokens { + uuid Id PK + uuid ApplicationId FK + uuid AuthorizationId FK + string Type + string Status + datetime ExpirationDate + } + + AspNetUsers ||--o{ UserStatuses : has + AspNetUsers ||--o{ AspNetUserRoles : has + AspNetRoles ||--o{ AspNetUserRoles : has +``` + +## CQRS Pipeline + +```mermaid +sequenceDiagram participant Controller participant MediatR participant LoggingBehavior @@ -88,114 +225,68 @@ sequenceDiagram participant TransactionBehavior participant CommandHandler participant Repository - participant DbContext - Client->>Controller: HTTP Request Controller->>MediatR: Send(Command) MediatR->>LoggingBehavior: Handle LoggingBehavior->>ValidatorBehavior: Next() ValidatorBehavior->>TransactionBehavior: Next() TransactionBehavior->>CommandHandler: Next() - CommandHandler->>Repository: Add/Update/Delete - Repository->>DbContext: SaveEntitiesAsync() - DbContext-->>Repository: Success + CommandHandler->>Repository: Save Repository-->>CommandHandler: Result CommandHandler-->>Controller: Response - Controller-->>Client: HTTP Response ``` -## Domain Events +### Pipeline Behaviors + +| Order | Behavior | Purpose | +|-------|----------|---------| +| 1 | LoggingBehavior | Log request/response with timing | +| 2 | ValidatorBehavior | FluentValidation | +| 3 | TransactionBehavior | Database transaction wrapper | + +## Security Architecture ```mermaid -graph LR - AR[Aggregate Root] -->|Raises| DE[Domain Event] - DE -->|Dispatched by| CTX[DbContext] - CTX -->|Publishes to| M[MediatR] - M -->|Handled by| H1[Handler 1] - M -->|Handled by| H2[Handler 2] +graph TD + subgraph "Authentication" + JWT[JWT Bearer Tokens] + RS256[RS256 Signing] + OIDC[OpenIddict Server] + end - style AR fill:#50c878,stroke:#2d8659,color:#fff - style DE fill:#f39c12,stroke:#d68910,color:#fff - style M fill:#9b59b6,stroke:#7d3c98,color:#fff -``` - -## Database Schema - -### Sample Aggregate - -```mermaid -erDiagram - samples { - uuid id PK - varchar(200) name - varchar(1000) description - int status_id FK - timestamp created_at - timestamp updated_at - } + subgraph "Authorization" + RBAC[Role-Based Access] + CLAIMS[Claims-Based] + POLICY[Policy Enforcement] + end - sample_statuses { - int id PK - varchar(50) name - } + subgraph "Protection" + HASH[bcrypt Password Hash] + HTTPS[HTTPS/TLS] + CORS[CORS Policy] + end - samples ||--o{ sample_statuses : has -``` - -## MediatR Pipeline - -``` -Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response - │ │ │ - ▼ ▼ ▼ - Log start/end Validate Begin/Commit - + timing with Transaction - FluentValidation -``` - -### Behavior Order - -1. **LoggingBehavior** - Logs request handling with timing -2. **ValidatorBehavior** - Validates request using FluentValidation -3. **TransactionBehavior** - Wraps command handlers in database transactions - -## Error Handling - -### Exception Hierarchy - -``` -Exception -└── DomainException - └── SampleDomainException -``` - -### Problem Details (RFC 7807) - -All errors are returned in Problem Details format: - -```json -{ - "type": "https://tools.ietf.org/html/rfc7807", - "title": "Validation Error", - "status": 400, - "detail": "One or more validation errors occurred.", - "errors": { - "Name": ["Name is required"] - } -} + JWT --> RS256 + RS256 --> OIDC + RBAC --> CLAIMS + CLAIMS --> POLICY + + style JWT fill:#3498db,stroke:#2980b9,color:#fff + style RBAC fill:#e74c3c,stroke:#c0392b,color:#fff + style HASH fill:#2ecc71,stroke:#27ae60,color:#fff ``` ## Health Checks ```mermaid graph TD - HC[Health Check Endpoint] - HC --> |/health/live| L[Liveness] - HC --> |/health/ready| R[Readiness] - HC --> |/health| F[Full Status] + HC[Health Check Endpoints] + HC -->|/health/live| L[Liveness Probe] + HC -->|/health/ready| R[Readiness Probe] + HC -->|/health| F[Full Status] - R --> PG[(PostgreSQL)] - R --> RD[(Redis)] + R --> PG[(PostgreSQL Check)] + R --> RD[(Redis Check)] style HC fill:#3498db,stroke:#2980b9,color:#fff style L fill:#2ecc71,stroke:#27ae60,color:#fff @@ -204,17 +295,19 @@ graph TD ## Deployment Architecture -### Docker Compose (Local) +### Docker Compose (Local/Development) ```yaml services: - iamservice-api: + iam-service: build: . - ports: ["5000:8080"] + ports: ["5001:8080"] depends_on: - postgres - redis - + environment: + - DATABASE_URL=Host=postgres;... + postgres: image: postgres:16-alpine @@ -228,14 +321,14 @@ services: apiVersion: apps/v1 kind: Deployment metadata: - name: iamservice-api + name: iam-service spec: replicas: 3 template: spec: containers: - - name: api - image: iamservice:latest + - name: iam-service + image: goodgo/iam-service:latest ports: - containerPort: 8080 livenessProbe: @@ -248,24 +341,43 @@ spec: port: 8080 ``` -## Security Considerations +## Error Handling -1. **Authentication**: JWT Bearer token (configure in production) -2. **Authorization**: Role-based access control -3. **Input Validation**: FluentValidation on all requests -4. **SQL Injection**: EF Core parameterized queries -5. **Secrets**: Environment variables, never in code +### Exception Hierarchy -## Performance Optimization +``` +Exception +└── DomainException + └── (Custom domain exceptions) +``` -1. **Connection Pooling**: EF Core with Npgsql connection resilience -2. **Async/Await**: All I/O operations are async -3. **Response Caching**: Add caching headers for queries -4. **Database Indexes**: Configure in EntityConfigurations +### Problem Details (RFC 7807) + +All errors return Problem Details format: + +```json +{ + "type": "https://tools.ietf.org/html/rfc7807", + "title": "Validation Error", + "status": 400, + "detail": "One or more validation errors occurred.", + "errors": { + "Email": ["Email is required"] + } +} +``` + +## Performance Considerations + +1. **Connection Pooling**: EF Core with Npgsql resilience +2. **Token Caching**: Redis for token validation +3. **Async Operations**: All I/O operations are async +4. **Database Indexes**: Configured in EntityConfigurations ## References +- [OpenIddict Documentation](https://documentation.openiddict.com/) +- [ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) +- [OAuth2 Specification](https://oauth.net/2/) - [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) -- [.NET Microservices Architecture Guide](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/) -- [Domain-Driven Design](https://martinfowler.com/bliki/DomainDrivenDesign.html) - [CQRS Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs) diff --git a/services/iam-service-net/docs/en/README.md b/services/iam-service-net/docs/en/README.md index 8240ad39..56e055ab 100644 --- a/services/iam-service-net/docs/en/README.md +++ b/services/iam-service-net/docs/en/README.md @@ -1,130 +1,80 @@ -# .NET 10 Microservice Template +# IAM Service .NET 10 -> Enterprise-grade .NET 10 microservice template following DDD, CQRS, and Clean Architecture patterns. +> Identity and Access Management Service built with .NET 10, ASP.NET Core Identity, and OpenIddict following DDD, CQRS, and Clean Architecture patterns. ## Overview -This template provides a production-ready structure for .NET microservices based on the eShopOnContainers reference architecture with: +This service provides OAuth2/OpenID Connect authentication and authorization: -- **Domain-Driven Design (DDD)** - Aggregates, Entities, Value Objects, Domain Events -- **CQRS Pattern** - Separate Commands (write) and Queries (read) with MediatR -- **Clean Architecture** - Domain, Infrastructure, API layered separation -- **EF Core 10** - PostgreSQL with connection resilience -- **FluentValidation** - Request validation -- **API Versioning** - URL segment versioning -- **Health Checks** - Kubernetes-ready probes -- **Structured Logging** - Serilog with console and Seq +- **OAuth2/OIDC Server** - OpenIddict for token management +- **User Management** - Registration, profile, soft-delete +- **Role-Based Access Control** - User roles and permissions +- **Token Management** - Access (15 min), Refresh (7 days) tokens +- **CQRS Pattern** - MediatR for Commands/Queries +- **Clean Architecture** - Domain, Infrastructure, API layers -## Prerequisites +## Tech Stack -| Requirement | Version | -|-------------|---------| -| .NET SDK | 10.0.101+ | -| Docker | 24.0+ | -| PostgreSQL | 15+ (or use Docker) | - -```bash -# Check .NET version -dotnet --version -# Should output: 10.0.xxx -``` +| Technology | Purpose | +|------------|---------| +| .NET 10 | Runtime | +| ASP.NET Core Identity | User/Role management | +| OpenIddict | OAuth2/OIDC server | +| EF Core + PostgreSQL | Data persistence | +| MediatR | CQRS pattern | +| FluentValidation | Request validation | +| Serilog | Structured logging | ## Quick Start -### 1. Create New Service +### 1. Prerequisites -```bash -# Copy template to new service -cp -r services/_template_dot_net services/your-service-name - -# Navigate to service directory -cd services/your-service-name - -# Rename all occurrences of "IamService" to "YourService" -find . -type f -name "*.cs" -exec sed -i '' 's/IamService/YourService/g' {} + -find . -type f -name "*.csproj" -exec sed -i '' 's/IamService/YourService/g' {} + -``` +- .NET SDK 10.0.101+ +- Docker (for PostgreSQL) ### 2. Configure Environment ```bash -# Copy environment template cp .env.example .env - -# Edit with your configuration -nano .env +# Edit DATABASE_URL, JWT_SECRET in .env ``` ### 3. Run with Docker ```bash -# Start all services (API + PostgreSQL + Redis) docker-compose up -d - -# View logs -docker-compose logs -f iamservice-api ``` +Service available at: `http://localhost:5001` + ### 4. Run Locally ```bash -# Restore dependencies dotnet restore - -# Build all projects dotnet build - -# Run the API dotnet run --project src/IamService.API ``` -## Project Structure - -``` -_template_dot_net/ -├── src/ -│ ├── IamService.API/ # Presentation Layer (Controllers, CQRS) -│ │ ├── Controllers/ # API endpoints -│ │ ├── Application/ # CQRS Implementation -│ │ │ ├── Commands/ # Write operations (MediatR) -│ │ │ ├── Queries/ # Read operations -│ │ │ ├── Behaviors/ # MediatR pipeline behaviors -│ │ │ └── Validations/ # FluentValidation validators -│ │ ├── Middleware/ # Custom middleware -│ │ └── Program.cs # Application entry point -│ │ -│ ├── IamService.Domain/ # Domain Layer (Pure business logic) -│ │ ├── AggregatesModel/ # Aggregate roots and entities -│ │ ├── Events/ # Domain events -│ │ ├── Exceptions/ # Domain exceptions -│ │ └── SeedWork/ # Base classes (Entity, ValueObject, etc.) -│ │ -│ └── IamService.Infrastructure/ # Infrastructure Layer (Data access) -│ ├── EntityConfigurations/ # EF Core Fluent API configurations -│ ├── Repositories/ # Repository implementations -│ ├── Idempotency/ # Request idempotency handling -│ └── IamServiceContext.cs # DbContext with Unit of Work -│ -├── tests/ -│ ├── IamService.UnitTests/ # Unit tests (Domain, Application) -│ └── IamService.FunctionalTests/ # Integration tests (API endpoints) -│ -├── Dockerfile # Multi-stage Docker build -├── docker-compose.yml # Local development setup -├── global.json # .NET SDK version pinning -└── Directory.Build.props # Common MSBuild properties -``` - ## API Endpoints -| Method | Endpoint | Description | -|--------|----------|-------------| -| `GET` | `/api/v1/samples` | Get all samples | -| `GET` | `/api/v1/samples/{id}` | Get sample by ID | -| `POST` | `/api/v1/samples` | Create new sample | -| `PUT` | `/api/v1/samples/{id}` | Update sample | -| `DELETE` | `/api/v1/samples/{id}` | Delete sample | -| `PATCH` | `/api/v1/samples/{id}/status` | Change status | +### Authentication (`/api/v1/auth`) + +| Method | Endpoint | Description | Auth | +|--------|----------|-------------|------| +| `POST` | `/api/v1/auth/register` | Register new user | ❌ | +| `POST` | `/connect/token` | OAuth2 token endpoint | ❌ | +| `POST` | `/api/v1/auth/change-password` | Change password | ✅ | +| `POST` | `/api/v1/auth/logout` | Logout (revoke tokens) | ✅ | + +### User Management (`/api/v1/users`) + +| Method | Endpoint | Description | Auth | +|--------|----------|-------------|------| +| `GET` | `/api/v1/users` | List users (paginated) | ✅ | +| `GET` | `/api/v1/users/me` | Get current user | ✅ | +| `GET` | `/api/v1/users/{id}` | Get user by ID | ✅ | +| `PUT` | `/api/v1/users/{id}` | Update user | ✅ | +| `DELETE` | `/api/v1/users/{id}` | Delete user (soft) | ✅ | ### Health Endpoints @@ -134,62 +84,141 @@ _template_dot_net/ | `/health/live` | Liveness probe | | `/health/ready` | Readiness probe | -## CQRS Pattern +## Authentication Flow -### Commands (Write Operations) +### Step 1: Register a New User -```csharp -// Define command -public record CreateSampleCommand(string Name, string? Description) - : IRequest; +```bash +curl -X POST http://localhost:5001/api/v1/auth/register \ + -H "Content-Type: application/json" \ + -d '{ + "email": "user@example.com", + "password": "Password123!", + "firstName": "John", + "lastName": "Doe" + }' +``` -// Handle command -public class CreateSampleCommandHandler : IRequestHandler +### Step 2: Login (Password Grant) + +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=password" \ + -d "username=user@example.com" \ + -d "password=Password123!" \ + -d "scope=openid profile email offline_access" +``` + +**Response:** +```json { - public async Task Handle(CreateSampleCommand request, CancellationToken ct) - { - var sample = new Sample(request.Name, request.Description); - _repository.Add(sample); - await _repository.UnitOfWork.SaveEntitiesAsync(ct); - return new CreateSampleCommandResult(sample.Id); - } + "access_token": "eyJhbGciOiJSUzI1NiIs...", + "token_type": "Bearer", + "expires_in": 900, + "refresh_token": "eyJhbGciOiJSUzI1NiIs...", + "scope": "openid profile email offline_access" } ``` -### Queries (Read Operations) +### Step 3: Use Access Token -```csharp -// Define query -public record GetSampleQuery(Guid SampleId) : IRequest; +```bash +curl http://localhost:5001/api/v1/users/me \ + -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..." ``` -## Domain Model +### Step 4: Refresh Token -### Aggregate Root - -```csharp -public class Sample : Entity, IAggregateRoot -{ - public string Name => _name; - public SampleStatus Status => _status; - - public Sample(string name, string? description) { - // Business logic validation - if (string.IsNullOrWhiteSpace(name)) - throw new SampleDomainException("Sample name cannot be empty"); - - // Domain event - AddDomainEvent(new SampleCreatedDomainEvent(this)); - } - - public void Activate() { - if (_status != SampleStatus.Draft) - throw new SampleDomainException("Only draft samples can be activated"); - // State transition - } -} +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=refresh_token" \ + -d "refresh_token=eyJhbGciOiJSUzI1NiIs..." ``` +### Step 5: Logout + +```bash +curl -X POST http://localhost:5001/api/v1/auth/logout \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" +``` + +## Client Credentials (Service-to-Service) + +For service-to-service authentication without user context: + +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=client_credentials" \ + -d "client_id=goodgo-service" \ + -d "client_secret=service-secret" \ + -d "scope=api" +``` + +### Supported OAuth2 Grant Types + +| Grant Type | Use Case | Requires User | +|------------|----------|---------------| +| `password` | User login from trusted apps | Yes | +| `refresh_token` | Token renewal | No (uses refresh token) | +| `client_credentials` | Service-to-service | No | + +## Configuration + +### Environment Variables + +| Variable | Description | Required | +|----------|-------------|----------| +| `ASPNETCORE_ENVIRONMENT` | Environment | No (default: Development) | +| `DATABASE_URL` | PostgreSQL connection | Yes | +| `JWT_SECRET` | JWT signing secret (32+ chars) | Yes | +| `REDIS_URL` | Redis connection | No | + +### Token Lifetimes + +| Token | Lifetime | +|-------|----------| +| Access Token | 15 minutes | +| Refresh Token | 7 days | + +### Password Policy + +- Minimum 8 characters +- Requires: uppercase, lowercase, digit, special character + +## Project Structure + +``` +iam-service-net/ +├── src/ +│ ├── IamService.API/ # Controllers, CQRS +│ │ ├── Controllers/ # AuthController, UsersController +│ │ └── Application/ # Commands, Queries, Validations +│ ├── IamService.Domain/ # Domain entities +│ │ ├── AggregatesModel/ # UserAggregate, RoleAggregate +│ │ ├── Events/ # Domain events +│ │ └── Exceptions/ # Domain exceptions +│ └── IamService.Infrastructure/ # Data access +│ ├── IamServiceContext.cs # DbContext with Identity +│ └── Repositories/ # Repository implementations +├── tests/ +│ ├── IamService.UnitTests/ +│ └── IamService.FunctionalTests/ +├── docs/ +│ ├── en/ # English documentation +│ └── vi/ # Vietnamese documentation +├── Dockerfile +└── docker-compose.yml +``` + +## Swagger UI + +After running the service, access Swagger UI at: +- **Local**: http://localhost:5001/swagger +- **Docker**: http://localhost/api/v1/iam/swagger + ## Testing ```bash @@ -197,68 +226,25 @@ public class Sample : Entity, IAggregateRoot dotnet test # Run with coverage -dotnet test /p:CollectCoverage=true /p:CoverageReportFormat=cobertura - -# Run specific test project -dotnet test tests/IamService.UnitTests +dotnet test /p:CollectCoverage=true ``` -## Configuration - -### Environment Variables - -| Variable | Description | Default | -|----------|-------------|---------| -| `ASPNETCORE_ENVIRONMENT` | Environment name | `Development` | -| `DATABASE_URL` | PostgreSQL connection string | - | -| `REDIS_URL` | Redis connection string | - | -| `JWT_SECRET` | JWT signing secret (min 32 chars) | - | - -### appsettings.json - -```json -{ - "ConnectionStrings": { - "DefaultConnection": "Host=localhost;Database=iamservice;Username=postgres;Password=postgres" - }, - "Serilog": { - "MinimumLevel": "Information" - } -} -``` - -## Deployment - -### Docker Build +## Docker ```bash -# Build Docker image -docker build -t iamservice:latest . +# Build image +docker build -t goodgo/iam-service:latest . # Run container -docker run -p 5000:8080 --env-file .env iamservice:latest +docker run -p 5001:8080 --env-file .env goodgo/iam-service:latest ``` -### Kubernetes - -See [ARCHITECTURE.md](./ARCHITECTURE.md) for Kubernetes deployment manifests. - -## What's New in .NET 10 - -- **C# 14** language features -- Improved **Native AOT** support -- Better **async/await** performance -- Enhanced **JSON serialization** -- Performance improvements across the board -- 3-year **LTS** support (until November 2028) - ## Resources -- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) - Reference architecture -- [.NET 10 Documentation](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10) -- [DDD with .NET](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/) -- [MediatR](https://github.com/jbogard/MediatR) - CQRS library -- [FluentValidation](https://docs.fluentvalidation.net/) - Validation library +- [OpenIddict Documentation](https://documentation.openiddict.com/) +- [ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) +- [OAuth2 Specification](https://oauth.net/2/) +- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) ## License diff --git a/services/iam-service-net/docs/vi/ARCHITECTURE.md b/services/iam-service-net/docs/vi/ARCHITECTURE.md index 3fb25178..9e04eee9 100644 --- a/services/iam-service-net/docs/vi/ARCHITECTURE.md +++ b/services/iam-service-net/docs/vi/ARCHITECTURE.md @@ -1,86 +1,223 @@ -# Tài Liệu Kiến Trúc +# Kiến Trúc IAM Service -> Tài liệu kiến trúc chi tiết cho Template Microservice .NET 10. +> 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, OpenIddict, và Clean Architecture. ## Tổng Quan Kiến Trúc ```mermaid graph TB + subgraph "Clients" + WEB[Web App] + MOB[Mobile App] + SVC[Các Services khác] + end + subgraph "Lớp API" - C[Controllers] + AUTH[AuthController] + USR[UsersController] + TOK[Token Endpoint] + end + + subgraph "Lớp Application - CQRS" CMD[Commands] - Q[Queries] - B[Behaviors] - V[Validations] + QRY[Queries] + VAL[Validators] + BHV[Behaviors] end subgraph "Lớp Domain" - AR[Aggregate Roots] - E[Entities] - VO[Value Objects] - DE[Domain Events] - DX[Domain Exceptions] + USER[User Aggregate] + ROLE[Role Aggregate] + EVT[Domain Events] end - subgraph "Lớp Infrastructure" + subgraph "Infrastructure" + CTX[Identity DbContext] + REPO[Repositories] + OIDDICT[OpenIddict] + end + + subgraph "External" DB[(PostgreSQL)] - R[Repositories] - CTX[DbContext] - ID[Idempotency] + REDIS[(Redis)] end - C --> CMD - C --> Q - CMD --> B --> V - CMD --> AR - Q --> R - R --> CTX --> DB - AR --> DE - R --> AR + WEB --> AUTH + MOB --> AUTH + SVC --> TOK + AUTH --> CMD + AUTH --> QRY + USR --> CMD + USR --> QRY + TOK --> OIDDICT + CMD --> VAL + CMD --> BHV + CMD --> USER + QRY --> REPO + USER --> EVT + REPO --> CTX + OIDDICT --> CTX + CTX --> DB + CTX --> REDIS - style C fill:#4a90d9,stroke:#2d5986,color:#fff - style AR fill:#50c878,stroke:#2d8659,color:#fff + 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 OIDDICT fill:#9b59b6,stroke:#7d3c98,color:#fff ``` -## Trách Nhiệm Các Lớp - -### 1. Lớp Domain (IamService.Domain) - -Trái tim của ứng dụng chứa business logic thuần túy. Lớp này: -- Có **ZERO** phụ thuộc bên ngoài (ngoại trừ MediatR.Contracts cho events) -- Chỉ chứa các class POCO -- Triển khai các tactical patterns của DDD - -#### Thành Phần - -| Thành phần | Mục Đích | -|------------|----------| -| **SeedWork** | Base classes: Entity, ValueObject, Enumeration, IAggregateRoot | -| **AggregatesModel** | Aggregate roots với entities và value objects | -| **Events** | Domain events cho giao tiếp cross-aggregate | -| **Exceptions** | Domain exceptions cho vi phạm business rules | - -### 2. Lớp Infrastructure (IamService.Infrastructure) - -Triển khai kỹ thuật và các mối quan tâm bên ngoài: -- Truy cập database (EF Core) -- Triển khai repositories -- Tích hợp external services - -### 3. Lớp API (IamService.API) - -Điểm vào ứng dụng và triển khai CQRS: -- Controllers để xử lý HTTP -- Commands cho các thao tác ghi -- Queries cho các thao tác đọc -- MediatR behaviors cho cross-cutting concerns - -## Luồng CQRS +## Luồng Xác Thực OAuth2 ```mermaid sequenceDiagram participant Client + participant AuthController + participant OpenIddict + participant UserManager + participant Database + + Note over Client,Database: Password Grant Flow (Đăng nhập) + + Client->>AuthController: POST /connect/token
grant_type=password + AuthController->>OpenIddict: Validate Request + OpenIddict->>UserManager: FindByEmailAsync() + UserManager->>Database: Query User + Database-->>UserManager: User Data + UserManager->>UserManager: CheckPasswordAsync() + UserManager-->>OpenIddict: User Validated + OpenIddict->>OpenIddict: Tạo Tokens (JWT) + OpenIddict-->>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
Authorization: Bearer {token} + AuthController->>OpenIddict: Validate JWT + OpenIddict-->>AuthController: Claims Principal + AuthController-->>Client: User Data +``` + +## Các Loại Token và Grant Types + +```mermaid +graph LR + subgraph "Grant Types" + PWD[Password Grant] + REF[Refresh Token] + CC[Client Credentials] + end + + subgraph "Tokens" + AT[Access Token
15 phút] + RT[Refresh Token
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 + +```mermaid +classDiagram + class ApplicationUser { + +Guid Id + +string Email + +string FirstName + +string LastName + +UserStatus Status + +DateTime CreatedAt + +DateTime? LastLoginAt + +UpdateProfile(firstName, lastName) + +Disable() + +RecordLogin() + } + + class UserStatus { + <> + +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 + +```mermaid +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 + } + + OpenIddictTokens { + uuid Id PK + uuid ApplicationId FK + uuid AuthorizationId FK + string Type + string Status + datetime ExpirationDate + } + + AspNetUsers ||--o{ UserStatuses : có + AspNetUsers ||--o{ AspNetUserRoles : có + AspNetRoles ||--o{ AspNetUserRoles : có +``` + +## CQRS Pipeline + +```mermaid +sequenceDiagram participant Controller participant MediatR participant LoggingBehavior @@ -88,114 +225,68 @@ sequenceDiagram participant TransactionBehavior participant CommandHandler participant Repository - participant DbContext - Client->>Controller: HTTP Request Controller->>MediatR: Send(Command) MediatR->>LoggingBehavior: Handle LoggingBehavior->>ValidatorBehavior: Next() ValidatorBehavior->>TransactionBehavior: Next() TransactionBehavior->>CommandHandler: Next() - CommandHandler->>Repository: Add/Update/Delete - Repository->>DbContext: SaveEntitiesAsync() - DbContext-->>Repository: Success + CommandHandler->>Repository: Save Repository-->>CommandHandler: Result CommandHandler-->>Controller: Response - Controller-->>Client: HTTP Response ``` -## Domain Events +### 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 ```mermaid -graph LR - AR[Aggregate Root] -->|Phát sinh| DE[Domain Event] - DE -->|Dispatch bởi| CTX[DbContext] - CTX -->|Publish tới| M[MediatR] - M -->|Xử lý bởi| H1[Handler 1] - M -->|Xử lý bởi| H2[Handler 2] +graph TD + subgraph "Authentication" + JWT[JWT Bearer Tokens] + RS256[RS256 Signing] + OIDC[OpenIddict Server] + end - style AR fill:#50c878,stroke:#2d8659,color:#fff - style DE fill:#f39c12,stroke:#d68910,color:#fff - style M fill:#9b59b6,stroke:#7d3c98,color:#fff -``` - -## Schema Database - -### Sample Aggregate - -```mermaid -erDiagram - samples { - uuid id PK - varchar(200) name - varchar(1000) description - int status_id FK - timestamp created_at - timestamp updated_at - } + subgraph "Authorization" + RBAC[Role-Based Access] + CLAIMS[Claims-Based] + POLICY[Policy Enforcement] + end - sample_statuses { - int id PK - varchar(50) name - } + subgraph "Protection" + HASH[bcrypt Password Hash] + HTTPS[HTTPS/TLS] + CORS[CORS Policy] + end - samples ||--o{ sample_statuses : has -``` - -## Pipeline MediatR - -``` -Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response - │ │ │ - ▼ ▼ ▼ - Log start/end Validate Begin/Commit - + timing với Transaction - FluentValidation -``` - -### Thứ Tự Behaviors - -1. **LoggingBehavior** - Ghi log xử lý request với timing -2. **ValidatorBehavior** - Validate request sử dụng FluentValidation -3. **TransactionBehavior** - Bao bọc command handlers trong database transactions - -## Xử Lý Lỗi - -### Phân Cấp Exceptions - -``` -Exception -└── DomainException - └── SampleDomainException -``` - -### Problem Details (RFC 7807) - -Tất cả lỗi được trả về theo định dạng Problem Details: - -```json -{ - "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": { - "Name": ["Tên là bắt buộc"] - } -} + JWT --> RS256 + RS256 --> OIDC + RBAC --> CLAIMS + CLAIMS --> POLICY + + style JWT fill:#3498db,stroke:#2980b9,color:#fff + style RBAC fill:#e74c3c,stroke:#c0392b,color:#fff + style HASH fill:#2ecc71,stroke:#27ae60,color:#fff ``` ## Health Checks ```mermaid graph TD - HC[Health Check Endpoint] - HC --> |/health/live| L[Liveness] - HC --> |/health/ready| R[Readiness] - HC --> |/health| F[Full Status] + HC[Health Check Endpoints] + HC -->|/health/live| L[Liveness Probe] + HC -->|/health/ready| R[Readiness Probe] + HC -->|/health| F[Full Status] - R --> PG[(PostgreSQL)] - R --> RD[(Redis)] + R --> PG[(PostgreSQL Check)] + R --> RD[(Redis Check)] style HC fill:#3498db,stroke:#2980b9,color:#fff style L fill:#2ecc71,stroke:#27ae60,color:#fff @@ -204,17 +295,19 @@ graph TD ## Kiến Trúc Deployment -### Docker Compose (Local) +### Docker Compose (Local/Development) ```yaml services: - iamservice-api: + iam-service: build: . - ports: ["5000:8080"] + ports: ["5001:8080"] depends_on: - postgres - redis - + environment: + - DATABASE_URL=Host=postgres;... + postgres: image: postgres:16-alpine @@ -228,14 +321,14 @@ services: apiVersion: apps/v1 kind: Deployment metadata: - name: iamservice-api + name: iam-service spec: replicas: 3 template: spec: containers: - - name: api - image: iamservice:latest + - name: iam-service + image: goodgo/iam-service:latest ports: - containerPort: 8080 livenessProbe: @@ -248,24 +341,43 @@ spec: port: 8080 ``` -## Cân Nhắc Bảo Mật +## Xử Lý Lỗi -1. **Authentication**: JWT Bearer token (cấu hình trong production) -2. **Authorization**: Role-based access control -3. **Input Validation**: FluentValidation trên tất cả requests -4. **SQL Injection**: EF Core parameterized queries -5. **Secrets**: Biến môi trường, không bao giờ trong code +### Phân Cấp Exception -## Tối Ưu Hiệu Năng +``` +Exception +└── DomainException + └── (Custom domain exceptions) +``` -1. **Connection Pooling**: EF Core với Npgsql connection resilience -2. **Async/Await**: Tất cả I/O operations đều async -3. **Response Caching**: Thêm caching headers cho queries +### Problem Details (RFC 7807) + +Tất cả lỗi trả về định dạng Problem Details: + +```json +{ + "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"] + } +} +``` + +## Cân Nhắc Hiệu Năng + +1. **Connection Pooling**: EF Core với Npgsql resilience +2. **Token Caching**: Redis cho token validation +3. **Async Operations**: Tất cả I/O operations đều async 4. **Database Indexes**: Cấu hình trong EntityConfigurations ## Tài Liệu Tham Khảo +- [OpenIddict Documentation](https://documentation.openiddict.com/) +- [ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) +- [OAuth2 Specification](https://oauth.net/2/) - [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) -- [Hướng dẫn Kiến trúc .NET Microservices](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/) -- [Domain-Driven Design](https://martinfowler.com/bliki/DomainDrivenDesign.html) - [CQRS Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs) diff --git a/services/iam-service-net/docs/vi/README.md b/services/iam-service-net/docs/vi/README.md index 309d34d2..80134af3 100644 --- a/services/iam-service-net/docs/vi/README.md +++ b/services/iam-service-net/docs/vi/README.md @@ -68,22 +68,88 @@ dotnet run --project src/IamService.API | `/health/live` | Kiểm tra sống | | `/health/ready` | Kiểm tra sẵn sàng | -## OAuth2 Token Endpoint +## Quy Trình Xác Thực -### Password Grant (Login) +### Bước 1: Đăng Ký User Mới ```bash -curl -X POST http://localhost:5001/connect/token \ - -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=password&username=user@example.com&password=YourPassword&scope=openid profile email roles api" +curl -X POST http://localhost:5001/api/v1/auth/register \ + -H "Content-Type: application/json" \ + -d '{ + "email": "user@example.com", + "password": "Password123!", + "firstName": "John", + "lastName": "Doe" + }' ``` -### Refresh Token +**Response:** +```json +{ + "success": true, + "data": { + "userId": "550e8400-e29b-41d4-a716-446655440000", + "email": "user@example.com" + } +} +``` + +### Bước 2: Đăng Nhập (Password Grant) ```bash curl -X POST http://localhost:5001/connect/token \ -H "Content-Type: application/x-www-form-urlencoded" \ - -d "grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN" + -d "grant_type=password" \ + -d "username=user@example.com" \ + -d "password=Password123!" \ + -d "scope=openid profile email offline_access" +``` + +**Response:** +```json +{ + "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9...", + "token_type": "Bearer", + "expires_in": 900, + "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9...", + "scope": "openid profile email offline_access" +} +``` + +### Bước 3: Sử Dụng Access Token + +Sử dụng `access_token` trong header `Authorization` cho các API được bảo vệ: + +```bash +curl http://localhost:5001/api/v1/users/me \ + -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9..." +``` + +### Bước 4: Làm Mới Token (Khi Access Token Hết Hạn) + +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=refresh_token" \ + -d "refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCJ9..." +``` + +### Bước 5: Đăng Xuất + +```bash +curl -X POST http://localhost:5001/api/v1/auth/logout \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" +``` + +### Client Credentials (Service-to-Service) + +```bash +curl -X POST http://localhost:5001/connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=client_credentials" \ + -d "client_id=goodgo-service" \ + -d "client_secret=service-secret" \ + -d "scope=api" ``` ## Swagger UI