- Added detailed sections for Organization, Group, Access Request, Access Review, and Audit & Compliance aggregates in both English and Vietnamese. - Included class diagrams and enumerations to enhance understanding of the new structures and their relationships. - Updated the AuditEventType table to reflect 18 event types, improving clarity on event handling within the IAM Service.
IAM Service .NET 10
Service IAM (Identity and Access Management) .NET 10 với OAuth2/OIDC sử dụng Duende IdentityServer.
Tổng Quan
IAM Service cung cấp các chức năng quản lý danh tính và truy cập:
- OAuth2/OIDC - Authentication với Duende IdentityServer
- User Management - CRUD operations cho users
- Password Management - Đổi mật khẩu
- Token Management - Issue, refresh, revoke tokens
- Email Verification - Xác thực email qua SMTP
- 2FA/MFA - Xác thực hai yếu tố với TOTP
- Social Login - Đăng nhập qua Google và Facebook
- CQRS Pattern - MediatR cho Commands/Queries
- Clean Architecture - Domain, Infrastructure, API layers
Yêu Cầu
| Yêu cầu | Phiên bản |
|---|---|
| .NET SDK | 10.0.101+ |
| Docker | 24.0+ |
| PostgreSQL | 15+ |
Bắt Đầu Nhanh
Chạy với Docker
cd deployments/local
docker-compose up -d
Chạy Local
cd services/iam-service-net
dotnet restore
dotnet build
dotnet run --project src/IamService.API
Database Migrations
Yêu Cầu
# Cài đặt EF Core tools (một lần)
dotnet tool install --global dotnet-ef
Tạo Migration
# Tạo migration mới
dotnet ef migrations add <TenMigration> \
--project src/IamService.Infrastructure \
--startup-project src/IamService.API
Áp Dụng Migration
# Áp dụng migrations vào database
dotnet ef database update \
--project src/IamService.Infrastructure \
--startup-project src/IamService.API
Các Lệnh Khác
# Xóa migration cuối (nếu chưa áp dụng)
dotnet ef migrations remove \
--project src/IamService.Infrastructure \
--startup-project src/IamService.API
# Tạo SQL script
dotnet ef migrations script \
--project src/IamService.Infrastructure \
--startup-project src/IamService.API \
--output migrations.sql
# Liệt kê migrations
dotnet ef migrations list \
--project src/IamService.Infrastructure \
--startup-project src/IamService.API
Thiết Lập Neon Database
- Tạo database trên Neon Console
- Sao chép connection string
- Cập nhật
appsettings.Development.json:
{
"ConnectionStrings": {
"DefaultConnection": "Host=<host>;Port=5432;Database=<db>;Username=<user>;Password=<pass>;SSL Mode=Require"
}
}
- Chạy migrations:
dotnet ef database update ...
API Endpoints
Authentication (/api/v1/auth)
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/auth/register |
Đăng ký user mới | ❌ |
POST |
/connect/token |
OAuth2 Token (login, refresh) | ❌ |
POST |
/api/v1/auth/change-password |
Đổi mật khẩu | ✅ |
POST |
/api/v1/auth/logout |
Đăng xuất (revoke tokens) | ✅ |
Email Verification (/api/v1/auth)
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/auth/send-verification-email |
Gửi email xác thực | ✅ |
POST |
/api/v1/auth/confirm-email |
Xác nhận email với token | ❌ |
Xác Thực Hai Yếu Tố (/api/v1/auth/2fa)
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/auth/2fa/enable |
Bật 2FA (lấy QR code) | ✅ |
POST |
/api/v1/auth/2fa/verify |
Xác minh mã TOTP & kích hoạt | ✅ |
POST |
/api/v1/auth/2fa/disable |
Tắt 2FA | ✅ |
Đăng Nhập Mạng Xã Hội (/api/v1/auth)
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
GET |
/api/v1/auth/external-login/{provider} |
Bắt đầu OAuth flow (Google/Facebook) | ❌ |
GET |
/api/v1/auth/external-callback |
Xử lý OAuth callback | ❌ |
GET |
/api/v1/auth/linked-accounts |
Lấy danh sách providers đã liên kết | ✅ |
User Management (/api/v1/users)
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
GET |
/api/v1/users |
Lấy danh sách users (phân trang) | ✅ |
GET |
/api/v1/users/me |
Lấy thông tin user hiện tại | ✅ |
GET |
/api/v1/users/{id} |
Lấy user theo ID | ✅ |
PUT |
/api/v1/users/{id} |
Cập nhật thông tin user | ✅ |
DELETE |
/api/v1/users/{id} |
Xóa user (soft delete) | ✅ |
Health Checks
| Endpoint | Mục Đích |
|---|---|
/health |
Trạng thái health đầy đủ |
/health/live |
Kiểm tra sống |
/health/ready |
Kiểm tra sẵn sàng |
Organizations (/api/v1/organizations) - Phase 2
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
GET |
/api/v1/organizations/{id} |
Lấy tổ chức theo ID | ✅ |
GET |
/api/v1/organizations/slug/{slug} |
Lấy tổ chức theo slug | ✅ |
POST |
/api/v1/organizations |
Tạo tổ chức mới | ✅ |
PUT |
/api/v1/organizations/{id} |
Cập nhật tổ chức | ✅ |
DELETE |
/api/v1/organizations/{id} |
Lưu trữ tổ chức | ✅ |
GET |
/api/v1/organizations/{id}/hierarchy |
Lấy phân cấp | ✅ |
GET |
/api/v1/organizations/{id}/children |
Lấy tổ chức con | ✅ |
Groups (/api/v1/groups) - Phase 2
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
GET |
/api/v1/groups |
Danh sách groups | ✅ |
GET |
/api/v1/groups/{id} |
Lấy group theo ID | ✅ |
POST |
/api/v1/groups |
Tạo group mới | ✅ |
DELETE |
/api/v1/groups/{id} |
Xóa group | ✅ |
POST |
/api/v1/groups/{id}/members |
Thêm thành viên | ✅ |
DELETE |
/api/v1/groups/{id}/members/{userId} |
Xóa thành viên | ✅ |
Access Requests (/api/v1/access-requests) - Phase 3A
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/access-requests |
Tạo yêu cầu truy cập | ✅ |
GET |
/api/v1/access-requests |
Danh sách requests | ✅ |
GET |
/api/v1/access-requests/{id} |
Lấy request theo ID | ✅ |
POST |
/api/v1/access-requests/{id}/submit |
Submit request | ✅ |
POST |
/api/v1/access-requests/{id}/approve |
Phê duyệt | ✅ |
POST |
/api/v1/access-requests/{id}/reject |
Từ chối | ✅ |
DELETE |
/api/v1/access-requests/{id} |
Hủy request | ✅ |
GET |
/api/v1/access-requests/pending |
Requests đang chờ | ✅ |
Access Reviews (/api/v1/access-reviews) - Phase 3B
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/access-reviews |
Tạo access review | ✅ |
GET |
/api/v1/access-reviews/{id} |
Lấy review theo ID | ✅ |
POST |
/api/v1/access-reviews/{id}/items |
Thêm item | ✅ |
POST |
/api/v1/access-reviews/{id}/start |
Bắt đầu review | ✅ |
POST |
/api/v1/access-reviews/{id}/items/{itemId}/review |
Certify/Revoke | ✅ |
POST |
/api/v1/access-reviews/{id}/complete |
Hoàn thành | ✅ |
Privileged Access (/api/v1/privileged-access) - Phase 3B PAM
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/privileged-access/request |
Yêu cầu JIT access | ✅ |
GET |
/api/v1/privileged-access/active |
Grants đang active | ✅ |
POST |
/api/v1/privileged-access/{id}/revoke |
Thu hồi access | ✅ |
Audit (/api/v1/audit) - Phase 4A
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
GET |
/api/v1/audit/logs |
Lấy audit logs (filtered) | ✅ |
Compliance (/api/v1/compliance) - Phase 4A
| Method | Endpoint | Mô Tả | Auth |
|---|---|---|---|
POST |
/api/v1/compliance/reports |
Generate report | ✅ |
GET |
/api/v1/compliance/reports |
Danh sách reports | ✅ |
GET |
/api/v1/compliance/reports/{id} |
Report chi tiết | ✅ |
POST |
/api/v1/compliance/reports/{id}/complete |
Hoàn thành report | ✅ |
GET |
/api/v1/compliance/violations |
Violations chưa giải quyết | ✅ |
Quy Trình Xác Thực
Bước 1: Đăng Ký User Mới
curl -X POST http://localhost:5001/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "Password123!",
"firstName": "John",
"lastName": "Doe"
}'
Response:
{
"success": true,
"data": {
"userId": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com"
}
}
Bước 2: Đăng Nhập (Password Grant)
curl -X POST http://localhost:5001/connect/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password" \
-d "client_id=password-client" \
-d "client_secret=password-client-secret" \
-d "username=user@example.com" \
-d "password=Password123!" \
-d "scope=openid profile email api offline_access"
Response:
{
"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ệ:
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)
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
curl -X POST http://localhost:5001/api/v1/auth/logout \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Client Credentials (Service-to-Service)
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"
Xác Thực Email
Gửi Email Xác Thực
curl -X POST http://localhost:5001/api/v1/auth/send-verification-email \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
Xác Nhận Email
curl -X POST http://localhost:5001/api/v1/auth/confirm-email \
-H "Content-Type: application/json" \
-d '{"userId": "user-guid", "token": "confirmation-token"}'
Xác Thực Hai Yếu Tố (2FA)
Bật 2FA
curl -X POST http://localhost:5001/api/v1/auth/2fa/enable \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response:
{
"success": true,
"data": {
"secretKey": "JBSWY3DPEHPK3PXP",
"qrCodeBase64": "data:image/png;base64,...",
"recoveryCodes": ["code1", "code2", "code3", ...]
}
}
Xác Minh Mã 2FA
curl -X POST http://localhost:5001/api/v1/auth/2fa/verify \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"code": "123456"}'
Tắt 2FA
curl -X POST http://localhost:5001/api/v1/auth/2fa/disable \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"code": "123456"}'
Đăng Nhập Mạng Xã Hội
Bắt Đầu OAuth Flow
Chuyển hướng user tới:
GET http://localhost:5001/api/v1/auth/external-login/Google?returnUrl=http://your-app/callback
GET http://localhost:5001/api/v1/auth/external-login/Facebook?returnUrl=http://your-app/callback
Lấy Tài Khoản Đã Liên Kết
curl http://localhost:5001/api/v1/auth/linked-accounts \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response:
{
"success": true,
"data": {
"linkedProviders": [
{"provider": "Google", "providerDisplayName": "Google"},
{"provider": "Facebook", "providerDisplayName": "Facebook"}
]
}
}
Swagger UI
Sau khi chạy service, truy cập Swagger UI tại:
- Local: http://localhost:5001/swagger
- Docker: http://localhost/api/v1/iam/swagger
Cấu Hình
Biến Môi Trường
| Biến | Mô Tả | Mặc định |
|---|---|---|
ASPNETCORE_ENVIRONMENT |
Môi trường | Development |
DATABASE_URL |
PostgreSQL connection | - |
JWT_SECRET |
Secret ký JWT (32+ ký tự) | - |
REDIS_HOST |
Redis server host | localhost |
REDIS_PORT |
Redis server port | 6379 |
REDIS_PASSWORD |
Redis password | - |
REDIS_DATABASE |
Redis database number | 0 |
Redis Caching
Service sử dụng Redis cho distributed caching thông qua interface ICacheService.
Cấu Hình Redis
Thêm cấu hình Redis trong appsettings.json:
{
"Redis": {
"Host": "localhost",
"Port": 6379,
"Password": "",
"Database": 0,
"ConnectTimeout": 5000,
"SyncTimeout": 5000
}
}
Hoặc sử dụng biến môi trường:
REDIS_HOST=your-redis-host
REDIS_PORT=6379
REDIS_PASSWORD=your-password
REDIS_DATABASE=0
ICacheService Interface
public interface ICacheService
{
// Các thao tác cơ bản
Task<T?> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value, TimeSpan? expiration = null);
Task RemoveAsync(string key);
Task<bool> ExistsAsync(string key);
// Get hoặc tạo mới pattern
Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null);
// Hỗ trợ blacklist token
Task BlacklistAsync(string key, TimeSpan expiration);
Task<bool> IsBlacklistedAsync(string key);
}
Ví Dụ Sử Dụng
Get/Set cơ bản:
public class MyService
{
private readonly ICacheService _cache;
public MyService(ICacheService cache) => _cache = cache;
public async Task<User?> GetUser(string userId)
{
return await _cache.GetAsync<User>($"user:{userId}");
}
public async Task CacheUser(User user)
{
await _cache.SetAsync($"user:{user.Id}", user, TimeSpan.FromMinutes(15));
}
}
Get or Set Pattern (Cache-Aside):
public async Task<User> GetUserById(string userId)
{
return await _cache.GetOrSetAsync(
$"user:{userId}",
async () => await _repository.GetByIdAsync(userId),
TimeSpan.FromMinutes(15)
);
}
Blacklist Token (cho Logout):
public async Task Logout(string tokenId)
{
// Blacklist refresh token trong thời gian còn lại của token
await _cache.BlacklistAsync($"token:{tokenId}", TimeSpan.FromDays(7));
}
public async Task<bool> IsTokenRevoked(string tokenId)
{
return await _cache.IsBlacklistedAsync($"token:{tokenId}");
}
Kiểm Thử
dotnet test
Triển Khai
Docker Build
docker build -t goodgo/iam-service:latest .
docker run -p 5001:8080 --env-file .env goodgo/iam-service:latest