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:
Ho Ngoc Hai
2026-01-12 16:37:31 +07:00
parent c621afbb74
commit 4ae24a7bc8
5 changed files with 873 additions and 531 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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:
-**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)

View File

@@ -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