Chuyển tới nội dung chính

platform/keycloak — Keycloak

realm/moc-realm.json được Keycloak nạp ở lần khởi động đầu (bỏ qua nếu realm đã có — sửa tệp thì make reset, hoặc sửa qua trang quản trị). Giá trị ${...} lấy từ biến môi trường của container. Chủ: #21.

PhầnNội dung
Client app-officeCông khai, Authorization Code + PKCE (S256), chuyển hướng về MOC_PUBLIC_URL
Client app-ca-nhanKhông gian cá nhân P.A.R.A (apps/ca-nhan, docs/them-ung-dung.md): như app-office — công khai, Authorization Code + PKCE (S256), cùng 5 mapper claim Hasura — nhưng chỉ chuyển hướng về MOC_PUBLIC_URL/ca-nhan/*. Cùng phiên SSO: đã đăng nhập Văn phòng điện tử thì không hỏi lại mật khẩu
Client moc-cliLuồng mật khẩu cho make seed, make smoke, các màn demo, CI — không cần trên server thật thì tắt bằng MOC_CLI_CLIENT_ENABLED=false
Client svc-workflowTài khoản dịch vụ, quyền view-users, đọc danh bạ
Đăng nhập trình duyệt (luồng moc-browser)Mật khẩu rồi mã TOTP. Bắt buộc với clerk, manager, director, tenant_admin: chưa cài thì Keycloak bắt cài bằng mã QR ngay lần đầu. staff chỉ bị hỏi mã nếu đã tự cài
Luồng mật khẩu của moc-cli (moc-cli-direct-grant)Mật khẩu rồi mã TOTP (tham số otp) theo đúng quy tắc của moc-browser: không là đường vòng qua xác thực hai bước. Vai trò bắt buộc mà chưa cài TOTP thì không lấy được token (luồng này không cài được). Kịch bản trong scripts/ tự tính mã từ MOC_DEMO_TOTP_SECRET (moc_env.user_login), không gửi lại mã đã dùng. Đây cũng là luồng mật khẩu mặc định của cả realm (directGrantFlow): client có sẵn admin-cli của realm cũng phải kèm mã, kể cả khi đã tắt moc-cli
MapperClaim https://hasura.io/jwt/claims: x-hasura-user-id, -tenant-id, -department-id, -default-role, -allowed-roles
User profiletenant_id, department_id, moc_role (5 vai trò) — chỉ quản trị viên xem và sửa; phone_number (số nhận SMS, #365) — người dùng xem được, chỉ quản trị viên sửa
Giao diện đăng nhập (loginTheme: signet)themes/signet/login/, trang chung của Văn phòng điện tử, P.A.R.A, Bảng tin. Kế thừa theme keycloak.v2 có sẵn trong image Keycloak 26 — giữ nguyên template FreeMarker, PatternFly 5, bản dịch tiếng Việt — chỉ thêm theme.properties (chỉ chế độ sáng, như Văn phòng điện tử), resources/css/signet.css (nhận diện Liền mạch #116: nền, thẻ, ô nhập, nút olive, cỡ trên điện thoại), phông Be Vietnam Pro (không nằm trong repo, #305: dịch vụ chạy một lần keycloak-fonts dựng từ fonts/ lấy phông từ gói npm @fontsource/be-vietnam-pro như Văn phòng điện tử, chép vào volume keycloak_fonts gắn đè lên thư mục giữ chỗ resources/fonts/be-vietnam-pro/, kèm OFL.txt; keycloak chờ nó xong), logo resources/img/signet.svg (trùng từng byte apps/office/public/favicon.svg, cũng là biểu tượng tab), biểu tượng đầu trang resources/img/signet-symbol.svg và tiêu đề thẻ ngắn "Đăng nhập" ở messages/. Tên ở đầu trang là displayName của realm. Compose gắn thư mục chỉ đọc vào /opt/keycloak/themes/signet, Keycloak vẫn chạy image chính thức; CSP giữ mặc định của Keycloak, CSS không tải gì từ ngoài máy chủ
Organizations (organizationsEnabled)Mỗi đơn vị một Organization: alias suy từ mã đơn vị (CNTT → cntt, ĐT → dt), thuộc tính tenant_id = id trong core.tenants; mọi tài khoản có tenant_id đó là thành viên (unmanaged) và không thuộc Organization nào khác. Đây là lớp quản lý thành viên và quản trị: gom tài khoản theo đơn vị, chỗ gắn tên miền email hay IdP riêng của đơn vị về sau. Token vẫn lấy đơn vị từ thuộc tính tenant_id qua mapper ở trên, không đổi: claim organization của Keycloak (scope tùy chọn organization, client của Signet không xin) là danh sách alias, nằm ngoài namespace https://hasura.io/jwt/claims nên Hasura không đọc, và tài khoản thuộc nhiều Organization thì không suy ra được đúng một đơn vị. Tạo bằng python3 scripts/moc.py tenant create, kiểm bằng tenant check; make seed tạo cho hai đơn vị mẫu. Thành viên Organization phải có email (Keycloak từ chối sửa hồ sơ thiếu email)

Stack dựng trước khi có một ứng dụng (vd app-ca-nhan) không có client của ứng dụng đó, vì realm không nạp lại: python3 scripts/moc.py realm clients (make seed cũng gọi) thêm các client ứng dụng trên trình duyệt có trong moc-realm.json mà realm đang chạy chưa có, thay ${...} bằng giá trị trong .env. Không sửa client đã có, không dựng lại moc-cli, svc-workflow (client dòng lệnh, dịch vụ) nếu quản trị đã gỡ. --dry-run chỉ in việc sẽ làm.

Stack dựng trước khi user profile có phone_number (#365) không có ô số điện thoại, vì realm không nạp lại: python3 scripts/moc.py realm profile (--dry-run chỉ in việc sẽ làm; nang_cap.sh tự chạy) thêm các thuộc tính của moc-realm.json mà realm đang chạy chưa có, không sửa thuộc tính đã có.

Stack dựng từ bản trước F3.12 chưa bật Organizations trong realm (realm không nạp lại): tenant create hoặc make seed tự bật (Admin REST, gửi lại nguyên bản realm như trang quản trị), không cần make reset.

Stack dựng từ bản mà moc-cli-direct-grant chưa hỏi TOTP vẫn giữ luồng cũ, vì realm không nạp lại: make reset (xóa toàn bộ dữ liệu), hoặc trong trang quản trị Keycloak mở Authentication → moc-cli-direct-grant, thêm hai subflow Conditional moc-cli-otp-bat-buoc, moc-cli-otp-tu-chon sau bước mật khẩu, giống hệt trong moc-realm.json. Tương tự, stack dựng trước khi realm đặt directGrantFlow: mở Authentication, ở luồng moc-cli-direct-grant chọn Bind flow → Direct grant flow (không thì admin-cli lấy được token chỉ bằng mật khẩu).

Stack dựng trước khi có theme đăng nhập vẫn hiện giao diện mặc định của Keycloak, vì realm không nạp lại: cài bản mới rồi docker compose up -d --wait (container keycloak dựng lại với thư mục theme), sau đó python3 scripts/moc.py realm theme (--dry-run chỉ in việc sẽ làm) đặt loginTheme của moc-realm.json cho realm đang chạy — chỉ khi realm còn dùng theme mặc định, không đổi theme quản trị đã chọn; Keycloak chưa thấy thư mục theme thì báo và không đặt. Làm tay: trang quản trị → Realm settings → Themes → Login theme → signet.

Sửa tệp trong themes/ thì dựng lại container: docker compose up -d --force-recreate --wait keycloak. Keycloak đọc CSS mới ngay nhưng trả kèm Cache-Control: max-age=2592000 (30 ngày) dưới cùng đường dẫn /resources/<mã>/…; mã giữ nguyên qua docker restart, đổi khi container dựng lại (đo trên Keycloak thử 26.7.5), nên chỉ dựng lại thì trình duyệt mới tải bản mới. Nâng cấp (docker compose down, up) dựng lại sẵn. Nâng Keycloak thì mở lại trang đăng nhập: theme cha đổi tên lớp hay biến CSS thì phần phủ mất tác dụng (trang vẫn chạy, về giao diện gốc).

Tài khoản mẫu tạo bằng make seed (seed/users.csv), không nằm trong realm. Các tài khoản văn thư, lãnh đạo, tổ trưởng, trưởng bộ môn, quản trị được cài sẵn TOTP bằng khóa chung MOC_DEMO_TOTP_SECRET; make totp in mã hiện tại và khóa để thêm vào ứng dụng xác thực.