- Added endpoints for sending and confirming email verification, enhancing user account security. - Integrated two-factor authentication (2FA) with TOTP support, including enabling, verifying, and disabling 2FA. - Implemented social login functionality for Google and Facebook, allowing users to authenticate using their existing accounts. - Updated dependency injection to include services for email, 2FA, and social login. - Enhanced documentation to reflect new features and usage examples for email verification and 2FA.
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 |
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