Bảng tổng hợp API — GIW POC Identity Platform

Trạng thái: POC · Ngày: 21/09/2026

Mọi API và tích hợp trong nền tảng, gói trên một trang.

Cột PII: none · personal · sensitive (dữ liệu cá nhân nhạy cảm theo Luật 91/2025/QH15).


1. Portal API — thứ duy nhất trình duyệt gọi

Bên cung cấp: Wi-Fi Portal. Bên tiêu thụ: chính frontend của portal. Xác thực: cookie mờ sid (HttpOnly, SameSite=Strict, 1 giờ).

API PII Idempotent Trạng thái POC / Production
POST /api/v1/auth/login personal (email) + secret (mật khẩu, thoáng qua) không — mỗi lần gọi là một lượt xác thực mới ĐÃ LÀM CHỈ POC. Direct Grant, T-GRANT
POST /api/v1/auth/register personal (email, tên) + secret (mật khẩu) không — 409 nếu trùng ĐÃ LÀM POC. Chưa xác minh email (không có mail server)
POST /api/v1/auth/employee sensitive (CCCD đi vào, không bao giờ đi ra) có — verify là chỉ đọc ĐÃ LÀM POC. T-CCCD
GET /api/v1/auth/session không, ngoài subject có — chỉ đọc ĐÃ LÀM POC
POST /api/v1/auth/refresh không không — xoay token ĐÃ LÀM POC
POST /api/v1/auth/logout không có — luôn trả 200 ĐÃ LÀM POC
GET /api/v1/meta không có ĐÃ LÀM POC

Trình duyệt chỉ biết một hostname (portal) và giữ một cookie mờ. Nó không bao giờ thấy token, URL của Keycloak, hay địa chỉ của bất kỳ service phía sau nào.

2. Các API service (Galaxy tự xây)

API Bên cung cấp Bên tiêu thụ Xác thực PII Idempotent Trạng thái POC / Production
POST /api/v1/employees/verify HR (giả lập) Keycloak authenticator X-API-Key sensitive (CCCD vào, không bao giờ ra) có — chỉ đọc ĐÃ LÀM POC. Hợp đồng production TBC (T-HR-SOR)
POST /api/v1/entitlements/evaluate Entitlement Service Wi-Fi Portal Bearer JWT (RS256, verify iss+aud+exp) không — chỉ subject id có — Idempotency-Key ĐÃ LÀM POC. Policy phụ thuộc ADR-011
GET /api/v1/entitlements/{id} Entitlement Service Wi-Fi Portal, Ops Bearer JWT không có — chỉ đọc ĐÃ LÀM POC
POST /api/v1/sessions Session Service Wi-Fi Portal X-API-Key không — deviceRef là mờ có — Idempotency-Key ĐÃ LÀM POC. Adapter thật theo ADR-007
POST /api/v1/sessions/{id}/grant Session Service Wi-Fi Portal X-API-Key không có — khai báo trạng thái mong muốn ĐÃ LÀM POC
POST /api/v1/sessions/{id}/revoke Session Service Wi-Fi Portal X-API-Key không có — khai báo ĐÃ LÀM POC
GET /api/v1/sessions/{id} Session Service Wi-Fi Portal, Ops X-API-Key không có — chỉ đọc ĐÃ LÀM POC
GET /health cả bốn service Ops, Docker không không có ĐÃ LÀM POC
POST /admin/fault mock-hr-api bộ test không không có ĐÃ LÀM CHỈ POC — không được tồn tại ở production
POST /admin/reset-limits mock-hr-api bộ test không không có ĐÃ LÀM CHỈ POC — không được tồn tại ở production
POST /api/v1/decisions/{model}/evaluate DMN Service Entitlement Service không không có — hàm thuần ĐÃ LÀM POC. Luật nằm trong .dmn trên đĩa
GET /api/v1/decisions DMN Service Ops không không có ĐÃ LÀM POC
POST /api/v1/members/lookup Loyalty (giả lập) Entitlement Service X-API-Key personal (email vào, không bao giờ ra) có — chỉ đọc ĐÃ LÀM POC. Làm giàu, không bao giờ là cổng chặn
POST /api/v1/decisions/reload DMN Service người phụ trách nghiệp vụ / CI không không có ĐÃ LÀM ⚠️ không xác thực — xem DMN-DECISION-SPEC.md §7

