16 KiB
16 KiB
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
Authorization Policies (Phân Quyền API)
Lưu ý: Tất cả API endpoints yêu cầu xác thực (Bearer JWT Token).
Một số endpoints yêu cầu role cụ thể như bảng dưới đây.
| Policy | Required Role | Áp Dụng Cho |
|---|---|---|
RequireSuperAdmin |
SuperAdmin | PAM (Privileged Access Management) |
RequireAdmin |
Admin, SuperAdmin | User/Role/Group/Organization management |
RequireAuditor |
Auditor, Admin, SuperAdmin | Audit logs, Compliance reports |
OwnerOrAdmin |
Owner hoặc Admin | User tự quản lý profile của mình |
Chi tiết phân quyền theo Controller:
| Controller | Policy | Mô tả |
|---|---|---|
| Users (GET /users, DELETE) | RequireAdmin | Xem danh sách users, xóa user |
| Users (GET/PUT /{id}) | OwnerOrAdmin | User xem/sửa profile mình hoặc Admin |
| Roles | RequireAdmin | Quản lý roles |
| Organizations | RequireAdmin | Quản lý tổ chức |
| Groups | RequireAdmin | Quản lý nhóm |
| Access Requests | RequireAdmin | Xử lý yêu cầu truy cập |
| Access Reviews | RequireAdmin | Xem xét truy cập định kỳ |
| Privileged Access | RequireSuperAdmin | PAM - nhạy cảm nhất |
| Audit | RequireAuditor | Xem audit logs |
| Compliance | RequireAuditor | Báo cáo tuân thủ |
| Verifications | RequireAdmin | Xác thực danh tính |
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