Major Features: - ✅ Multi-tenant architecture (tenant isolation) - ✅ Employee CRUD with lifecycle management (onboarding/offboarding) - ✅ Department tree structure with email domain management - ✅ Company info management (single-record editing) - ✅ System functions CRUD (permission management) - ✅ Email account management (multi-account per employee) - ✅ Keycloak SSO integration (auth.lab.taipei) - ✅ Redis session storage (10.1.0.254:6379) - Solves Cookie 4KB limitation - Cross-system session sharing - Sliding expiration (8 hours) - Automatic token refresh Technical Stack: Backend: - FastAPI + SQLAlchemy - PostgreSQL 16 (10.1.0.20:5433) - Keycloak Admin API integration - Docker Mailserver integration (SSH) - Alembic migrations Frontend: - Next.js 14 (App Router) - NextAuth 4 with Keycloak Provider - Redis session storage (ioredis) - Tailwind CSS Infrastructure: - Redis 7 (10.1.0.254:6379) - Session + Cache - Keycloak 26.1.0 (auth.lab.taipei) - Docker Mailserver (10.1.0.254) Architecture Highlights: - Session管理由 Keycloak + Redis 統一控制 - 支援多系統 (HR/WebMail/Calendar/Drive/Office) 共享 session - Token 自動刷新,異質服務整合 - 未來可無縫遷移到雲端 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
428 lines
9.1 KiB
Markdown
428 lines
9.1 KiB
Markdown
# Keycloak 配置指南 - HR Portal
|
|
|
|
## 📋 前置需求
|
|
|
|
- Keycloak 已運行在 `https://auth.ease.taipei`
|
|
- Realm `porscheworld` 已創建
|
|
- 管理員帳號可登入
|
|
|
|
---
|
|
|
|
## 🔧 配置步驟
|
|
|
|
### 1. 創建 HR Portal 客戶端
|
|
|
|
#### 1.1 登入 Keycloak Admin Console
|
|
|
|
1. 訪問: `https://auth.ease.taipei`
|
|
2. 點擊「Administration Console」
|
|
3. 使用管理員帳號登入
|
|
|
|
#### 1.2 選擇 Realm
|
|
|
|
1. 左上角選擇 `porscheworld` realm
|
|
2. 如果沒有,請先創建
|
|
|
|
#### 1.3 創建新 Client
|
|
|
|
1. 左側選單 → **Clients**
|
|
2. 點擊 **Create client** 按鈕
|
|
|
|
**General Settings**:
|
|
```
|
|
Client type: OpenID Connect
|
|
Client ID: hr-portal
|
|
Name: HR Portal
|
|
Description: HR Management Portal
|
|
```
|
|
|
|
點擊 **Next**
|
|
|
|
**Capability config**:
|
|
```
|
|
Client authentication: OFF (公開客戶端)
|
|
Authorization: OFF
|
|
Authentication flow:
|
|
✓ Standard flow (Authorization Code Flow)
|
|
✓ Direct access grants
|
|
```
|
|
|
|
點擊 **Next**
|
|
|
|
**Login settings**:
|
|
```
|
|
Root URL: https://hr.ease.taipei
|
|
Home URL: https://hr.ease.taipei
|
|
Valid redirect URIs:
|
|
https://hr.ease.taipei/*
|
|
http://localhost:3000/* (開發用)
|
|
Valid post logout redirect URIs:
|
|
https://hr.ease.taipei/*
|
|
http://localhost:3000/*
|
|
Web origins:
|
|
https://hr.ease.taipei
|
|
http://localhost:3000
|
|
```
|
|
|
|
點擊 **Save**
|
|
|
|
---
|
|
|
|
### 2. 配置 Client 進階設定
|
|
|
|
進入 Client `hr-portal` 的設定頁面:
|
|
|
|
#### 2.1 Settings 標籤
|
|
|
|
**Access settings**:
|
|
```
|
|
Root URL: https://hr.ease.taipei
|
|
Home URL: https://hr.ease.taipei
|
|
Valid redirect URIs: https://hr.ease.taipei/*
|
|
Valid post logout redirect URIs: https://hr.ease.taipei/*
|
|
Web origins: https://hr.ease.taipei
|
|
Admin URL: (留空)
|
|
```
|
|
|
|
**Login settings**:
|
|
```
|
|
Login theme: keycloak (或自訂主題)
|
|
Consent required: OFF
|
|
Display client on screen: ON
|
|
```
|
|
|
|
**Logout settings**:
|
|
```
|
|
Front channel logout: OFF
|
|
Backchannel logout URL: (留空)
|
|
Backchannel logout session required: ON
|
|
```
|
|
|
|
點擊 **Save**
|
|
|
|
#### 2.2 Advanced 標籤
|
|
|
|
**Advanced settings**:
|
|
```
|
|
Access Token Lifespan: 5 minutes (300 秒)
|
|
Client Session Idle: 30 minutes (1800 秒)
|
|
Client Session Max: 12 hours (43200 秒)
|
|
```
|
|
|
|
**Authentication flow overrides**:
|
|
```
|
|
Browser Flow: browser (預設)
|
|
Direct Grant Flow: direct grant (預設)
|
|
```
|
|
|
|
點擊 **Save**
|
|
|
|
---
|
|
|
|
### 3. 配置 Client Scopes
|
|
|
|
#### 3.1 查看預設 Scopes
|
|
|
|
進入 `hr-portal` Client → **Client scopes** 標籤
|
|
|
|
**Assigned default client scopes** 應包含:
|
|
- `email`
|
|
- `profile`
|
|
- `roles`
|
|
- `web-origins`
|
|
|
|
#### 3.2 建議新增的 Scopes (可選)
|
|
|
|
如需更細緻的權限控制,可創建自訂 scope:
|
|
|
|
1. 左側選單 → **Client scopes**
|
|
2. 點擊 **Create client scope**
|
|
|
|
**範例: hr-admin scope**
|
|
```
|
|
Name: hr-admin
|
|
Description: HR Portal Admin Access
|
|
Protocol: openid-connect
|
|
Display on consent screen: ON
|
|
Include in token scope: ON
|
|
```
|
|
|
|
創建後,回到 `hr-portal` Client → **Client scopes** 標籤 → 將 `hr-admin` 加入 **Assigned optional client scopes**
|
|
|
|
---
|
|
|
|
### 4. 配置用戶屬性映射 (Mappers)
|
|
|
|
進入 `hr-portal` Client → **Client scopes** → **hr-portal-dedicated** → **Mappers**
|
|
|
|
#### 4.1 確認現有 Mappers
|
|
|
|
應該已有:
|
|
- `aud claim` - Audience mapping
|
|
- `Client IP Address` - Client IP
|
|
- `Client ID` - Client identifier
|
|
- `Client Host` - Client hostname
|
|
|
|
#### 4.2 新增自訂 Mapper (用於員工資訊)
|
|
|
|
點擊 **Add mapper** → **By configuration** → **User Attribute**
|
|
|
|
**Mapper 1: Employee ID**
|
|
```
|
|
Name: employee_id
|
|
User Attribute: employee_id
|
|
Token Claim Name: employee_id
|
|
Claim JSON Type: String
|
|
Add to ID token: ON
|
|
Add to access token: ON
|
|
Add to userinfo: ON
|
|
Multivalued: OFF
|
|
Aggregate attribute values: OFF
|
|
```
|
|
|
|
**Mapper 2: Business Unit**
|
|
```
|
|
Name: business_unit
|
|
User Attribute: business_unit_id
|
|
Token Claim Name: business_unit_id
|
|
Claim JSON Type: String
|
|
Add to ID token: ON
|
|
Add to access token: ON
|
|
Add to userinfo: ON
|
|
```
|
|
|
|
點擊 **Save**
|
|
|
|
---
|
|
|
|
### 5. 測試 Client 配置
|
|
|
|
#### 5.1 使用 Keycloak 內建測試工具
|
|
|
|
1. 進入 `hr-portal` Client → **Client scopes** → **Evaluate**
|
|
2. 選擇測試用戶 (例如: testuser)
|
|
3. 點擊 **Generated access token**
|
|
4. 檢查 token payload 是否包含所需 claims
|
|
|
|
#### 5.2 測試 OIDC Discovery
|
|
|
|
在瀏覽器或命令列執行:
|
|
```bash
|
|
curl https://auth.ease.taipei/realms/porscheworld/.well-known/openid-configuration
|
|
```
|
|
|
|
應返回完整的 OIDC 配置,包含:
|
|
- `issuer`
|
|
- `authorization_endpoint`
|
|
- `token_endpoint`
|
|
- `userinfo_endpoint`
|
|
- `end_session_endpoint`
|
|
|
|
---
|
|
|
|
## 🧪 測試登入流程
|
|
|
|
### 測試 1: 前端登入測試
|
|
|
|
1. 訪問 `https://hr.ease.taipei` (或 `http://localhost:3000`)
|
|
2. 系統自動重定向到 Keycloak 登入頁面
|
|
3. 輸入測試用戶帳號密碼
|
|
4. 登入成功後重定向回 HR Portal
|
|
5. 檢查 localStorage 中是否有 `kc-token`
|
|
|
|
### 測試 2: Token 驗證
|
|
|
|
在瀏覽器 Console 執行:
|
|
```javascript
|
|
// 獲取 token
|
|
const keycloak = window.keycloak;
|
|
console.log('Access Token:', keycloak.token);
|
|
console.log('ID Token:', keycloak.idToken);
|
|
console.log('Refresh Token:', keycloak.refreshToken);
|
|
|
|
// 解析 token
|
|
const payload = JSON.parse(atob(keycloak.token.split('.')[1]));
|
|
console.log('Token Payload:', payload);
|
|
```
|
|
|
|
檢查 payload 應包含:
|
|
- `sub`: User ID
|
|
- `email`: 用戶 email
|
|
- `preferred_username`: 用戶名
|
|
- `given_name`, `family_name`: 姓名
|
|
- `employee_id`, `business_unit_id`: 自訂屬性 (如已配置)
|
|
|
|
---
|
|
|
|
## 🔐 安全設定建議
|
|
|
|
### 1. Token 生命週期
|
|
|
|
建議設定:
|
|
```
|
|
Access Token Lifespan: 5 minutes
|
|
SSO Session Idle: 30 minutes
|
|
SSO Session Max: 12 hours
|
|
Refresh Token Max Reuse: 0 (單次使用)
|
|
```
|
|
|
|
### 2. 密碼策略
|
|
|
|
Realm Settings → **Authentication** → **Password Policy**
|
|
|
|
建議啟用:
|
|
- Minimum Length: 8
|
|
- Not Username
|
|
- Uppercase Characters: 1
|
|
- Lowercase Characters: 1
|
|
- Digits: 1
|
|
- Special Characters: 1
|
|
- Not Recently Used: 3
|
|
|
|
### 3. 啟用 MFA (多因素認證)
|
|
|
|
Realm Settings → **Authentication** → **Flows**
|
|
|
|
編輯 **Browser** flow:
|
|
1. 找到 `Browser - Conditional OTP`
|
|
2. 將 **Requirement** 改為 **Required**
|
|
3. 點擊 **Save**
|
|
|
|
用戶首次登入時會要求設定 OTP (Google Authenticator / FreeOTP)
|
|
|
|
---
|
|
|
|
## 👥 測試用戶管理
|
|
|
|
### 創建測試用戶
|
|
|
|
1. 左側選單 → **Users**
|
|
2. 點擊 **Add user**
|
|
|
|
**User details**:
|
|
```
|
|
Username: test.employee
|
|
Email: test.employee@porscheworld.tw
|
|
First name: Test
|
|
Last name: Employee
|
|
Email verified: ON
|
|
Enabled: ON
|
|
```
|
|
|
|
3. 點擊 **Create**
|
|
|
|
**設定密碼**:
|
|
1. 進入用戶 → **Credentials** 標籤
|
|
2. 點擊 **Set password**
|
|
3. 輸入密碼 (例如: Test1234!)
|
|
4. Temporary: OFF
|
|
5. 點擊 **Save**
|
|
|
|
**設定屬性** (如需要):
|
|
1. 進入用戶 → **Attributes** 標籤
|
|
2. 新增屬性:
|
|
- Key: `employee_id`, Value: `EMP001`
|
|
- Key: `business_unit_id`, Value: `1`
|
|
3. 點擊 **Save**
|
|
|
|
---
|
|
|
|
## 🔄 同步到後端 HR 系統
|
|
|
|
### 選項 1: 手動同步
|
|
|
|
當在 Keycloak 創建用戶後,需要在 HR Portal 後端創建對應的員工記錄:
|
|
|
|
```bash
|
|
# 使用 API 創建員工
|
|
curl -X POST https://hr-api.ease.taipei/api/v1/employees/ \
|
|
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"employee_id": "EMP001",
|
|
"username": "test.employee",
|
|
"keycloak_user_id": "KEYCLOAK_USER_UUID",
|
|
"first_name": "Test",
|
|
"last_name": "Employee",
|
|
"chinese_name": "測試員工",
|
|
"email": "test.employee@porscheworld.tw",
|
|
"business_unit_id": 1,
|
|
"position": "Test Engineer",
|
|
"job_level": "Staff",
|
|
"hire_date": "2024-01-01",
|
|
"status": "active"
|
|
}'
|
|
```
|
|
|
|
### 選項 2: 自動同步 (使用 Keycloak Event Listener)
|
|
|
|
在後端實現 Keycloak Event Listener (進階功能):
|
|
|
|
1. 監聽 Keycloak 的 `REGISTER` 和 `UPDATE_PROFILE` 事件
|
|
2. 當用戶註冊或更新資料時,自動在 HR 資料庫創建/更新記錄
|
|
3. 使用 Keycloak Admin API 獲取用戶資訊
|
|
|
|
---
|
|
|
|
## 📝 配置檢查清單
|
|
|
|
部署前確認:
|
|
|
|
- [ ] Client `hr-portal` 已創建
|
|
- [ ] Client Type: OpenID Connect
|
|
- [ ] Client authentication: OFF (公開客戶端)
|
|
- [ ] Standard flow: ✓ Enabled
|
|
- [ ] Valid redirect URIs: `https://hr.ease.taipei/*`
|
|
- [ ] Web origins: `https://hr.ease.taipei`
|
|
- [ ] Access token lifespan: 5 minutes
|
|
- [ ] 測試用戶已創建並可登入
|
|
- [ ] Token 包含必要的 claims
|
|
- [ ] 前端可成功登入並獲取 token
|
|
- [ ] 前端可使用 token 調用後端 API
|
|
|
|
---
|
|
|
|
## 🐛 常見問題
|
|
|
|
### 問題 1: Invalid redirect URI
|
|
|
|
**原因**: 前端重定向 URI 不在 Valid redirect URIs 列表中
|
|
|
|
**解決**:
|
|
1. 檢查前端配置的 `VITE_KEYCLOAK_URL`
|
|
2. 確認 Keycloak Client 的 Valid redirect URIs 包含前端 URL
|
|
|
|
### 問題 2: CORS 錯誤
|
|
|
|
**原因**: Web origins 未正確配置
|
|
|
|
**解決**:
|
|
在 Keycloak Client 設定中,確保 Web origins 包含前端域名:
|
|
```
|
|
https://hr.ease.taipei
|
|
```
|
|
|
|
### 問題 3: Token 過期過快
|
|
|
|
**原因**: Access token lifespan 設定過短
|
|
|
|
**解決**:
|
|
調整 Client → Advanced → Access Token Lifespan
|
|
建議: 5-15 分鐘
|
|
|
|
### 問題 4: 用戶無法登入
|
|
|
|
**檢查**:
|
|
1. 用戶是否已啟用 (Enabled: ON)
|
|
2. Email 是否已驗證 (Email verified: ON)
|
|
3. 密碼是否正確設定
|
|
4. Client 的 Authentication flow 是否啟用
|
|
|
|
---
|
|
|
|
## 📚 參考資料
|
|
|
|
- [Keycloak Documentation](https://www.keycloak.org/documentation)
|
|
- [OIDC Specification](https://openid.net/specs/openid-connect-core-1_0.html)
|
|
- [Keycloak Admin REST API](https://www.keycloak.org/docs-api/latest/rest-api/index.html)
|