Chiến lược xác thực — GIW
Trạng thái: POC · Ngày: 21/09/2026 Căn cứ: TASK ORDER — APPLY AUTH ARCHITECTURE GUARDRAILS, 21/09/2026
DIRECT GRANT / PASSWORD GRANT = BẢN TRIỂN KHAI CHỈ DÀNH CHO POC
Không phải kiến trúc production. Không phải luồng Galaxy ID production. Không phải khuyến nghị xác thực nhân viên cho production.
RFC 9700 (OAuth 2.0 Security Best Current Practice) nói password grant MUST NOT be used, và OAuth 2.1 đã loại bỏ nó. Không viện dẫn ngoại lệ tiêu chuẩn nào ở đây. Direct Grant chỉ được dùng như một giải pháp tạm thời có giới hạn cho POC, nhằm kiểm chứng trải nghiệm captive portal. Kiến trúc xác thực cho production vẫn TBC và nên ưu tiên OIDC/federation chạy trên trình duyệt khi điều kiện vận hành cho phép.
1. Hai chế độ
Cả hai đều đã cấu hình trong realm. Chỉ Mode A được đấu vào portal.
MODE A — POC LOCAL
Wi-Fi Portal / BFF
→ Direct Grant (POST /token, grant_type=password)
→ Keycloak
→ HR Verification API (chỉ ở đường nhân viên)
Trạng thái: ĐÃ TRIỂN KHAI / CHỈ CHO POC
| Client | wifi-portal-api, wifi-portal-employee-api — cả hai đều confidential |
| Khách hàng | grant_type=password |
| Đăng ký | client_credentials → Admin API → password |
| Nhân viên | grant_type=password + employeeId + citizenId → flow tuỳ biến employee-direct-grant → Keycloak gọi HR |
| Trình duyệt thấy gì | Chỉ portal. Một cookie mờ sid. |
| Vì sao tồn tại | Để kiểm chứng trọn hành trình captive portal mà không cần redirect. |
Nó không làm được gì: SSO, MFA, step-up, federation, cô lập credential.
MODE B — PRODUCTION CANDIDATE
Wi-Fi Portal
→ Authorization Code + PKCE
→ Keycloak
→ Enterprise IdP / Federation / MFA
→ Keycloak
→ Wi-Fi Portal
Trạng thái: ĐỀ XUẤT / CHƯA TRIỂN KHAI
| Client | wifi-portal, wifi-portal-employee — public, bắt buộc PKCE S256 |
| Flow | employee-browser → employee-verify-form → giw-employee-verify |
| Có trong realm không? | Có. Đã cấu hình, import được, và từng chạy ở các bản POC trước. |
| Đã đấu vào portal chưa? | Chưa. Cố ý để không dùng. |
| Vì sao giữ lại | Đây là đường mà một Enterprise IdP sẽ liên kết vào. ADR-003 khuyến nghị dùng liên kết IdP doanh nghiệp thay cho CCCD; không có đường này thì khuyến nghị đó không thể triển khai mà không dựng lại kiến trúc. |
Không được xoá: flow employee-browser · sub-flow employee-verify-form · EmployeeVerifyAuthenticator · provider giw-employee-verify · theme login galaxy và employee-verify.ftl · client wifi-portal và wifi-portal-employee · việc gắn scope giw-identity và ghi đè flow binding của chúng.
bootstrap đối chiếu cả hai bộ ở mỗi lần khởi động, nên Mode B vẫn chạy được chứ không mục dần.
2. So sánh
| Năng lực | Direct Grant (Mode A, POC) | Browser/OIDC (Mode B, ứng viên production) |
|---|---|---|
| Đơn giản cho captive portal | Cao | TBC — phải kiểm chứng với hành vi CNA thật |
| SSO | Không | Có |
| MFA / step-up | Hạn chế / Không | Có |
| Liên kết (Enterprise IdP, mạng xã hội) | Không | Có |
| Cô lập credential | Thấp hơn — mật khẩu đi qua portal | Tốt hơn — portal không bao giờ thấy nó |
| Tương thích CNA | Tốt hơn trong POC này, trên trình duyệt máy tính | Phải kiểm chứng trên thiết bị thật, mạng captive thật |
| Vị thế theo tiêu chuẩn | RFC 9700: MUST NOT | Được khuyến nghị |
| Phạm vi bảo mật / audit | Rộng hơn — portal nằm trong phạm vi xử lý credential | Hẹp hơn |
| Liền mạch thương hiệu | Không có bước chuyển giao nhìn thấy được | Có bước chuyển giao, nhưng theme được |
| Khuyến nghị cho production | Không | Ứng viên |
Hai dòng cần thận trọng, vì POC chưa đủ cơ sở để khẳng định:
- Tương thích CNA. "Tốt hơn trong POC" nghĩa là tốt hơn trên trình duyệt desktop với một stack chạy local. Chưa ai thử chế độ nào bên trong một captive network assistant iOS hay Android thật trên tàu bay. Chừng nào chưa có phép thử đó, đây là giả thuyết, không phải kết luận.
- Đơn giản cho captive portal. Ô của Mode B để
TBCcũng vì lý do đó.
3. Lộ trình chuyển Mode A → Mode B
Không thứ nào dưới đây đã được xây. Ghi lại để việc chuyển là đổi đường đi, không phải viết lại.
| Bước | Cái gì thay đổi | Cái gì không |
|---|---|---|
| 1 | Portal thêm /login/oidc → redirect sang client wifi-portal |
Entitlement, Session, HR, giao diện portal của mọi màn còn lại |
| 2 | Portal thêm /callback → đổi code kèm PKCE verifier |
Đường establish(): token → entitlement → session → cookie sid |
| 3 | Hành trình nhân viên trỏ sang wifi-portal-employee |
EmployeeVerification, hợp đồng với HR, cách xử lý CCCD |
| 4 | Keycloak thêm Enterprise IdP làm identity provider | Hợp đồng claim: user_type, employee_verified, employee_ref, company |
| 5 | Flow employee-browser thêm một IdP redirector đứng trước giw-employee-verify |
Mọi thứ nằm sau token |
| 6 | Tắt Direct Grant trên cả hai API client; gỡ EmployeeDirectGrantAuthenticator |
— |
Lý do việc này rẻ: hàm establish() của portal nhận một token rồi làm mọi thứ phía sau. Cái gì sinh ra token đó thì thay thế được.
4. Ranh giới bảo mật
Ai biết cái gì. Đây là tính chất mà thiết kế BFF mang lại, và nó tồn tại qua cả khi chuyển sang Mode B.
Trình duyệt
| Biết gì | Giữ gì | Không bao giờ |
|---|---|---|
| Host của portal, và không gì khác | Một cookie mờ sid — HttpOnly, SameSite=Strict, Max-Age=3600 |
Access token · refresh token · ID token |
| Endpoint của Keycloak | ||
| Endpoint của HR | ||
| Endpoint của Entitlement | ||
| Endpoint của Session |
sid là một UUID không mang ý nghĩa gì ngoài bản đồ trong bộ nhớ của portal. HttpOnly giữ nó khỏi tầm với của script; SameSite=Strict nghĩa là không request cross-site nào mang nó theo.
Ngoại lệ, công cụ trợ giúp demo của POC: DEBUG_EXPOSE_TOKEN=1 trả về access token gốc trên GET /api/v1/auth/session, để mồi cho một API client trong lúc demo. Mặc định tắt và phải giữ tắt khi ra khỏi máy tính cá nhân.
Portal / BFF
| Làm gì | Không làm gì |
|---|---|
| Giữ token chỉ phía server, trong một bản đồ TTL 1 giờ, dọn mỗi 5 phút | Không bao giờ lưu mật khẩu — không kho, không file, không cơ sở dữ liệu |
| Chuyển tiếp credential trong luồng Direct Grant của POC | Không bao giờ lưu CCCD |
| Kiểm hình dạng dữ liệu trước khi gọi bất cứ đâu (định dạng email, độ dài mật khẩu, sự có mặt của trường) | Không bao giờ ghi log credential — không lệnh log nào nhận mật khẩu hay CCCD |
| Điều phối: Galaxy ID → Entitlement → Session | Không bao giờ quyết định việc xác thực |
| Không bao giờ quyết định entitlement | |
| Không bao giờ chạm vào thiết bị mạng |
Về câu "xoá giá trị nhạy cảm ngay sau request" — thực tế bảo đảm được gì.
Chuỗi trong JavaScript là bất biến. Một mật khẩu hay CCCD đọc từ body của request không thể bị ghi đè bằng 0; không có memset nào. Thay vào đó code làm thế này:
- Giá trị được đọc vào một biến cục bộ và truyền thẳng vào request đi ra.
- Nó không bao giờ được gán vào một field, một object phiên, một cache hay một closure sống lâu hơn request.
- Nó ra khỏi scope khi handler trả về, và để bộ thu gom rác xử lý.
- Không lệnh log nào trong portal nhận nó làm tham số.
Điểm 2–4 cưỡng chế được và đang được cưỡng chế. Điểm 1 không phải là xoá sạch, và tài liệu này sẽ không tuyên bố như vậy. Một ngôn ngữ kiểm soát được bộ nhớ sẽ làm tốt hơn; đó là một hạn chế thật của việc dùng Node BFF để xử lý credential, và là thêm một lý do nữa để Mode A chỉ dành cho POC.
Điều tương tự áp dụng bên trong Keycloak: EmployeeVerification giữ CCCD trong một biến cục bộ, đưa cho HrVerificationClient, và không bao giờ ghi nó vào field, user attribute, auth-session note, chi tiết event hay dòng log. Khi lỗi, HrVerificationClient chỉ log e.getClass().getSimpleName(), không bao giờ e.getMessage(), vì một số tầng transport vọng nguyên body của request vào thông báo exception.
Bản đồ thẩm quyền
| Hệ thống | Có thẩm quyền về | Không bao giờ |
|---|---|---|
| Keycloak (Galaxy ID) | Xác thực | Mở mạng · quyết định entitlement |
| HR Verification API | Tình trạng lao động — có phải nhân viên đang làm việc và đủ điều kiện không | Trả về hồ sơ · lưu bất cứ thứ gì · là hệ gốc của danh tính |
| Entitlement Service | Cấp quyền — subject này được truy cập những gì | Thực thi · tạo phiên |
| Session Service | Thực thi — trạng thái truy cập (CREATED/GRANTED/REVOKED/EXPIRED) |
Quyết định. POST /sessions/{id}/grant trả 422 nếu thiếu entitlementId |
| Wi-Fi Portal / BFF | Điều phối và trình bày | Quyết định bất cứ điều gì |
5. Hồ sơ quyết định
| Câu hỏi | Ref | Trạng thái |
|---|---|---|
| Direct Grant có chấp nhận được ngoài phạm vi POC không? | T-GRANT | Không. Chỉ POC. |
| Kiến trúc xác thực cho production là gì? | — | TBC / CHƯA PHÊ DUYỆT |
| Nếu bắt buộc MFA thì cái gì mang step-up? | T-MFA | TBC. Mode A không làm được. |
| Liên kết IdP doanh nghiệp cho nhân viên? | ADR-003 | PROPOSED. Cần Mode B. |
| CCCD có chấp nhận được ở production không? | T-CCCD, ADR-003, D3 | TBC. Cần Security + Legal/DPO + HR phê duyệt. |
| Mode A có sống sót với CNA thật trên tàu bay thật không? | — | Chưa kiểm chứng. |
| Mode B có sống sót với CNA thật không? | — | Chưa kiểm chứng. |
6. Trạng thái chính thức
| Hạng mục | Trạng thái |
|---|---|
| POC UX / API | ACCEPTABLE FOR LOCAL DEMO |
| POC Identity | PASS |
| Direct Grant | POC ONLY |
| OIDC Browser / Federation | PRODUCTION CANDIDATE / TBC |
| Kiến trúc xác thực production | NOT APPROVED / TBC |
| CCCD | CHỈ LÀ ĐẦU VÀO XÁC MINH CHO POC. Dùng ở production cần Security + Legal/DPO + HR phê duyệt. |