feat: HR Portal - Complete Multi-Tenant System with Redis Session Storage

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>
This commit is contained in:
2026-02-23 20:12:43 +08:00
commit 360533393f
386 changed files with 70353 additions and 0 deletions

162
KEYCLOAK-CLIENT-SETUP.md Normal file
View File

@@ -0,0 +1,162 @@
# Keycloak Client 配置 - HR Portal Frontend
## 需要在 Keycloak 建立的 Client
### Client 基本資訊
- **Client ID**: `hr-portal-web`
- **Client Type**: OpenID Connect
- **Access Type**: Public (因為是 SPA 應用)
### Client 配置
#### 1. Settings
```
Client ID: hr-portal-web
Name: HR Portal Web Application
Description: HR Portal Frontend SPA
Client Protocol: openid-connect
Access Type: public
Standard Flow Enabled: ON
Direct Access Grants Enabled: OFF
Implicit Flow Enabled: OFF
```
#### 2. Valid Redirect URIs
開發環境:
```
http://localhost:3000/*
http://10.1.0.245:3000/*
```
生產環境 (待部署):
```
https://hr.ease.taipei/*
```
**建議**: 如果想要更嚴格的安全控制,可以只允許根路徑:
```
http://localhost:3000/
http://10.1.0.245:3000/
https://hr.ease.taipei/
```
**說明**:
- `/*` - 允許任何路徑作為回調 URI (方便但較不安全)
- `/` - 只允許根路徑作為回調 URI (更安全但需確保 Keycloak 回調到根路徑)
- 對於開發環境,使用 `/*` 比較方便
- 對於生產環境,建議使用更具體的路徑
#### 3. Valid Post Logout Redirect URIs
```
http://localhost:3000/
http://10.1.0.245:3000/
https://hr.ease.taipei/
```
**注意**: Post Logout URIs 不需要 `*`,因為登出後只會導向根路徑
#### 4. Web Origins
```
http://localhost:3000
http://10.1.0.245:3000
https://hr.ease.taipei
```
#### 5. Advanced Settings
```
PKCE Code Challenge Method: S256
```
### Realm Roles (如果需要)
為 HR Portal 建立以下角色:
- `hr-admin` - HR 管理員 (完整權限)
- `hr-manager` - HR 經理 (查看與編輯)
- `hr-viewer` - HR 檢視者 (僅查看)
### Client Scopes
確保以下 scopes 已啟用:
- `openid` - 必須
- `profile` - 使用者基本資料
- `email` - 使用者電子郵件
- `roles` - 角色資訊
## 建立步驟
### 1. 登入 Keycloak Admin Console
```
URL: https://auth.ease.taipei
Realm: porscheworld
```
### 2. 建立 Client
1. 進入 **Clients** 頁面
2. 點擊 **Create****Add Client**
3. 填寫 Client ID: `hr-portal-web`
4. 選擇 Client Protocol: `openid-connect`
5. 點擊 **Save**
### 3. 配置 Client Settings
1. **Access Type**: 改為 `public`
2. **Standard Flow Enabled**: 勾選 ON
3. **Direct Access Grants Enabled**: 取消勾選
4. **Implicit Flow Enabled**: 取消勾選
5. **Valid Redirect URIs**: 加入上述 URIs
6. **Web Origins**: 加入上述 Origins
7. 點擊 **Save**
### 4. 配置 Advanced Settings
1. 進入 **Advanced Settings** 標籤
2. **PKCE Code Challenge Method**: 選擇 `S256`
3. 點擊 **Save**
### 5. 驗證 Client Scopes
1. 進入 **Client Scopes** 標籤
2. 確認 `openid`, `profile`, `email` 都在 **Assigned Default Client Scopes**
3. 如果沒有,從 **Available Client Scopes** 加入
## 測試配置
### 1. 測試 OIDC Discovery
```bash
curl https://auth.ease.taipei/realms/porscheworld/.well-known/openid-configuration | jq
```
### 2. 測試前端登入流程
1. 啟動開發伺服器: `npm run dev`
2. 瀏覽器開啟: http://localhost:3000
3. 應該會被導向登入頁面
4. 點擊「使用 SSO 登入」
5. 應該會跳轉到 Keycloak 登入頁面
6. 登入成功後應該回到 HR Portal 首頁
## 環境變數配置
前端 `.env` 檔案:
```env
VITE_API_BASE_URL=https://hr-api.ease.taipei
VITE_KEYCLOAK_URL=https://auth.ease.taipei
VITE_KEYCLOAK_REALM=porscheworld
VITE_KEYCLOAK_CLIENT_ID=hr-portal-web
```
## 常見問題
### CORS 錯誤
- 確認 **Web Origins** 已正確設定
- 檢查 Keycloak Realm Settings 的 CORS 設定
### 重新導向錯誤
- 確認 **Valid Redirect URIs** 包含當前使用的 URL
- 確認 URL 結尾有 `/*` 萬用字元
### Token 過期
- 檢查 Keycloak 的 Token Lifespan 設定
- 確認前端有正確實作 Token 刷新機制
## 相關文件
- Keycloak Documentation: https://www.keycloak.org/docs/latest/
- Keycloak JS Adapter: https://www.keycloak.org/docs/latest/securing_apps/#_javascript_adapter
- PKCE Flow: https://oauth.net/2/pkce/