Files
pos-system/docs/vi/guides/iam-authentication.md
Ho Ngoc Hai 928a22fe3e feat(authentication): Implement email verification, two-factor authentication, and social login features
- 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.
2026-01-12 23:07:53 +07:00

10 KiB

Hướng Dẫn Xác Thực IAM Service

Hướng dẫn đầy đủ về xác thực sử dụng GoodGo IAM Service với OAuth2/OpenID Connect.

Tổng Quan

IAM Service cung cấp xác thực OAuth2/OIDC sử dụng Duende IdentityServer:

  • Password Grant - Đăng nhập user với email/password
  • Refresh Token - Làm mới token mà không cần xác thực lại
  • Client Credentials - Xác thực service-to-service
  • Email Verification - Xác minh email qua SMTP
  • Two-Factor Authentication (2FA) - TOTP với QR code và recovery codes
  • Social Login - Tích hợp OAuth Google và Facebook

Bắt Đầu Nhanh

1. Đăng Ký User

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"
  }'

2. Đăng Nhập (Lấy Tokens)

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:

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "scope": "openid profile email offline_access"
}

3. Sử Dụng Access Token

curl http://localhost:5001/api/v1/users/me \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Các Loại OAuth2 Grant

Password Grant (Đăng Nhập User)

Cho các ứng dụng đáng tin cậy nơi users nhập thông tin đăng nhập trực tiếp.

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"
Tham số Bắt buộc Mô tả
grant_type Phải là password
username Email của user
password Mật khẩu của user
scope Không Các scopes cách nhau bằng dấu cách

Refresh Token

Làm mới access token mà không cần nhập lại thông tin đăng nhập.

curl -X POST http://localhost:5001/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=eyJhbGciOiJSUzI1NiIs..."
Tham số Bắt buộc Mô tả
grant_type Phải là refresh_token
refresh_token Refresh token hợp lệ

Client Credentials (Service-to-Service)

Cho các backend services xác thực mà không cần user context.

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"
Tham số Bắt buộc Mô tả
grant_type Phải là client_credentials
client_id Client ID đã đăng ký
client_secret Client secret
scope Không Các scopes yêu cầu

Lưu ý: Client applications phải được đăng ký trong database trước khi sử dụng.

Thời Hạn Token

Loại Token Thời hạn Sử dụng
Access Token 15 phút API authorization
Refresh Token 7 ngày Làm mới token

Các Scopes Hỗ Trợ

Scope Mô tả
openid Bắt buộc cho OIDC
profile Thông tin profile (tên)
email Email của user
roles Các roles của user
offline_access Nhận refresh token
api Truy cập API

API Endpoints

Xác Thực

Method Endpoint Mô tả
POST /api/v1/auth/register Đăng ký user mới
POST /connect/token OAuth2 token endpoint
POST /api/v1/auth/change-password Đổi mật khẩu (cần auth)
POST /api/v1/auth/logout Thu hồi tokens (cần auth)

Xác Minh Email

Method Endpoint Mô tả
POST /api/v1/auth/send-verification-email Gửi link xác minh email (cần auth)
POST /api/v1/auth/confirm-email Xác nhận email với token

Xác Thực Hai Yếu Tố (2FA)

Method Endpoint Mô tả
POST /api/v1/auth/2fa/enable Bật 2FA (lấy QR code) (cần auth)
POST /api/v1/auth/2fa/verify Xác minh mã TOTP & kích hoạt (cần auth)
POST /api/v1/auth/2fa/disable Tắt 2FA (cần auth)

Đăng Nhập Mạng Xã Hội

Method Endpoint Mô tả
GET /api/v1/auth/external-login/{provider} Khởi tạo 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 OAuth providers đã liên kết (cần auth)

Quản Lý User

Method Endpoint Mô tả
GET /api/v1/users Danh sách users (phân trang)
GET /api/v1/users/me 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 user
DELETE /api/v1/users/{id} Xóa user (soft)

Ví Dụ Tích Hợp

JavaScript/TypeScript

// Đăng nhập
const response = await fetch('http://localhost:5001/connect/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'password',
    username: 'user@example.com',
    password: 'Password123!',
    scope: 'openid profile email offline_access'
  })
});
const { access_token, refresh_token } = await response.json();

// Sử dụng token
const user = await fetch('http://localhost:5001/api/v1/users/me', {
  headers: { 'Authorization': `Bearer ${access_token}` }
}).then(r => r.json());

C# / .NET

// Đăng nhập
var client = new HttpClient();
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
    ["grant_type"] = "password",
    ["username"] = "user@example.com",
    ["password"] = "Password123!",
    ["scope"] = "openid profile email offline_access"
});
var response = await client.PostAsync("http://localhost:5001/connect/token", content);
var tokens = await response.Content.ReadFromJsonAsync<TokenResponse>();

// Sử dụng token
client.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
var user = await client.GetFromJsonAsync<UserDto>("/api/v1/users/me");

Xác Minh Email

Gửi Email Xác Minh

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

Khởi Tạo OAuth Flow

Chuyển hướng user đến:

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 Danh Sách 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"}
    ]
  }
}

Xử Lý Lỗi

Các Lỗi Thường Gặp

Lỗi HTTP Code Mô tả
invalid_grant 400 Thông tin đăng nhập không hợp lệ
invalid_client 400 client_id không xác định
invalid_scope 400 Scope yêu cầu không hợp lệ
unauthorized 401 Token không hợp lệ/hết hạn

Định Dạng Lỗi

{
  "error": "invalid_grant",
  "error_description": "The username or password is incorrect."
}

Best Practices Bảo Mật

  1. Lưu trữ tokens an toàn - Sử dụng httpOnly cookies hoặc secure storage
  2. Làm mới tokens chủ động - Trước khi access token hết hạn
  3. Sử dụng HTTPS trong production - Không bao giờ gửi credentials qua HTTP
  4. Đăng xuất đúng cách - Gọi logout endpoint để thu hồi tokens
  5. Validate tokens phía server - Không tin tưởng validation phía client

Swagger UI

Truy cập tài liệu API tương tác:

Tài Liệu Liên Quan