2b. Module giw-admin — tích hợp công ty thành viên

Bên cung cấp: một module tuỳ biến của Keycloak (RealmResourceProvider, id giw-admin), do chính Keycloak phục vụ tại /realms/{realm}/giw-admin. Phạm vi MVP POC: một module giữ cấu hình kết nối, import theo lô, webhook nhận vào và trình biên tập bảng quyết định. ADR-012 ghi lại những gì một MVP sau sẽ tách ra và cái giá phải trả hôm nay.

Xác thực trên /api/*: bearer token của realm mang quyền realm-management/manage-users. Không phải realm-admin — module này cấp phát người dùng, không quản trị realm. Token admin của realm master không được chấp nhận: module xác thực theo đúng realm mà nó đang phục vụ.

API PII Idempotent Trạng thái POC / Production
GET /giw-admin/ không có ĐÃ LÀM POC. Vỏ GUI, công khai; mọi lệnh /api bên dưới đều xác thực
GET /giw-admin/api/companies không có — chỉ đọc ĐÃ LÀM POC. Trả hasSecret, không bao giờ trả secret
PUT /giw-admin/api/companies/{code} không có — thay thế toàn bộ ĐÃ LÀM POC. Lưu bằng realm attribute — không phiên bản, không review (ADR-012)
DELETE /giw-admin/api/companies/{code} không có ĐÃ LÀM POC. Chỉ xoá cấu hình; user đã import vẫn giữ nguyên
POST /giw-admin/api/companies/{code}/test không có — thăm dò chỉ đọc ĐÃ LÀM POC. GET {origin-của-endpoint}/health, báo UP / HTTP_n / UNREACHABLE
POST /giw-admin/api/import?mode=preview sensitive (CCCD vào, không ra, không lưu) có — không ghi gì ĐÃ LÀM POC. So từng dòng với realm ở trạng thái hiện tại
POST /giw-admin/api/import?mode=commit sensitive (CCCD vào, băm ngay khi nhận) không — tạo và liên kết user ĐÃ LÀM POC. T-USER-SYNC — sao chép hay liên kết vẫn còn mở
POST /giw-admin/webhook/{company} personal (mã tham chiếu nhân viên) có — chống trùng theo X-GIW-Event-Id ĐÃ LÀM POC. Chống trùng trong bộ nhớ; T-WEBHOOK-DURABILITY
GET /giw-admin/api/events personal (payload của sự kiện) có — chỉ đọc ĐÃ LÀM POC. Ring buffer 200 bản ghi, mất khi restart
GET /giw-admin/api/dmn/models không có ĐÃ LÀM POC. Chuyển tiếp sang dmn-service
GET /giw-admin/api/dmn/models/{name} không có ĐÃ LÀM POC
PUT /giw-admin/api/dmn/models/{name} không có — thay thế toàn bộ ĐÃ LÀM POC. Không có quy trình duyệt — cùng khoảng trống với /reload, xem DMN-DECISION-SPEC §7
POST /giw-admin/api/dmn/evaluate/{name} không có — chỉ đọc ĐÃ LÀM POC. Để thử một thay đổi luật trước khi lưu

⚠️ Cấu hình kết nối chưa nối vào luồng đăng nhập. Authenticator của nhân viên đọc hrEndpoint từ authenticatorConfig của flow, không đọc từ giw.company.{code}. Sửa endpoint ở đây không đổi được địa chỉ Keycloak gọi lúc đăng nhập. Muốn nối lại cần chốt T-HR-ROUTING; theo dõi ở DEFERRED-TASKS D5.

Những gì module cố ý không làm

Không làm Vì sao
Lưu CCCD Các dòng được khớp bằng HMAC-SHA256(cccd, salt của realm). Số gốc bị bỏ sau khi băm. Cái giá: không tra cứu được người theo CCCD, và xoay salt thì mất toàn bộ liên kết
Đặt employee_verified khi import Import là nạp dữ liệu, không phải xác minh. Chỉ một lượt hỏi HR trực tiếp lúc đăng nhập mới được đặt
Áp dụng sự kiện không thuộc danh tính MEMBER_TIER_CHANGED và những loại tương tự chỉ được ghi nhận, không áp dụng. Ghi hạng hội viên vào Keycloak là biến nó thành bản sao cũ của hệ loyalty
Ghi đè khi trùng CCCD Cùng CCCD dưới một employee_ref khác sẽ trả CONFLICT. Có thể là tái tuyển dụng, có thể là gõ nhầm, có thể là một dòng sắp chiếm tài khoản của người khác — ghi đè im lặng là kết cục duy nhất giấu mất đó là trường hợp nào
Trả về secret của webhook Chỉ ghi được. API cho biết đã đặt hay chưa, không bao giờ cho biết giá trị

Hợp đồng webhook

POST /realms/{realm}/giw-admin/webhook/{company}

Header Bắt buộc Nghĩa là gì
X-GIW-Timestamp có Unix seconds. Từ chối nếu lệch quá ±5 phút
X-GIW-Signature có base64(HMAC-SHA256(secret, "{timestamp}.{rawBody}")). Ký trên đúng byte đã gửi — serialize lại trước khi ký là kiểm tra bộ serialize của chính mình, không phải kiểm tra bên gửi
X-GIW-Event-Id nên có Chống trùng. Thiếu nó thì một lần retry của nhà cung cấp sẽ được áp dụng hai lần
Loại sự kiện Tác động
EMPLOYEE_TERMINATED Vô hiệu hoá tài khoản, employee_verified=false
EMPLOYEE_SUSPENDED Vô hiệu hoá tài khoản
EMPLOYEE_REINSTATED Bật lại tài khoản. Không khôi phục trạng thái đã xác minh
EMPLOYEE_COMPANY_CHANGED Cập nhật attribute company
mọi loại khác RECORDED — ghi nhận, không áp dụng
Kết quả HTTP Nghĩa là gì
APPLIED 200 Đã thay đổi danh tính
RECORDED 200 Đã nhận, nhưng không phải sự kiện danh tính
DUPLICATE 200 Event id đã xử lý rồi
NO_MATCH 200 Không user nào mang employeeRef đó — ghi nhận để xem lại
CONFLICT 409 Nhiều hơn một user khớp
REJECTED 401 / 400 Sai chữ ký, timestamp quá hạn, chưa cấu hình secret, hoặc thiếu employeeRef

3. OAuth / OIDC chuẩn (không do Galaxy xây — không đặc tả lại)

Toàn bộ là server-to-server. Không có redirect trình duyệt nào trong nền tảng này.

Endpoint Grant / phương thức Bên tiêu thụ Client PII Trạng thái
POST /protocol/openid-connect/token password Wi-Fi Portal wifi-portal-api (confidential) secret trên đường truyền CHUẨN — RFC 6749 §4.3
POST /protocol/openid-connect/token password + employeeId + citizenId Wi-Fi Portal wifi-portal-employee-api (confidential) sensitive trên đường truyền Endpoint CHUẨN, flow direct-grant tuỳ biến
POST /protocol/openid-connect/token refresh_token Wi-Fi Portal cả hai không CHUẨN — RFC 6749 §6
POST /protocol/openid-connect/token client_credentials Wi-Fi Portal service account của wifi-portal-api không CHUẨN — RFC 6749 §4.4
POST /admin/realms/{realm}/users Bearer (service account) Wi-Fi Portal giới hạn ở manage-users personal CHUẨN KEYCLOAK ADMIN
POST /protocol/openid-connect/logout back-channel, refresh_token Wi-Fi Portal cả hai không CHUẨN — kiểu RFC 7009
GET /protocol/openid-connect/userinfo Bearer Wi-Fi Portal cả hai personal CHUẨN OIDC
GET /protocol/openid-connect/certs không (khoá công khai) Entitlement Service — không CHUẨN OIDC
Keycloak Admin REST admin password grant bootstrap (chạy một lần) admin-cli personal (user test) CHUẨN KEYCLOAK

Các client

Client Loại Grant Ghi đè flow Dùng cho
wifi-portal-api confidential password, client_credentials direct grant dựng sẵn Đăng nhập khách hàng, đăng ký
wifi-portal-employee-api confidential password direct_grant → employee-direct-grant Xác minh nhân viên
wifi-portal public (standard flow, portal không dùng) — Giữ lại chỉ để tham chiếu
wifi-portal-employee public (standard flow, portal không dùng) browser → employee-browser Giữ lại chỉ để tham chiếu

Confidential chứ không phải public: một public client bật Direct Access Grants cho phép bất kỳ ai trên mạng phát lại grant mà không cần xác thực client nào. Portal chạy phía server nên nó giữ được secret.

4. Các lệnh gọi nội bộ không có hợp đồng HTTP

Từ Tới Cơ chế Ghi chú
Custom authenticator Kho user của Keycloak SPI UserProvider Ghi user_type, employee_verified, employee_ref, company. Không bao giờ ghi CCCD.
Keycloak event log Keycloak events Chỉ ghi giw_correlation_id và giw_employee_ref.

5. Callback / tích hợp nội bộ

Một chiều vào duy nhất: POST /giw-admin/webhook/{company} ở §2b. Không có hàng đợi tin nhắn, không có callback đi ra — mọi chặng khác đều là request đồng bộ do portal khởi xướng.

Đợt review kiến trúc (ADR-009) yêu cầu đường thành công ở production phải được xác nhận bằng callback backend, không bao giờ chỉ bằng redirect trình duyệt. Yêu cầu đó chưa chạm tới đây — chưa có thanh toán hay chiến dịch bên ngoài nào trong phạm vi. Webhook đã đáp ứng các trường dưới đây, trừ đệm khi retry: nó được xử lý trực tiếp trong Keycloak, nên một sự kiện đến đúng lúc restart sẽ mất chứ không vào hàng đợi (T-WEBHOOK-DURABILITY). Mọi callback thêm về sau phải mang theo:

Trường Mục đích
correlationId Nối callback về đúng hành trình đã khởi xướng
subjectId Callback này nói về ai
sessionId Nó ảnh hưởng phiên truy cập nào
Idempotency-Key / X-Event-Id Chống trùng — nhà cung cấp nào cũng retry
Chữ ký HMAC trên raw body + timestamp Tính xác thực; cửa sổ ±5 phút
Chính sách retry Phía nhà cung cấp, bên nhận trả 200 trước khi xử lý bất đồng bộ

6. Timeout và retry

Lệnh gọi Timeout Số lần Thử lại có an toàn?
Authenticator → HR verify 3000 ms 2 Có — chỉ đọc, không đổi trạng thái
Portal → Galaxy ID, grant khách hàng 5000 ms 1 Không — xác thực lại là việc của người dùng
Portal → Loyalty 2000 ms 1 Có — chỉ đọc. Ngắn có chủ đích: nó nằm trên đường đăng nhập và là tuỳ chọn. Mọi thất bại đều suy giảm thành "không có hạng", không bao giờ thành lỗi
Portal → Galaxy ID, grant nhân viên 12000 ms 1 Có — chỉ đọc. Phải lớn hơn hrTimeout × hrAttempts, nếu không portal hết giờ trước và quy sai một sự cố của HR thành lỗi của Galaxy ID
Portal → Entitlement 5000 ms 1 Có — có idempotency key
Portal → Session create 5000 ms 1 Có — có idempotency key
Portal → Session grant 5000 ms 1 Có — khai báo trạng thái mong muốn
Entitlement → JWKS 4000 ms 1 + 1 lần refresh cưỡng bức khi gặp kid lạ Có

Mọi giá trị đều là GIẢ ĐỊNH CỦA POC. Xem API-OVERVIEW.md §7.

7. Giới hạn tần suất

Bề mặt Giới hạn Cơ chế
HR verify — theo từng hồ sơ nhân viên 5 lần thất bại / 60 s Khoá theo employeeId, không theo địa chỉ bên gọi: Keycloak là bên gọi duy nhất, nên giới hạn theo IP sẽ bóp cả một khoang hành khách qua chung một rổ. Lần xác minh thành công không bị tính — chúng không phải là đoán mò
HR verify — trần toàn cục 600 req / 60 s chỉ để chặn vòng lặp mất kiểm soát
Form nhân viên 5 lần submit mỗi phiên xác thực cấu hình authenticator
Đăng nhập Keycloak 5 lần thất bại rồi giãn tới 900 s brute-force protection của realm
Bùng phát quick-login 2 lần thất bại trong 200 ms → giữ 60 s quickLoginCheckMilliSeconds của realm. Trước đây là 1000 ms, khiến một hành khách chỉ bấm Đăng nhập hai lần liên tiếp đã bị khoá.
Portal API không có Khoảng trống — portal giờ là bề mặt hứng brute-force
Entitlement, Session không có Khoảng trống — xem API-REVIEW-CHECKLIST.md

GIW POC Identity Platform · bản demo local · không phải production · sinh từ repository