feat(docs): Update authentication flow documentation and improve clarity
- Revised IAM service documentation to reflect recent changes in the authentication flow, including user registration and token management. - Enhanced API documentation with clearer examples and updated architecture diagrams for better understanding. - Added comprehensive sections on OAuth2 grant types and user management to assist developers. - Streamlined Vietnamese documentation for consistency and improved accessibility.
This commit is contained in:
237
docs/en/guides/iam-authentication.md
Normal file
237
docs/en/guides/iam-authentication.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# IAM Service Authentication Guide
|
||||
|
||||
> Complete guide for authentication using GoodGo IAM Service with OAuth2/OpenID Connect.
|
||||
|
||||
## Overview
|
||||
|
||||
The IAM Service provides OAuth2/OIDC authentication using OpenIddict:
|
||||
- **Password Grant** - User login with email/password
|
||||
- **Refresh Token** - Token renewal without re-authentication
|
||||
- **Client Credentials** - Service-to-service authentication
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Register a User
|
||||
|
||||
```bash
|
||||
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. Login (Get Tokens)
|
||||
|
||||
```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
|
||||
{
|
||||
"access_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 900,
|
||||
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||
"scope": "openid profile email offline_access"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Use Access Token
|
||||
|
||||
```bash
|
||||
curl http://localhost:5001/api/v1/users/me \
|
||||
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
|
||||
```
|
||||
|
||||
## OAuth2 Grant Types
|
||||
|
||||
### Password Grant (User Login)
|
||||
|
||||
For trusted first-party applications where users enter credentials directly.
|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `grant_type` | Yes | Must be `password` |
|
||||
| `username` | Yes | User's email |
|
||||
| `password` | Yes | User's password |
|
||||
| `scope` | No | Space-separated scopes |
|
||||
|
||||
### Refresh Token
|
||||
|
||||
Renew access token without re-entering credentials.
|
||||
|
||||
```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=eyJhbGciOiJSUzI1NiIs..."
|
||||
```
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `grant_type` | Yes | Must be `refresh_token` |
|
||||
| `refresh_token` | Yes | Valid refresh token |
|
||||
|
||||
### Client Credentials (Service-to-Service)
|
||||
|
||||
For backend services authenticating 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"
|
||||
```
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------|
|
||||
| `grant_type` | Yes | Must be `client_credentials` |
|
||||
| `client_id` | Yes | Registered client ID |
|
||||
| `client_secret` | Yes | Client secret |
|
||||
| `scope` | No | Requested scopes |
|
||||
|
||||
> **Note:** Client applications must be registered in the database before use.
|
||||
|
||||
## Token Lifetimes
|
||||
|
||||
| Token Type | Lifetime | Use |
|
||||
|------------|----------|-----|
|
||||
| Access Token | 15 minutes | API authorization |
|
||||
| Refresh Token | 7 days | Token renewal |
|
||||
|
||||
## Available Scopes
|
||||
|
||||
| Scope | Description |
|
||||
|-------|-------------|
|
||||
| `openid` | Required for OIDC |
|
||||
| `profile` | User profile (name) |
|
||||
| `email` | User email |
|
||||
| `roles` | User roles |
|
||||
| `offline_access` | Get refresh token |
|
||||
| `api` | API access |
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Authentication
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `POST` | `/api/v1/auth/register` | Register new user |
|
||||
| `POST` | `/connect/token` | OAuth2 token endpoint |
|
||||
| `POST` | `/api/v1/auth/change-password` | Change password (auth required) |
|
||||
| `POST` | `/api/v1/auth/logout` | Revoke tokens (auth required) |
|
||||
|
||||
### User Management
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| `GET` | `/api/v1/users` | List users (paginated) |
|
||||
| `GET` | `/api/v1/users/me` | Current user info |
|
||||
| `GET` | `/api/v1/users/{id}` | Get user by ID |
|
||||
| `PUT` | `/api/v1/users/{id}` | Update user |
|
||||
| `DELETE` | `/api/v1/users/{id}` | Delete user (soft) |
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### JavaScript/TypeScript
|
||||
|
||||
```typescript
|
||||
// Login
|
||||
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();
|
||||
|
||||
// Use token
|
||||
const user = await fetch('http://localhost:5001/api/v1/users/me', {
|
||||
headers: { 'Authorization': `Bearer ${access_token}` }
|
||||
}).then(r => r.json());
|
||||
```
|
||||
|
||||
### C# / .NET
|
||||
|
||||
```csharp
|
||||
// Login
|
||||
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>();
|
||||
|
||||
// Use token
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
|
||||
var user = await client.GetFromJsonAsync<UserDto>("/api/v1/users/me");
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
### Common Errors
|
||||
|
||||
| Error | HTTP Code | Description |
|
||||
|-------|-----------|-------------|
|
||||
| `invalid_grant` | 400 | Invalid credentials |
|
||||
| `invalid_client` | 400 | Unknown client_id |
|
||||
| `invalid_scope` | 400 | Invalid scope requested |
|
||||
| `unauthorized` | 401 | Invalid/expired token |
|
||||
|
||||
### Error Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_grant",
|
||||
"error_description": "The username or password is incorrect."
|
||||
}
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Store tokens securely** - Use httpOnly cookies or secure storage
|
||||
2. **Refresh tokens proactively** - Before access token expires
|
||||
3. **Use HTTPS in production** - Never send credentials over HTTP
|
||||
4. **Logout properly** - Call logout endpoint to revoke tokens
|
||||
5. **Validate tokens server-side** - Don't trust client-side validation
|
||||
|
||||
## Swagger UI
|
||||
|
||||
Access interactive API documentation:
|
||||
- **Local**: http://localhost:5001/swagger
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [IAM Migration Guide](./iam-migration.md)
|
||||
- [Local Development](./local-development.md)
|
||||
- [Security Architecture](../architecture/security-architecture.md)
|
||||
237
docs/vi/guides/iam-authentication.md
Normal file
237
docs/vi/guides/iam-authentication.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 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 OpenIddict:
|
||||
- **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
|
||||
|
||||
## Bắt Đầu Nhanh
|
||||
|
||||
### 1. Đăng Ký User
|
||||
|
||||
```bash
|
||||
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)
|
||||
|
||||
```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
|
||||
{
|
||||
"access_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 900,
|
||||
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||
"scope": "openid profile email offline_access"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Sử Dụng Access Token
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
| Tham số | Bắt buộc | Mô tả |
|
||||
|---------|----------|-------|
|
||||
| `grant_type` | Có | Phải là `password` |
|
||||
| `username` | Có | Email của user |
|
||||
| `password` | Có | 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.
|
||||
|
||||
```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=eyJhbGciOiJSUzI1NiIs..."
|
||||
```
|
||||
|
||||
| Tham số | Bắt buộc | Mô tả |
|
||||
|---------|----------|-------|
|
||||
| `grant_type` | Có | Phải là `refresh_token` |
|
||||
| `refresh_token` | Có | 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.
|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
| Tham số | Bắt buộc | Mô tả |
|
||||
|---------|----------|-------|
|
||||
| `grant_type` | Có | Phải là `client_credentials` |
|
||||
| `client_id` | Có | Client ID đã đăng ký |
|
||||
| `client_secret` | Có | 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) |
|
||||
|
||||
### 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
|
||||
|
||||
```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
|
||||
|
||||
```csharp
|
||||
// Đă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ử 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
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
- **Local**: http://localhost:5001/swagger
|
||||
|
||||
## Tài Liệu Liên Quan
|
||||
|
||||
- [Hướng Dẫn IAM Migration](./iam-migration.md)
|
||||
- [Local Development](./local-development.md)
|
||||
- [Security Architecture](../architecture/security-architecture.md)
|
||||
Reference in New Issue
Block a user