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.
This commit is contained in:
@@ -74,22 +74,88 @@ dotnet run --project src/IamService.API
|
|||||||
| `PUT` | `/api/v1/users/{id}` | Update user | ✅ |
|
| `PUT` | `/api/v1/users/{id}` | Update user | ✅ |
|
||||||
| `DELETE` | `/api/v1/users/{id}` | Delete user (soft delete) | ✅ |
|
| `DELETE` | `/api/v1/users/{id}` | Delete user (soft delete) | ✅ |
|
||||||
|
|
||||||
### Token Request Examples
|
## Authentication Flow
|
||||||
|
|
||||||
**Password Grant (Login):**
|
### Step 1: Register a New User
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:5001/connect/token \
|
curl -X POST http://localhost:5001/api/v1/auth/register \
|
||||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
-H "Content-Type: application/json" \
|
||||||
-d "grant_type=password&username=user@example.com&password=Password123!&scope=openid profile email roles api"
|
-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
|
```bash
|
||||||
curl -X POST http://localhost:5001/connect/token \
|
curl -X POST http://localhost:5001/connect/token \
|
||||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
-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
|
### Health Checks
|
||||||
|
|||||||
@@ -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
|
## Architecture Overview
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
|
subgraph "Clients"
|
||||||
|
WEB[Web App]
|
||||||
|
MOB[Mobile App]
|
||||||
|
SVC[Other Services]
|
||||||
|
end
|
||||||
|
|
||||||
subgraph "API Layer"
|
subgraph "API Layer"
|
||||||
C[Controllers]
|
AUTH[AuthController]
|
||||||
|
USR[UsersController]
|
||||||
|
TOK[Token Endpoint]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Application Layer - CQRS"
|
||||||
CMD[Commands]
|
CMD[Commands]
|
||||||
Q[Queries]
|
QRY[Queries]
|
||||||
B[Behaviors]
|
VAL[Validators]
|
||||||
V[Validations]
|
BHV[Behaviors]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph "Domain Layer"
|
subgraph "Domain Layer"
|
||||||
AR[Aggregate Roots]
|
USER[User Aggregate]
|
||||||
E[Entities]
|
ROLE[Role Aggregate]
|
||||||
VO[Value Objects]
|
EVT[Domain Events]
|
||||||
DE[Domain Events]
|
|
||||||
DX[Domain Exceptions]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph "Infrastructure Layer"
|
subgraph "Infrastructure"
|
||||||
|
CTX[Identity DbContext]
|
||||||
|
REPO[Repositories]
|
||||||
|
OIDDICT[OpenIddict]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "External"
|
||||||
DB[(PostgreSQL)]
|
DB[(PostgreSQL)]
|
||||||
R[Repositories]
|
REDIS[(Redis)]
|
||||||
CTX[DbContext]
|
|
||||||
ID[Idempotency]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
C --> CMD
|
WEB --> AUTH
|
||||||
C --> Q
|
MOB --> AUTH
|
||||||
CMD --> B --> V
|
SVC --> TOK
|
||||||
CMD --> AR
|
AUTH --> CMD
|
||||||
Q --> R
|
AUTH --> QRY
|
||||||
R --> CTX --> DB
|
USR --> CMD
|
||||||
AR --> DE
|
USR --> QRY
|
||||||
R --> AR
|
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 AUTH fill:#4a90d9,stroke:#2d5986,color:#fff
|
||||||
style AR fill:#50c878,stroke:#2d8659,color:#fff
|
style USER fill:#50c878,stroke:#2d8659,color:#fff
|
||||||
style DB fill:#ff6b6b,stroke:#c0392b,color:#fff
|
style DB fill:#ff6b6b,stroke:#c0392b,color:#fff
|
||||||
|
style OIDDICT fill:#9b59b6,stroke:#7d3c98,color:#fff
|
||||||
```
|
```
|
||||||
|
|
||||||
## Layer Responsibilities
|
## OAuth2 Authentication Flow
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Client
|
participant Client
|
||||||
|
participant AuthController
|
||||||
|
participant OpenIddict
|
||||||
|
participant UserManager
|
||||||
|
participant Database
|
||||||
|
|
||||||
|
Note over Client,Database: Password Grant Flow (User Login)
|
||||||
|
|
||||||
|
Client->>AuthController: POST /connect/token<br/>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<br/>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<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
|
||||||
|
|
||||||
|
```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 {
|
||||||
|
<<enumeration>>
|
||||||
|
+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 Controller
|
||||||
participant MediatR
|
participant MediatR
|
||||||
participant LoggingBehavior
|
participant LoggingBehavior
|
||||||
@@ -88,114 +225,68 @@ sequenceDiagram
|
|||||||
participant TransactionBehavior
|
participant TransactionBehavior
|
||||||
participant CommandHandler
|
participant CommandHandler
|
||||||
participant Repository
|
participant Repository
|
||||||
participant DbContext
|
|
||||||
|
|
||||||
Client->>Controller: HTTP Request
|
|
||||||
Controller->>MediatR: Send(Command)
|
Controller->>MediatR: Send(Command)
|
||||||
MediatR->>LoggingBehavior: Handle
|
MediatR->>LoggingBehavior: Handle
|
||||||
LoggingBehavior->>ValidatorBehavior: Next()
|
LoggingBehavior->>ValidatorBehavior: Next()
|
||||||
ValidatorBehavior->>TransactionBehavior: Next()
|
ValidatorBehavior->>TransactionBehavior: Next()
|
||||||
TransactionBehavior->>CommandHandler: Next()
|
TransactionBehavior->>CommandHandler: Next()
|
||||||
CommandHandler->>Repository: Add/Update/Delete
|
CommandHandler->>Repository: Save
|
||||||
Repository->>DbContext: SaveEntitiesAsync()
|
|
||||||
DbContext-->>Repository: Success
|
|
||||||
Repository-->>CommandHandler: Result
|
Repository-->>CommandHandler: Result
|
||||||
CommandHandler-->>Controller: Response
|
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
|
```mermaid
|
||||||
graph LR
|
graph TD
|
||||||
AR[Aggregate Root] -->|Raises| DE[Domain Event]
|
subgraph "Authentication"
|
||||||
DE -->|Dispatched by| CTX[DbContext]
|
JWT[JWT Bearer Tokens]
|
||||||
CTX -->|Publishes to| M[MediatR]
|
RS256[RS256 Signing]
|
||||||
M -->|Handled by| H1[Handler 1]
|
OIDC[OpenIddict Server]
|
||||||
M -->|Handled by| H2[Handler 2]
|
end
|
||||||
|
|
||||||
style AR fill:#50c878,stroke:#2d8659,color:#fff
|
subgraph "Authorization"
|
||||||
style DE fill:#f39c12,stroke:#d68910,color:#fff
|
RBAC[Role-Based Access]
|
||||||
style M fill:#9b59b6,stroke:#7d3c98,color:#fff
|
CLAIMS[Claims-Based]
|
||||||
```
|
POLICY[Policy Enforcement]
|
||||||
|
end
|
||||||
## 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
|
|
||||||
}
|
|
||||||
|
|
||||||
sample_statuses {
|
subgraph "Protection"
|
||||||
int id PK
|
HASH[bcrypt Password Hash]
|
||||||
varchar(50) name
|
HTTPS[HTTPS/TLS]
|
||||||
}
|
CORS[CORS Policy]
|
||||||
|
end
|
||||||
|
|
||||||
samples ||--o{ sample_statuses : has
|
JWT --> RS256
|
||||||
```
|
RS256 --> OIDC
|
||||||
|
RBAC --> CLAIMS
|
||||||
## MediatR Pipeline
|
CLAIMS --> POLICY
|
||||||
|
|
||||||
```
|
style JWT fill:#3498db,stroke:#2980b9,color:#fff
|
||||||
Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response
|
style RBAC fill:#e74c3c,stroke:#c0392b,color:#fff
|
||||||
│ │ │
|
style HASH fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||||
▼ ▼ ▼
|
|
||||||
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"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Health Checks
|
## Health Checks
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
HC[Health Check Endpoint]
|
HC[Health Check Endpoints]
|
||||||
HC --> |/health/live| L[Liveness]
|
HC -->|/health/live| L[Liveness Probe]
|
||||||
HC --> |/health/ready| R[Readiness]
|
HC -->|/health/ready| R[Readiness Probe]
|
||||||
HC --> |/health| F[Full Status]
|
HC -->|/health| F[Full Status]
|
||||||
|
|
||||||
R --> PG[(PostgreSQL)]
|
R --> PG[(PostgreSQL Check)]
|
||||||
R --> RD[(Redis)]
|
R --> RD[(Redis Check)]
|
||||||
|
|
||||||
style HC fill:#3498db,stroke:#2980b9,color:#fff
|
style HC fill:#3498db,stroke:#2980b9,color:#fff
|
||||||
style L fill:#2ecc71,stroke:#27ae60,color:#fff
|
style L fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||||
@@ -204,17 +295,19 @@ graph TD
|
|||||||
|
|
||||||
## Deployment Architecture
|
## Deployment Architecture
|
||||||
|
|
||||||
### Docker Compose (Local)
|
### Docker Compose (Local/Development)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
iamservice-api:
|
iam-service:
|
||||||
build: .
|
build: .
|
||||||
ports: ["5000:8080"]
|
ports: ["5001:8080"]
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=Host=postgres;...
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
|
|
||||||
@@ -228,14 +321,14 @@ services:
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: iamservice-api
|
name: iam-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: api
|
- name: iam-service
|
||||||
image: iamservice:latest
|
image: goodgo/iam-service:latest
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
@@ -248,24 +341,43 @@ spec:
|
|||||||
port: 8080
|
port: 8080
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security Considerations
|
## Error Handling
|
||||||
|
|
||||||
1. **Authentication**: JWT Bearer token (configure in production)
|
### Exception Hierarchy
|
||||||
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
|
|
||||||
|
|
||||||
## Performance Optimization
|
```
|
||||||
|
Exception
|
||||||
|
└── DomainException
|
||||||
|
└── (Custom domain exceptions)
|
||||||
|
```
|
||||||
|
|
||||||
1. **Connection Pooling**: EF Core with Npgsql connection resilience
|
### Problem Details (RFC 7807)
|
||||||
2. **Async/Await**: All I/O operations are async
|
|
||||||
3. **Response Caching**: Add caching headers for queries
|
All errors return Problem Details format:
|
||||||
4. **Database Indexes**: Configure in EntityConfigurations
|
|
||||||
|
```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
|
## 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)
|
- [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)
|
- [CQRS Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs)
|
||||||
|
|||||||
@@ -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
|
## 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
|
- **OAuth2/OIDC Server** - OpenIddict for token management
|
||||||
- **CQRS Pattern** - Separate Commands (write) and Queries (read) with MediatR
|
- **User Management** - Registration, profile, soft-delete
|
||||||
- **Clean Architecture** - Domain, Infrastructure, API layered separation
|
- **Role-Based Access Control** - User roles and permissions
|
||||||
- **EF Core 10** - PostgreSQL with connection resilience
|
- **Token Management** - Access (15 min), Refresh (7 days) tokens
|
||||||
- **FluentValidation** - Request validation
|
- **CQRS Pattern** - MediatR for Commands/Queries
|
||||||
- **API Versioning** - URL segment versioning
|
- **Clean Architecture** - Domain, Infrastructure, API layers
|
||||||
- **Health Checks** - Kubernetes-ready probes
|
|
||||||
- **Structured Logging** - Serilog with console and Seq
|
|
||||||
|
|
||||||
## Prerequisites
|
## Tech Stack
|
||||||
|
|
||||||
| Requirement | Version |
|
| Technology | Purpose |
|
||||||
|-------------|---------|
|
|------------|---------|
|
||||||
| .NET SDK | 10.0.101+ |
|
| .NET 10 | Runtime |
|
||||||
| Docker | 24.0+ |
|
| ASP.NET Core Identity | User/Role management |
|
||||||
| PostgreSQL | 15+ (or use Docker) |
|
| OpenIddict | OAuth2/OIDC server |
|
||||||
|
| EF Core + PostgreSQL | Data persistence |
|
||||||
```bash
|
| MediatR | CQRS pattern |
|
||||||
# Check .NET version
|
| FluentValidation | Request validation |
|
||||||
dotnet --version
|
| Serilog | Structured logging |
|
||||||
# Should output: 10.0.xxx
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### 1. Create New Service
|
### 1. Prerequisites
|
||||||
|
|
||||||
```bash
|
- .NET SDK 10.0.101+
|
||||||
# Copy template to new service
|
- Docker (for PostgreSQL)
|
||||||
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' {} +
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Configure Environment
|
### 2. Configure Environment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Copy environment template
|
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
|
# Edit DATABASE_URL, JWT_SECRET in .env
|
||||||
# Edit with your configuration
|
|
||||||
nano .env
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Run with Docker
|
### 3. Run with Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start all services (API + PostgreSQL + Redis)
|
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
|
|
||||||
# View logs
|
|
||||||
docker-compose logs -f iamservice-api
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Service available at: `http://localhost:5001`
|
||||||
|
|
||||||
### 4. Run Locally
|
### 4. Run Locally
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Restore dependencies
|
|
||||||
dotnet restore
|
dotnet restore
|
||||||
|
|
||||||
# Build all projects
|
|
||||||
dotnet build
|
dotnet build
|
||||||
|
|
||||||
# Run the API
|
|
||||||
dotnet run --project src/IamService.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
|
## API Endpoints
|
||||||
|
|
||||||
| Method | Endpoint | Description |
|
### Authentication (`/api/v1/auth`)
|
||||||
|--------|----------|-------------|
|
|
||||||
| `GET` | `/api/v1/samples` | Get all samples |
|
| Method | Endpoint | Description | Auth |
|
||||||
| `GET` | `/api/v1/samples/{id}` | Get sample by ID |
|
|--------|----------|-------------|------|
|
||||||
| `POST` | `/api/v1/samples` | Create new sample |
|
| `POST` | `/api/v1/auth/register` | Register new user | ❌ |
|
||||||
| `PUT` | `/api/v1/samples/{id}` | Update sample |
|
| `POST` | `/connect/token` | OAuth2 token endpoint | ❌ |
|
||||||
| `DELETE` | `/api/v1/samples/{id}` | Delete sample |
|
| `POST` | `/api/v1/auth/change-password` | Change password | ✅ |
|
||||||
| `PATCH` | `/api/v1/samples/{id}/status` | Change status |
|
| `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
|
### Health Endpoints
|
||||||
|
|
||||||
@@ -134,62 +84,141 @@ _template_dot_net/
|
|||||||
| `/health/live` | Liveness probe |
|
| `/health/live` | Liveness probe |
|
||||||
| `/health/ready` | Readiness probe |
|
| `/health/ready` | Readiness probe |
|
||||||
|
|
||||||
## CQRS Pattern
|
## Authentication Flow
|
||||||
|
|
||||||
### Commands (Write Operations)
|
### Step 1: Register a New User
|
||||||
|
|
||||||
```csharp
|
```bash
|
||||||
// Define command
|
curl -X POST http://localhost:5001/api/v1/auth/register \
|
||||||
public record CreateSampleCommand(string Name, string? Description)
|
-H "Content-Type: application/json" \
|
||||||
: IRequest<CreateSampleCommandResult>;
|
-d '{
|
||||||
|
"email": "user@example.com",
|
||||||
|
"password": "Password123!",
|
||||||
|
"firstName": "John",
|
||||||
|
"lastName": "Doe"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
// Handle command
|
### Step 2: Login (Password Grant)
|
||||||
public class CreateSampleCommandHandler : IRequestHandler<CreateSampleCommand, CreateSampleCommandResult>
|
|
||||||
|
```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<CreateSampleCommandResult> Handle(CreateSampleCommand request, CancellationToken ct)
|
"access_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||||
{
|
"token_type": "Bearer",
|
||||||
var sample = new Sample(request.Name, request.Description);
|
"expires_in": 900,
|
||||||
_repository.Add(sample);
|
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||||
await _repository.UnitOfWork.SaveEntitiesAsync(ct);
|
"scope": "openid profile email offline_access"
|
||||||
return new CreateSampleCommandResult(sample.Id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Queries (Read Operations)
|
### Step 3: Use Access Token
|
||||||
|
|
||||||
```csharp
|
```bash
|
||||||
// Define query
|
curl http://localhost:5001/api/v1/users/me \
|
||||||
public record GetSampleQuery(Guid SampleId) : IRequest<SampleViewModel?>;
|
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
|
||||||
```
|
```
|
||||||
|
|
||||||
## Domain Model
|
### Step 4: Refresh Token
|
||||||
|
|
||||||
### Aggregate Root
|
```bash
|
||||||
|
curl -X POST http://localhost:5001/connect/token \
|
||||||
```csharp
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||||
public class Sample : Entity, IAggregateRoot
|
-d "grant_type=refresh_token" \
|
||||||
{
|
-d "refresh_token=eyJhbGciOiJSUzI1NiIs..."
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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
|
## Testing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -197,68 +226,25 @@ public class Sample : Entity, IAggregateRoot
|
|||||||
dotnet test
|
dotnet test
|
||||||
|
|
||||||
# Run with coverage
|
# Run with coverage
|
||||||
dotnet test /p:CollectCoverage=true /p:CoverageReportFormat=cobertura
|
dotnet test /p:CollectCoverage=true
|
||||||
|
|
||||||
# Run specific test project
|
|
||||||
dotnet test tests/IamService.UnitTests
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Docker
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build Docker image
|
# Build image
|
||||||
docker build -t iamservice:latest .
|
docker build -t goodgo/iam-service:latest .
|
||||||
|
|
||||||
# Run container
|
# 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
|
## Resources
|
||||||
|
|
||||||
- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers) - Reference architecture
|
- [OpenIddict Documentation](https://documentation.openiddict.com/)
|
||||||
- [.NET 10 Documentation](https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-10)
|
- [ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity)
|
||||||
- [DDD with .NET](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/)
|
- [OAuth2 Specification](https://oauth.net/2/)
|
||||||
- [MediatR](https://github.com/jbogard/MediatR) - CQRS library
|
- [eShopOnContainers](https://github.com/dotnet-architecture/eShopOnContainers)
|
||||||
- [FluentValidation](https://docs.fluentvalidation.net/) - Validation library
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
## Tổng Quan Kiến Trúc
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
|
subgraph "Clients"
|
||||||
|
WEB[Web App]
|
||||||
|
MOB[Mobile App]
|
||||||
|
SVC[Các Services khác]
|
||||||
|
end
|
||||||
|
|
||||||
subgraph "Lớp API"
|
subgraph "Lớp API"
|
||||||
C[Controllers]
|
AUTH[AuthController]
|
||||||
|
USR[UsersController]
|
||||||
|
TOK[Token Endpoint]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Lớp Application - CQRS"
|
||||||
CMD[Commands]
|
CMD[Commands]
|
||||||
Q[Queries]
|
QRY[Queries]
|
||||||
B[Behaviors]
|
VAL[Validators]
|
||||||
V[Validations]
|
BHV[Behaviors]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph "Lớp Domain"
|
subgraph "Lớp Domain"
|
||||||
AR[Aggregate Roots]
|
USER[User Aggregate]
|
||||||
E[Entities]
|
ROLE[Role Aggregate]
|
||||||
VO[Value Objects]
|
EVT[Domain Events]
|
||||||
DE[Domain Events]
|
|
||||||
DX[Domain Exceptions]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph "Lớp Infrastructure"
|
subgraph "Infrastructure"
|
||||||
|
CTX[Identity DbContext]
|
||||||
|
REPO[Repositories]
|
||||||
|
OIDDICT[OpenIddict]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "External"
|
||||||
DB[(PostgreSQL)]
|
DB[(PostgreSQL)]
|
||||||
R[Repositories]
|
REDIS[(Redis)]
|
||||||
CTX[DbContext]
|
|
||||||
ID[Idempotency]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
C --> CMD
|
WEB --> AUTH
|
||||||
C --> Q
|
MOB --> AUTH
|
||||||
CMD --> B --> V
|
SVC --> TOK
|
||||||
CMD --> AR
|
AUTH --> CMD
|
||||||
Q --> R
|
AUTH --> QRY
|
||||||
R --> CTX --> DB
|
USR --> CMD
|
||||||
AR --> DE
|
USR --> QRY
|
||||||
R --> AR
|
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 AUTH fill:#4a90d9,stroke:#2d5986,color:#fff
|
||||||
style AR fill:#50c878,stroke:#2d8659,color:#fff
|
style USER fill:#50c878,stroke:#2d8659,color:#fff
|
||||||
style DB fill:#ff6b6b,stroke:#c0392b,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
|
## Luồng Xác Thực OAuth2
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Client
|
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<br/>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<br/>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<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
|
||||||
|
|
||||||
|
```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 {
|
||||||
|
<<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
|
||||||
|
|
||||||
|
```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 Controller
|
||||||
participant MediatR
|
participant MediatR
|
||||||
participant LoggingBehavior
|
participant LoggingBehavior
|
||||||
@@ -88,114 +225,68 @@ sequenceDiagram
|
|||||||
participant TransactionBehavior
|
participant TransactionBehavior
|
||||||
participant CommandHandler
|
participant CommandHandler
|
||||||
participant Repository
|
participant Repository
|
||||||
participant DbContext
|
|
||||||
|
|
||||||
Client->>Controller: HTTP Request
|
|
||||||
Controller->>MediatR: Send(Command)
|
Controller->>MediatR: Send(Command)
|
||||||
MediatR->>LoggingBehavior: Handle
|
MediatR->>LoggingBehavior: Handle
|
||||||
LoggingBehavior->>ValidatorBehavior: Next()
|
LoggingBehavior->>ValidatorBehavior: Next()
|
||||||
ValidatorBehavior->>TransactionBehavior: Next()
|
ValidatorBehavior->>TransactionBehavior: Next()
|
||||||
TransactionBehavior->>CommandHandler: Next()
|
TransactionBehavior->>CommandHandler: Next()
|
||||||
CommandHandler->>Repository: Add/Update/Delete
|
CommandHandler->>Repository: Save
|
||||||
Repository->>DbContext: SaveEntitiesAsync()
|
|
||||||
DbContext-->>Repository: Success
|
|
||||||
Repository-->>CommandHandler: Result
|
Repository-->>CommandHandler: Result
|
||||||
CommandHandler-->>Controller: Response
|
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
|
```mermaid
|
||||||
graph LR
|
graph TD
|
||||||
AR[Aggregate Root] -->|Phát sinh| DE[Domain Event]
|
subgraph "Authentication"
|
||||||
DE -->|Dispatch bởi| CTX[DbContext]
|
JWT[JWT Bearer Tokens]
|
||||||
CTX -->|Publish tới| M[MediatR]
|
RS256[RS256 Signing]
|
||||||
M -->|Xử lý bởi| H1[Handler 1]
|
OIDC[OpenIddict Server]
|
||||||
M -->|Xử lý bởi| H2[Handler 2]
|
end
|
||||||
|
|
||||||
style AR fill:#50c878,stroke:#2d8659,color:#fff
|
subgraph "Authorization"
|
||||||
style DE fill:#f39c12,stroke:#d68910,color:#fff
|
RBAC[Role-Based Access]
|
||||||
style M fill:#9b59b6,stroke:#7d3c98,color:#fff
|
CLAIMS[Claims-Based]
|
||||||
```
|
POLICY[Policy Enforcement]
|
||||||
|
end
|
||||||
## 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
|
|
||||||
}
|
|
||||||
|
|
||||||
sample_statuses {
|
subgraph "Protection"
|
||||||
int id PK
|
HASH[bcrypt Password Hash]
|
||||||
varchar(50) name
|
HTTPS[HTTPS/TLS]
|
||||||
}
|
CORS[CORS Policy]
|
||||||
|
end
|
||||||
|
|
||||||
samples ||--o{ sample_statuses : has
|
JWT --> RS256
|
||||||
```
|
RS256 --> OIDC
|
||||||
|
RBAC --> CLAIMS
|
||||||
## Pipeline MediatR
|
CLAIMS --> POLICY
|
||||||
|
|
||||||
```
|
style JWT fill:#3498db,stroke:#2980b9,color:#fff
|
||||||
Request → LoggingBehavior → ValidatorBehavior → TransactionBehavior → Handler → Response
|
style RBAC fill:#e74c3c,stroke:#c0392b,color:#fff
|
||||||
│ │ │
|
style HASH fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||||
▼ ▼ ▼
|
|
||||||
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"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Health Checks
|
## Health Checks
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TD
|
graph TD
|
||||||
HC[Health Check Endpoint]
|
HC[Health Check Endpoints]
|
||||||
HC --> |/health/live| L[Liveness]
|
HC -->|/health/live| L[Liveness Probe]
|
||||||
HC --> |/health/ready| R[Readiness]
|
HC -->|/health/ready| R[Readiness Probe]
|
||||||
HC --> |/health| F[Full Status]
|
HC -->|/health| F[Full Status]
|
||||||
|
|
||||||
R --> PG[(PostgreSQL)]
|
R --> PG[(PostgreSQL Check)]
|
||||||
R --> RD[(Redis)]
|
R --> RD[(Redis Check)]
|
||||||
|
|
||||||
style HC fill:#3498db,stroke:#2980b9,color:#fff
|
style HC fill:#3498db,stroke:#2980b9,color:#fff
|
||||||
style L fill:#2ecc71,stroke:#27ae60,color:#fff
|
style L fill:#2ecc71,stroke:#27ae60,color:#fff
|
||||||
@@ -204,17 +295,19 @@ graph TD
|
|||||||
|
|
||||||
## Kiến Trúc Deployment
|
## Kiến Trúc Deployment
|
||||||
|
|
||||||
### Docker Compose (Local)
|
### Docker Compose (Local/Development)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
iamservice-api:
|
iam-service:
|
||||||
build: .
|
build: .
|
||||||
ports: ["5000:8080"]
|
ports: ["5001:8080"]
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
- redis
|
- redis
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=Host=postgres;...
|
||||||
|
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
|
|
||||||
@@ -228,14 +321,14 @@ services:
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: iamservice-api
|
name: iam-service
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: api
|
- name: iam-service
|
||||||
image: iamservice:latest
|
image: goodgo/iam-service:latest
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8080
|
- containerPort: 8080
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
@@ -248,24 +341,43 @@ spec:
|
|||||||
port: 8080
|
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)
|
### Phân Cấp Exception
|
||||||
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
|
|
||||||
|
|
||||||
## Tối Ưu Hiệu Năng
|
```
|
||||||
|
Exception
|
||||||
|
└── DomainException
|
||||||
|
└── (Custom domain exceptions)
|
||||||
|
```
|
||||||
|
|
||||||
1. **Connection Pooling**: EF Core với Npgsql connection resilience
|
### Problem Details (RFC 7807)
|
||||||
2. **Async/Await**: Tất cả I/O operations đều async
|
|
||||||
3. **Response Caching**: Thêm caching headers cho queries
|
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
|
4. **Database Indexes**: Cấu hình trong EntityConfigurations
|
||||||
|
|
||||||
## Tài Liệu Tham Khảo
|
## 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)
|
- [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)
|
- [CQRS Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs)
|
||||||
|
|||||||
@@ -68,22 +68,88 @@ dotnet run --project src/IamService.API
|
|||||||
| `/health/live` | Kiểm tra sống |
|
| `/health/live` | Kiểm tra sống |
|
||||||
| `/health/ready` | Kiểm tra sẵn 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
|
```bash
|
||||||
curl -X POST http://localhost:5001/connect/token \
|
curl -X POST http://localhost:5001/api/v1/auth/register \
|
||||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
-H "Content-Type: application/json" \
|
||||||
-d "grant_type=password&username=user@example.com&password=YourPassword&scope=openid profile email roles api"
|
-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
|
```bash
|
||||||
curl -X POST http://localhost:5001/connect/token \
|
curl -X POST http://localhost:5001/connect/token \
|
||||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
-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
|
## Swagger UI
|
||||||
|
|||||||
Reference in New Issue
Block a user