Files
zyt/docs/iam-quick-login.md
T

5.4 KiB
Raw Blame History

IAM 统一账号快捷登录

在原账号密码、企业微信入口之外提供可关闭的 OIDC 登录方式。不开启配置时不访问 IAM、不创建事务文件,也不改变原登录行为。

配置

在服务器私密 server/.env 增加:

[IAM]
ENABLED = false
ISSUER = https://login.zhenyangtang.com.cn/auth/realms/iam-hub
CLIENT_ID = zyt
CLIENT_SECRET = CONFIGURE_PRIVATE_CLIENT_SECRET
REDIRECT_URI = https://admin.zhenyangtang.com.cn/adminapi/iam/callback
API_URL = https://login.zhenyangtang.com.cn/iam-api
APPLICATION_ID = zyt
APPLICATION_TOKEN = CONFIGURE_PRIVATE_APPLICATION_TOKEN
PUBLIC_URL = https://admin.zhenyangtang.com.cn

Keycloak 的 zyt client 仅登记上述精确 HTTPS 回调,开启 Authorization Code 与 PKCE S256;不启用密码授权。配置完成后再设 ENABLED=true

账号和权限

  • IAM 必须存在有效员工、zyt 应用授权和经管理员确认的账号绑定。externalAccountId 是已存在的甄养堂管理员 ID,不是账号名称。
  • 不按用户名或邮箱自动合并,不自动生成 root、不改已有角色/部门/数据范围。
  • 本次新员工按用户指定的 医助角色 ID 2 开通,沿用原新增账号流程(含 IM 账号导入),再显式绑定到 IAM。后续人员也需由管理员明确开通与绑定;没有绑定时快捷登录拒绝,而非猜测账号。
  • 登录签发原 AdminTokenService token,继续遵守本地 disable、软删除、单点/多点登录、首次改密以及强制绑定企微规则。
  • 新账号默认 is_paw=0。完成首次改密、绑定企微之前不会跳过原有业务门禁。
  • 此版本在每次新快捷登录时检查 IAM 状态,不批量撤销既有业务会话,也不宣称已实现 IAM 停权事件实时踢下线。

协议与部署

端点为 /adminapi/iam/configstartcallbackexchange。浏览器保存独立 HttpOnly/Secure/SameSite=Lax 事务 Cookiestate、nonce、PKCE 与一次性兑换码绑定浏览器。URL 中不传业务 token,兑换只能 POST 且校验来源。事务文件保存在应用 runtime 的 iam-login 私有子目录,0600,受文件锁保护。当前单服务器 PHP-FPM 多进程共享该目录;扩展到多机前须换成共享原子存储。

JWT 使用锁定的 firebase/php-jwt,校验 RS256、签名、issuer、audience/azp、有效期和 nonce;出站 TLS 校验保持开启,不跟随重定向。 前端通过服务器开关显示按钮,兑换后复用现有 token 缓存与页面分流,IAM 失败时原账号及企微入口仍可使用。 发布静态文件保留旧哈希资源,最后更新入口,避免已打开页面加载旧资源时报错。

验证与回退

php server/tests/IamOidcClientTest.php
php server/tests/IamLoginTransactionTest.php
php server/tests/AdminDesktopAuthContractTest.php
php server/tests/AdminDesktopSessionBehaviorTest.php
php server/tests/AdminMultiRoleRegressionTest.php
php server/tests/DataScopeMultiRoleTest.php
NODE_PATH=admin/node_modules node server/tests/IamLoginUiContractTest.mjs

回退时先关闭 IAM 开关,再恢复本次代码和静态入口备份,不清空会话、用户、角色、业务数据库或全部缓存。新创建的员工与医助账号保留,由管理员决定是否停用。

2026-09-10: authorized first-login provisioning

Deploy server/database/migrations/20260910_iam_local_identity.sql before the adapter update (adjust the zyt_ prefix only when configured differently). This additive InnoDB ledger is keyed by SHA-256 of the exact issuer/application/subject tuple and uniquely associates a new local account. Keep the ledger when rolling code back; never drop it or delete real accounts as a code rollback.

The authenticated provisioning-context API is the authority for active employee + application grant. Only an explicit shouldCreateLocalAccount=true with an empty external ID creates an account. The local transaction inserts the immutable ledger, a random local account name and password, and role 2 / 医助 only; a missing, deleted, renamed or disabled default role fails closed. Neither name, phone nor email merges accounts. Existing explicit bindings use existing permissions without modification. Password setup (is_paw=0), non-root status, and original WeCom binding gates remain intact. The normal physician IM import runs best-effort after central binding confirmation, matching AdminLogic::add semantics; provider errors are logged by the existing helper.

After local commit, the adapter uses application authentication and stable idempotency metadata to POST bindings, accepts HTTP 201, then reads provisioning-context again and requires the exact created ID before issuing a login ticket. Remote failures keep the local ledger for retry, with no business token. Parallel workers cannot create another ledger/account for that immutable identity. A transient DB deadlock can fail one login attempt; retry resumes the winning account.

The IAM server must reject changing an existing binding to a different external ID on this endpoint (rather than an upsert overwrite). Ship that conflict fix before this adapter. Admin-driven deliberate rebindings require a separate verified workflow.

Focused local tests:

php server/tests/IamOidcClientTest.php
php server/tests/IamLoginTransactionTest.php
php server/tests/IamProvisioningTest.php
# Dedicated disposable local MariaDB only; drops four fixture tables in iam_fixture.
IAM_TEST_MYSQL=1 php server/tests/IamProvisioningTest.php