@h-ai/iam 0.1.0-alpha.73 → 0.1.0-alpha.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -892,17 +892,19 @@ Standard Kit/Serv expose POST /auth/captcha (Kit same-origin /api prefix) and pr
892
892
 
893
893
  ## Optional password and login protection
894
894
 
895
- `password.minLength/maxLength` and individual `require*` flags control strength. `minCharacterClasses` accepts any N of uppercase, lowercase, digits and special characters; 0 adds no count requirement. CLI/admin examples use 8 characters and any 3 classes. All server creation/change/reset paths share validation.
895
+ `password.minLength/maxLength` and individual `require*` flags control strength. `minCharacterClasses` accepts any N of uppercase, lowercase, digits and special characters; 0 adds no count requirement. CLI/admin examples default to 8–128 characters with no extra class count; set `minCharacterClasses: 3` to require any three classes. All server creation/change/reset paths share validation.
896
896
 
897
897
  `expirationDays: 0` disables expiry; positive values are days. `preventSamePassword` is independent. Expiry/initial-change policies reject the current password. `requireInitialChange` applies to administrator-created/reset credentials, rather than self-chosen registration passwords. Initial/expired credentials cannot authorize sessions or refresh existing ones. `iam.user.renewPassword({ identifier, password, newPassword })` verifies the old password and requires fresh login; Kit/Serv and AuthPanel provide the completion flow.
898
898
 
899
- `security.lockoutEnabled` defaults to true. `failureWindow` defaults to 86400 seconds from the first failure, `maxLoginAttempts` is the threshold, and `lockoutDuration` is seconds locked. Examples use 1200 seconds. Disabling ignores historical locks. Shared database updates atomically enforce counts/thresholds.
899
+ `security.lockoutEnabled` defaults to true in the framework Schema; simplified CLI/admin examples explicitly disable it until enabled by the project. `failureWindow` defaults to 86400 seconds from the first failure, `maxLoginAttempts` is the threshold, and `lockoutDuration` is seconds locked. Examples use 1200 seconds. Disabling ignores historical locks. Shared database updates atomically enforce counts/thresholds.
900
900
 
901
901
  `initialCredentials.enabled` enables `iam.user.inviteUser()`: private random initial secrets are never returned or sent; only short-lived single-use setup invitations are delivered. This reuses passwordReset mode, expiry, attempt limits and sensitive delivery. Missing delivery/disabled reset prevents creation. Unconfirmed delivery returns failure while retaining the account/invitation; verify delivery before requesting another reset notification.
902
902
 
903
903
  `loginAccess.enabled` enables restrictions. `allowIps/denyIps` accept IPv4/IPv6/CIDR with deny precedence; empty lists leave that dimension unrestricted. `windows` use `days` (Monday1–Sunday7), `start/end` (HH:mm, inclusive start/exclusive end, overnight supported), and IANA `timezone`. Login methods take trusted `AuthRequestContext` as their second argument: Kit uses platform getClientAddress(), Serv uses context.ip resolved through trusted proxies. Never copy arbitrary forwarding headers or credential JSON addresses. Address restrictions fail closed without a trusted address and cover automatic registration login/API Key login.
904
904
 
905
- `reauthentication.enabled/operations` select sensitive operations; `ttlSeconds` bounds proof lifetime. `reauthenticate(token, password, operation)` issues a single-use proof; authorized handlers call `checkSensitiveOperation(token, operation, ticket)` after permission/resource checks. Proofs bind subject/session/operation and grant no extra permissions. Serv IAM writes/admin guards read `x-hai-reauthentication`; Kit business handlers use `kit.guard.requireSensitive(iam, event, operation)` with project-defined IDs. Keep proof tickets briefly in memory. Public configuration exposes switches/operation IDs only.
905
+ `reauthentication.enabled/operations` select sensitive operations; `ttlSeconds` bounds proof lifetime. `reauthenticate(token, password, operation)` issues a single-use proof; authorized handlers call `checkSensitiveOperation(token, operation, ticket)` after permission/resource checks. Proofs bind subject/session/operation and grant no extra permissions. Serv IAM writes/admin guards read `x-hai-reauthentication`; Kit business handlers use `kit.guard.requireSensitive(iam, event, operation)` with project-defined IDs. Keep proof tickets briefly in memory. Public configuration exposes switches/operation IDs only. In the standard admin/fullstack UI, an already signed-in user who selects a sensitive action confirms their current account password in a dialog. Cancel does not submit, an incorrect password stops the action, and success continues the original action without creating another login session.
906
+
907
+ RBAC permissions accept `sensitiveOperations: string[]`, persisted as nullable `sensitive_operations`. Trusted management code can call `iam.authz.setPermissionSensitiveOperations(permissionId, operations)` and must check `success`. An empty array removes only that permission's additions. With `reauthentication.enabled`, rules from all permission records are combined with file `operations` and enforced live. IDs must match actual business guards; they grant no permissions. Async `iam.getPublicConfig()` returns the combined public policy and fails on database read errors; synchronous `iam.publicConfig` retains the file snapshot only. Disabling RBAC ignores database rules. Migration adds a nullable JSON column while preserving authorization relationships.
906
908
 
907
909
  ## Account type and lifecycle
908
910
 
@@ -924,6 +926,10 @@ After RelDB initialization, `migrateIam({ dryRun: true })` previews changes with
924
926
 
925
927
  When audit is initialized and capture.enabled is on, IAM records important account, authentication, password, authorization, API Key and session events, including failed/denied outcomes and safe reason codes. Initialize audit before IAM and close in reverse order. Passwords, one-time proofs and tokens are never copied into events. Only successful login establishes an actor; trusted service/CLI callers supply actor/correlation through audit.withContext. Events inside an external authorization transaction commit or roll back with that transaction; record failed attempts after rollback at the caller boundary. Audit failures emit diagnostics without converting an applied business result into an unsafe retry.
926
928
 
929
+ `session.terminalLimits: { web: 2, app: 1 }` limits sessions per terminal type; `{}` disables this policy. Unlisted types are rejected when enabled, and older sessions count as `web`. `terminalLimitMode` defaults to `reject`; `replace` evicts only the oldest sessions of the same type. Global `maxSessions` and `singleDevice` still apply in the same atomic user CAS. Server login context `terminalType` and standard client `auth.terminalType` select a group; this is not hardware identification or authorization.
930
+
931
+ For in-place recovery, use UI `createSessionRecovery` / `SessionRecoveryDialog` with SDK `recoverSession`. Normal password/OTP/LDAP login policies still apply; `expectedUserId` restricts session issuance to the original principal. Only an explicitly authentication-rejected request may be retried once. Ambiguous network failures are not replayed. In-memory drafts lost through document reload/closure cannot be restored.
932
+
927
933
  ## Page exit session policy
928
934
 
929
935
  `session.endOnPageHide` defaults to `false`. Hosts call `actions.bindPageExit({ enabled: config.session.endOnPageHide })` and invoke its returned cleanup on unmount; standard admin/H5/fullstack templates already bind it. A non-BFCache `pagehide` (including reload and full document navigation) makes a best effort POST to `/auth/session/close`, revoking the held access and related refresh through the existing CSRF/encrypted transport with `keepalive` and no automatic refresh. This response does not clear cookies/local credentials, avoiding a late old response overwriting a new login. Explicit logout still revokes and clears current credentials normally. Browsers may omit page exit requests; configure server `idleTimeout`/`absoluteTimeout` as appropriate. Immediate revocation on browser close is not guaranteed.
package/README.ja.md CHANGED
@@ -888,17 +888,19 @@ Kit/Serv の標準入口は POST /auth/captcha(Kit は同一生成元の /api
888
888
 
889
889
  ## 任意のパスワード・ログイン保護
890
890
 
891
- `password.minLength/maxLength` と各 `require*` は強度を指定します。`minCharacterClasses` は大文字、小文字、数字、特殊文字の任意 N 種類を要求し、0は追加制約なしです。CLI/admin 例は8文字・任意3種類です。作成・変更・リセットで同じ検証を使います。
891
+ `password.minLength/maxLength` と各 `require*` は強度を指定します。`minCharacterClasses` は大文字、小文字、数字、特殊文字の任意 N 種類を要求し、0は追加制約なしです。CLI/admin 例の既定は8–128文字で、追加の文字種数制限はありません。任意3種類を要求する場合は `minCharacterClasses: 3` を明示します。作成・変更・リセットで同じ検証を使います。
892
892
 
893
893
  `expirationDays: 0` は定期変更を無効にし、正数は日数です。`preventSamePassword` は単独でも有効化でき、有効期限制約・初回変更時は旧パスワードを拒否します。`requireInitialChange` は管理者作成・リセットの認証情報に適用され、自分で登録時に設定したパスワードは再変更不要です。初期・期限切れパスワードでは通常セッションを取得・更新できません。`iam.user.renewPassword({ identifier, password, newPassword })` で旧パスワードを確認し、変更後に再ログインします。Kit/Serv と AuthPanel はこの完了経路を提供します。
894
894
 
895
- `security.lockoutEnabled` は既定で有効です。`failureWindow` は最初の失敗からの固定期間(既定86400秒)、`maxLoginAttempts` は閾値、`lockoutDuration` はロック秒数です。例は1200秒です。無効時は既存ロックも無視します。共有DBで計数・閾値を原子的に処理します。
895
+ `security.lockoutEnabled` はフレームワークSchemaでは既定で有効ですが、簡易CLI/admin例では明示的に無効にし、必要に応じて有効化します。`failureWindow` は最初の失敗からの固定期間(既定86400秒)、`maxLoginAttempts` は閾値、`lockoutDuration` はロック秒数です。例は1200秒です。無効時は既存ロックも無視します。共有DBで計数・閾値を原子的に処理します。
896
896
 
897
897
  `initialCredentials.enabled` は `iam.user.inviteUser()` を有効にします。利用者ごとに非公開のランダム秘密を作り、短期・単回の設定招待だけ送ります。初期秘密は返却・送信しません。passwordReset の通知・期限・試行回数を再利用し、受信者は再設定画面でパスワードを選びます。通知未設定・リセット無効なら作成しません。配送未確認時は失敗とし、作成済みアカウント・招待を保持します。配送を確認後に再設定通知を要求してください。
898
898
 
899
899
  `loginAccess.enabled` は制限を有効にします。`allowIps/denyIps` は IPv4/IPv6/CIDR を受け付け、拒否を優先します。空リストは対象を制限しません。`windows` は `days`(月曜1〜日曜7)、`start/end`(HH:mm、開始を含み終了を含まず、日跨ぎ対応)、IANA `timezone` を指定します。ログインの第2引数は信頼できる `AuthRequestContext` です。Kit はプラットフォーム getClientAddress()、Serv は信頼プロキシで解決した context.ip を使います。任意の転送ヘッダー・入力 JSON の IP を信用してはいけません。アドレス制限時に信頼できるアドレスがなければ拒否し、自動ログイン・API Key にも適用します。
900
900
 
901
- `reauthentication.enabled/operations` で対象操作、`ttlSeconds` で証明期限を選びます。`reauthenticate(token, password, operation)` は単回証明を発行し、入口は権限・資源検証後に `checkSensitiveOperation(token, operation, ticket)` を呼びます。主体・現在セッション・操作に結びつけ、権限を増やしません。Serv IAM 更新操作と admin 守衛は `x-hai-reauthentication`、Kit は固有 ID と `kit.guard.requireSensitive(iam, event, operation)` を使います。証明は短時間メモリに保持し、公開設定は開閉・操作リストのみです。
901
+ `reauthentication.enabled/operations` で対象操作、`ttlSeconds` で証明期限を選びます。`reauthenticate(token, password, operation)` は単回証明を発行し、入口は権限・資源検証後に `checkSensitiveOperation(token, operation, ticket)` を呼びます。主体・現在セッション・操作に結びつけ、権限を増やしません。Serv IAM 更新操作と admin 守衛は `x-hai-reauthentication`、Kit は固有 ID と `kit.guard.requireSensitive(iam, event, operation)` を使います。証明は短時間メモリに保持し、公開設定は開閉・操作リストのみです。admin/fullstack標準UIでは、ログイン済み利用者が機密操作を選ぶと、現在のアカウントのパスワード確認ダイアログを表示します。取消時は送信せず、誤りなら操作を止め、成功時だけ元の操作を続けます。別のログインセッションは作成しません。
902
+
903
+ RBAC 権限は `sensitiveOperations: string[]` を持ち、可空 JSON 列 `sensitive_operations` に保存します。管理側は `iam.authz.setPermissionSensitiveOperations(permissionId, operations)` の `success` を確認します。空配列はその権限の追加規則だけを削除します。`reauthentication.enabled` が有効なら全権限の規則とファイルの `operations` を和集合にして即時検証します。ID は実際の業務ガードと一致させ、既存の認可を省略しません。非同期 `iam.getPublicConfig()` は統合した公開方針を返し、DB 読取障害時は失敗します。同期 `iam.publicConfig` はファイルのスナップショットのみです。RBAC 無効時は DB 規則を無視し、移行は既存の認可関係を保持します。
902
904
 
903
905
  ## アカウント種別とライフサイクル
904
906
 
@@ -920,6 +922,10 @@ RelDB 初期化後の `migrateIam({ dryRun: true })` は読み取り専用です
920
922
 
921
923
  audit が初期化済みで capture.enabled が有効なら、IAM はアカウント・認証・パスワード・権限・API Key・セッションの重要イベントと失敗/拒否の安全な理由コードを記録します。audit を IAM より先に初期化し、逆順に終了してください。パスワード・単回証明・トークンは記録しません。ログイン成功後に主体を確定し、信頼できるサービス/CLI は audit.withContext で操作者と関連文脈を設定します。外部権限トランザクション内のイベントは一緒にコミット/ロールバックされ、失敗試行はロールバック後に呼出境界で記録します。監査失敗は診断を出し、反映済み業務を危険な再試行に変えません。
922
924
 
925
+ `session.terminalLimits: { web: 2, app: 1 }` は端末種別ごとの上限で、既定 `{}` は無効です。有効時は未記載の種別を拒否し、旧セッションは `web` とします。`terminalLimitMode` の既定 `reject` は新規ログインを拒否し、`replace` は同種の古いセッションだけを置換します。全体の `maxSessions` と `singleDevice` も同じ CAS 内で適用します。ログインコンテキスト `terminalType` / 標準クライアント `auth.terminalType` は分類用であり、機器の真正性や認可を保証しません。
926
+
927
+ 画面内の復帰は UI の `createSessionRecovery` / `SessionRecoveryDialog` と SDK `recoverSession` を組み合わせます。通常のパスワード・OTP・LDAP 方針を適用し、`expectedUserId` で元の本人に限定してからセッションを発行します。認証拒否が明確な要求だけ1回再送でき、結果不明のネットワーク障害は再送しません。画面再読込や終了で失われたメモリの入力は復元できません。
928
+
923
929
  ## ページ離脱時のセッション方針
924
930
 
925
931
  `session.endOnPageHide` の既定値は `false` です。ホストは `actions.bindPageExit({ enabled: config.session.endOnPageHide })` を呼び、破棄時に戻り値の解除関数を実行します。標準 admin/H5/fullstack テンプレートは接続済みです。BFCache 以外の `pagehide`(再読み込み、文書全体の移動を含む)で POST `/auth/session/close` を最善努力で送信し、保持する access と関連 refresh を失効します。既存の CSRF/暗号化転送を使用し、`keepalive` を付け、自動更新しません。古い応答が新しいログインを上書きしないよう Cookie/ローカル資格情報を消去しません。明示的ログアウトは通常どおり失効と消去を行います。ブラウザーが離脱要求を送らない場合もあるため、業務に応じてサーバーの `idleTimeout`/`absoluteTimeout` を併用してください。閉じた瞬間の失効は保証できません。
package/README.md CHANGED
@@ -901,17 +901,19 @@ if (!result.success) {
901
901
 
902
902
  ## 可选密码与登录保护
903
903
 
904
- `password.minLength/maxLength` 控制长度,各 `require*` 指定必需类别;`minCharacterClasses` 支持大小写、数字、特殊字符中任意 N 类,0不额外限制。CLI/admin 示例默认8位、任意3类。所有服务端创建、改密和重置入口共用校验。
904
+ `password.minLength/maxLength` 控制长度,各 `require*` 指定必需类别;`minCharacterClasses` 支持大小写、数字、特殊字符中任意 N 类,0不额外限制。CLI/admin 示例默认8–128位、不额外要求字符类别;需要任意3类时显式设置 `minCharacterClasses: 3`。所有服务端创建、改密和重置入口共用校验。
905
905
 
906
906
  `expirationDays: 0` 关闭定期改密,正数为天数;`preventSamePassword` 可独立开启。启用期限或首次强改时,新旧密码必须不同。`requireInitialChange` 作用于管理端创建/重置的密码,自助注册设密不重复强改。首次/过期密码不能授权普通会话或刷新旧会话;用户通过 `iam.user.renewPassword({ identifier, password, newPassword })` 验证原密码并更换后重新登录,Kit/Serv 与 AuthPanel 提供完成流程。
907
907
 
908
- `security.lockoutEnabled` 默认开启;`failureWindow` 默认86400秒,从首次失败起按固定窗口计数,`maxLoginAttempts` 为阈值,`lockoutDuration` 为锁定秒数。示例锁定1200秒。关闭时忽略历史锁;计数与阈值在共享数据库中原子处理。
908
+ `security.lockoutEnabled` 的框架 Schema 默认开启,CLI/admin 简化示例显式关闭,按需启用;`failureWindow` 默认86400秒,从首次失败起按固定窗口计数,`maxLoginAttempts` 为阈值,`lockoutDuration` 为锁定秒数。示例锁定1200秒。关闭时忽略历史锁;计数与阈值在共享数据库中原子处理。
909
909
 
910
910
  `initialCredentials.enabled` 开启管理端 `iam.user.inviteUser()`:内部生成独立随机秘密,仅发送短期单次设密邀请,不返回/发送初始秘密。沿用 `passwordReset` 的模式、期限、尝试次数和敏感通知通道,接收人通过重置页面设密。关闭重置或缺少通道时不创建账号。投递未知返回失败并保留已创建账号及邀请,核对后再请求重置通知,不自动回滚或重发。
911
911
 
912
912
  `loginAccess.enabled` 开启登录限制;`allowIps/denyIps` 支持 IPv4/IPv6/CIDR,拒绝优先,空列表不限制对应维度。`windows` 用 `days`(周一1至周日7)、`start/end`(HH:mm,含起点不含终点,支持跨午夜)和 IANA `timezone`。登录方法第二参为可信 `AuthRequestContext`:Kit 使用平台 `getClientAddress()`,Serv 使用可信代理策略解析的 `context.ip`。不得复制任意转发头或凭据 JSON 的 IP;地址限制启用但无可信地址时拒绝。注册后自动登录、API Key 登录同样执行。
913
913
 
914
- `reauthentication.enabled/operations` 选择敏感操作,`ttlSeconds` 为证明期限。`iam.auth.reauthenticate(token, password, operation)` 验证原密码并签发单次票据;操作入口完成权限/资源检查后用 `checkSensitiveOperation(token, operation, ticket)` 消费。证明绑定主体、当前会话和操作,不提升权限。Serv 标准 IAM 写操作与 admin 守卫读取 `x-hai-reauthentication`;Kit 业务入口用 `kit.guard.requireSensitive(iam, event, operation)`,业务操作 ID 由项目定义。票据只短暂保存在内存,公开配置仅暴露开关/操作清单。
914
+ `reauthentication.enabled/operations` 选择敏感操作,`ttlSeconds` 为证明期限。`iam.auth.reauthenticate(token, password, operation)` 验证原密码并签发单次票据;操作入口完成权限/资源检查后用 `checkSensitiveOperation(token, operation, ticket)` 消费。证明绑定主体、当前会话和操作,不提升权限。Serv 标准 IAM 写操作与 admin 守卫读取 `x-hai-reauthentication`;Kit 业务入口用 `kit.guard.requireSensitive(iam, event, operation)`,业务操作 ID 由项目定义。票据只短暂保存在内存,公开配置仅暴露开关/操作清单。admin/fullstack 标准界面在已登录用户点击敏感动作时弹出当前账号密码验证窗口;取消不提交、密码错误不继续,正确后才执行原操作,不创建另一个登录会话。
915
+
916
+ RBAC 权限支持 `sensitiveOperations: string[]`(持久字段 `sensitive_operations`)。可信管理端可调用 `iam.authz.setPermissionSensitiveOperations(permissionId, operations)`;检查结果的 `success`,空列表仅移除该权限的附加规则。开启 `reauthentication.enabled` 后,所有权限记录的规则与文件 `operations` 取并集,服务端实时执行。规则 ID 必须对应实际业务守卫,不能替代原权限检查。异步 `iam.getPublicConfig()` 返回合并后的公开策略,DB 读取失败返回失败;同步 `iam.publicConfig` 只保留文件快照。RBAC 关闭时忽略数据库规则。权限表迁移只添加可空 JSON 字段,保留既有角色/权限关联。
915
917
 
916
918
  ## 账号类型和生命周期
917
919
 
@@ -933,6 +935,10 @@ if (!result.success) {
933
935
 
934
936
  审计模块已初始化且 capture.enabled 开启时,IAM 自动记录关键账号、鉴别、密码、授权、API Key 和会话事件,包含失败/拒绝与安全原因码。先初始化 audit 再初始化 IAM,关闭时反序。不会记录密码、一次性票据或令牌;成功登录才确认主体,可信服务/CLI 调用应以 audit.withContext 提供操作者和关联信息。外部授权事务的事件随该事务提交或回滚,回滚失败尝试由调用入口在事务结束后记录。审计写失败输出诊断,不把已生效业务改成可盲目重试的失败。
935
937
 
938
+ `session.terminalLimits: { web: 2, app: 1 }` 设置分终端会话数,默认 `{}` 关闭。非空时未列出的 `desktop/api` 等类型不能新登录;旧会话按 `web` 处理。`terminalLimitMode: reject` 为默认,`replace` 仅替换同类最早会话。全局 `maxSessions` 和 `singleDevice` 继续约束所有终端,配额同一 CAS 原子提交。服务端登录上下文的 `terminalType` 和标准客户端 `auth.terminalType` 选择分组;类型不是硬件身份或授权凭据。
939
+
940
+ Web 原地恢复使用 UI 的 `createSessionRecovery` / `SessionRecoveryDialog` 与 SDK 的 `recoverSession`,复用正常登录策略。`expectedUserId` 在密码、OTP、LDAP 登录签发会话前限制原主体;验证失败不签发恢复会话。仅重发明确未认证拒绝的一笔请求,网络结果不明时不重发。页面刷新/关闭后的内存草稿不在恢复范围内。
941
+
936
942
  ## 页面离开会话策略
937
943
 
938
944
  `session.endOnPageHide` 默认 `false`。启用后宿主调用 `actions.bindPageExit({ enabled: config.session.endOnPageHide })` 并在卸载时调用返回的清理函数;标准 admin/H5/fullstack 模板已经接入。非 BFCache 的 `pagehide`(也包括刷新、整页导航)通过 POST `/auth/session/close` 尽力撤销当前 access 及关联 refresh,沿用 CSRF/传输加密,使用 `keepalive` 且不自动刷新会话。该响应不清 Cookie/本地凭据,避免旧响应覆盖下一次登录。显式退出仍使用正常 logout,可靠撤销并清理当前凭据。浏览器可能不发送页面离开请求,因此必须按业务配合服务端 `idleTimeout`/`absoluteTimeout`;不能据此保证关闭即撤销。
package/dist/browser.d.ts CHANGED
@@ -387,6 +387,16 @@ declare const SessionConfigSchema: z.ZodObject<{
387
387
  reject: "reject";
388
388
  }>>;
389
389
  maxSessions: z.ZodDefault<z.ZodNumber>;
390
+ terminalLimits: z.ZodDefault<z.ZodRecord<z.ZodEnum<{
391
+ web: "web";
392
+ app: "app";
393
+ desktop: "desktop";
394
+ api: "api";
395
+ }> & z.core.$partial, z.ZodNumber>>;
396
+ terminalLimitMode: z.ZodDefault<z.ZodEnum<{
397
+ replace: "replace";
398
+ reject: "reject";
399
+ }>>;
390
400
  idleTimeout: z.ZodDefault<z.ZodNumber>;
391
401
  absoluteTimeout: z.ZodDefault<z.ZodNumber>;
392
402
  refreshTokenMaxAge: z.ZodDefault<z.ZodNumber>;
@@ -571,6 +581,16 @@ declare const IamConfigSchema: z.ZodObject<{
571
581
  reject: "reject";
572
582
  }>>;
573
583
  maxSessions: z.ZodDefault<z.ZodNumber>;
584
+ terminalLimits: z.ZodDefault<z.ZodRecord<z.ZodEnum<{
585
+ web: "web";
586
+ app: "app";
587
+ desktop: "desktop";
588
+ api: "api";
589
+ }> & z.core.$partial, z.ZodNumber>>;
590
+ terminalLimitMode: z.ZodDefault<z.ZodEnum<{
591
+ replace: "replace";
592
+ reject: "reject";
593
+ }>>;
574
594
  idleTimeout: z.ZodDefault<z.ZodNumber>;
575
595
  absoluteTimeout: z.ZodDefault<z.ZodNumber>;
576
596
  refreshTokenMaxAge: z.ZodDefault<z.ZodNumber>;
@@ -818,6 +838,8 @@ interface PermissionQueryOptions extends PaginationOptionsInput {
818
838
  * ```
819
839
  */
820
840
  interface Permission {
841
+ /** 此资源权限要求二次认证的操作ID;与文件清单取并集,空或null表示不追加。 */
842
+ sensitiveOperations?: string[] | null;
821
843
  /** 权限 ID */
822
844
  id: string;
823
845
  /** 权限代码(如 users:read, posts:write) */
@@ -878,6 +900,10 @@ interface Role {
878
900
  * ```
879
901
  */
880
902
  interface AuthzOperations {
903
+ /** 实时读取RBAC中配置的敏感操作;RBAC关闭时为空,读取失败须拒绝受保护操作。 */
904
+ getSensitiveOperations: () => Promise<HaiResult<string[]>>;
905
+ /** 设置资源权限的敏感操作清单;空数组清除数据库追加规则,不清除文件中的规则。 */
906
+ setPermissionSensitiveOperations: (permissionId: string, operations: string[]) => Promise<HaiResult<void>>;
881
907
  /** 查询当前角色与权限代码,一次读取角色并批量聚合权限,不依赖会话缓存。 */
882
908
  getUserAuthorization: (userId: string) => Promise<HaiResult<Pick<Session, 'roles' | 'permissions'>>>;
883
909
  /**
@@ -1581,6 +1607,8 @@ interface AuthResult {
1581
1607
  * ```
1582
1608
  */
1583
1609
  interface Session {
1610
+ /** 登录时固定的终端分组;旧记录未设置时归为 web,刷新不能切换分组。 */
1611
+ terminalType?: 'web' | 'app' | 'desktop' | 'api';
1584
1612
  /** 会话内容版本;显式更新产生新版本,内部滑动续期保留版本。 */
1585
1613
  version: string;
1586
1614
  /** 认证快照绑定的持久凭据代;改密后的旧会话即使缓存清理失败也拒绝。 */
@@ -1641,6 +1669,8 @@ type SessionData = Record<string, unknown> & {
1641
1669
  * ```
1642
1670
  */
1643
1671
  interface CreateSessionOptions {
1672
+ /** 可信宿主提供的终端分组;会话签发后不可通过 update/refresh 改变。 */
1673
+ terminalType?: 'web' | 'app' | 'desktop' | 'api';
1644
1674
  /** 策略已验证的凭据代;创建前须与当前数据库核对,省略时仅供可信服务端按当前用户签发。 */
1645
1675
  expectedCredentialVersion?: string;
1646
1676
  /** 服务端已验证的 API Key 绑定;不得来自未验证客户端输入。 */
@@ -1975,6 +2005,8 @@ interface IamCaptchaChallenge {
1975
2005
  * ```
1976
2006
  */
1977
2007
  interface AuthRequestContext {
2008
+ /** 宿主识别的接入终端分组;不是物理设备证明,省略时普通登录归为 web。 */
2009
+ terminalType?: 'web' | 'app' | 'desktop' | 'api';
1978
2010
  /** socket/平台或受信代理解析后的地址;不能直接复制 X-Forwarded-For。 */
1979
2011
  ip?: string;
1980
2012
  userAgent?: string;
@@ -1989,6 +2021,8 @@ interface AuthRequestContext {
1989
2021
  * ```
1990
2022
  */
1991
2023
  interface PasswordCredentials {
2024
+ /** 原地恢复会话时限定原主体;鉴别成功但不是此用户时也不签发新会话。 */
2025
+ expectedUserId?: string;
1992
2026
  captcha?: string;
1993
2027
  /** 用户名/邮箱/手机号 */
1994
2028
  identifier: string;
@@ -2004,6 +2038,8 @@ interface PasswordCredentials {
2004
2038
  * ```
2005
2039
  */
2006
2040
  interface OtpCredentials {
2041
+ /** 会话恢复限定原主体,不能以其他账号的有效验证码恢复原业务动作。 */
2042
+ expectedUserId?: string;
2007
2043
  captcha?: string;
2008
2044
  /** 邮箱/手机号 */
2009
2045
  identifier: string;
@@ -2019,6 +2055,8 @@ interface OtpCredentials {
2019
2055
  * ```
2020
2056
  */
2021
2057
  interface LdapCredentials {
2058
+ /** 会话恢复限定原主体;其他账号鉴别成功也不签发恢复会话。 */
2059
+ expectedUserId?: string;
2022
2060
  captcha?: string;
2023
2061
  /** 用户名 */
2024
2062
  username: string;
@@ -2548,8 +2586,19 @@ interface IamFunctions {
2548
2586
  close: () => Promise<void>;
2549
2587
  /** 当前脱敏后的配置快照(未初始化时为 null) */
2550
2588
  readonly config: IamConfig | null;
2551
- /** 安全且反映实际可用能力的认证页面配置。 */
2589
+ /** 安全的文件配置快照;含数据库敏感规则的有效配置使用 getPublicConfig()。 */
2552
2590
  readonly publicConfig: IamPublicConfig | null;
2591
+ /**
2592
+ * 读取包含文件与当前RBAC数据库敏感操作并集的公开配置;数据库读取失败返回失败。
2593
+ * @returns 独立公开快照;未初始化或启用策略时数据库故障返回 HaiResult 失败。
2594
+ * @example
2595
+ * ```ts
2596
+ * import { iam } from '@h-ai/iam'
2597
+ * const config = await iam.getPublicConfig()
2598
+ * if (config.success) config.data.reauthentication.operations
2599
+ * ```
2600
+ */
2601
+ getPublicConfig: () => Promise<HaiResult<IamPublicConfig>>;
2553
2602
  /** 是否已初始化 */
2554
2603
  readonly isInitialized: boolean;
2555
2604
  /** 注册功能是否启用(未初始化或未配置时默认启用) */
package/dist/browser.js CHANGED
@@ -1,3 +1,3 @@
1
- export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-22SERPFD.js';
1
+ export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-KPG3M34Q.js';
2
2
  //# sourceMappingURL=browser.js.map
3
3
  //# sourceMappingURL=browser.js.map
@@ -5,17 +5,17 @@ import { core } from '@h-ai/core';
5
5
  var WebUrlSchema = z.url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol));
6
6
  var AuthStrategyTypeSchema = z.enum(["password", "otp", "ldap", "apikey"]);
7
7
  var PasswordConfigSchema = z.object({
8
- /** 最小长度(默认 8) */
8
+ /** 新设密码的最少字符数;提高后只约束新建、重置和改密,不会反查已有密码的散列。 */
9
9
  minLength: z.number().int().min(1).default(8),
10
- /** 最大长度(默认 128) */
10
+ /** 新设密码的最多字符数,须不小于 minLength;避免超长输入消耗鉴别资源。 */
11
11
  maxLength: z.number().int().min(1).max(256).default(128),
12
- /** 需要大写字母 */
12
+ /** 是否必须包含大写字母;与 minCharacterClasses 同时执行,false 仅取消这一项强制要求。 */
13
13
  requireUppercase: z.boolean().default(true),
14
- /** 需要小写字母 */
14
+ /** 是否必须包含小写字母;false 不代表取消长度或其他字符类别条件。 */
15
15
  requireLowercase: z.boolean().default(true),
16
- /** 需要数字 */
16
+ /** 是否必须包含数字;开启后所有服务端设密入口统一拒绝缺少数字的新密码。 */
17
17
  requireNumber: z.boolean().default(true),
18
- /** 需要特殊字符 */
18
+ /** 是否必须包含特殊字符;开启会增加密码输入要求,不影响已经保存的密码散列。 */
19
19
  requireSpecialChar: z.boolean().default(false),
20
20
  /** 至少包含的字符类别数(大小写、数字、特殊字符),0 不额外限制。 */
21
21
  minCharacterClasses: z.number().int().min(0).max(4).default(0),
@@ -92,31 +92,39 @@ var LoginConfigSchema = z.object({
92
92
  apikey: z.boolean().default(false)
93
93
  });
94
94
  var RegisterConfigSchema = z.object({
95
- /** 是否启用注册 */
95
+ /** 是否允许访客自助注册;关闭后隐藏注册入口并由服务端拒绝注册,不影响已有账号登录或管理员创建账号。 */
96
96
  enabled: z.boolean().default(true),
97
- /** 新注册用户是否默认启用 */
97
+ /** 自助注册的新账号是否立即启用;false 时账号已创建但不能登录,须由管理员审核启用,不改变已有账号。 */
98
98
  defaultEnabled: z.boolean().default(true)
99
99
  });
100
100
  var SecurityConfigSchema = z.object({
101
- /** 是否启用失败计数锁定;关闭时忽略历史锁。 */
101
+ /** 是否累计失败并暂时锁定密码鉴别;关闭忽略历史锁,成功密码验证会重置计数。 */
102
102
  lockoutEnabled: z.boolean().default(true),
103
- /** 从首次失败起的固定计数窗口(秒)。 */
103
+ /** 从首次失败开始的固定计数窗口,单位秒;86400 为24小时,不是滚动窗口。 */
104
104
  failureWindow: z.number().int().min(1).max(31536e3).default(86400),
105
- /** 最大登录失败次数(默认 5) */
105
+ /** 固定窗口内达到此失败次数后锁定密码鉴别;验证码失败等独立入口按各自策略处理。 */
106
106
  maxLoginAttempts: z.number().int().min(1).default(5),
107
- /** 锁定时长(秒,默认 900) */
107
+ /** 达到失败阈值后禁止密码鉴别的秒数;至少60秒,1200表示20分钟,锁定期间正确密码也不能登录。 */
108
108
  lockoutDuration: z.number().int().min(60).default(900)
109
109
  });
110
110
  var CaptchaConfigSchema = z.object({
111
+ /** 是否在认证提交前校验自托管计算挑战;开启会有验证等待,关闭时不加载浏览器求解器。 */
111
112
  enabled: z.boolean().default(false),
113
+ /** 挑战证明有效期,单位秒;到期或重复使用需重新计算,不是登录会话期限。 */
112
114
  ttlSeconds: z.number().int().min(1).max(600).default(120),
115
+ /** 单次挑战的计算工作量;越高越难自动滥用,但低性能客户端等待也越久。 */
113
116
  cost: z.number().int().min(100).max(5e4).default(5e3),
117
+ /** 求解随机计数范围;与 cost 共同决定客户端计算负担,不是密码失败次数。 */
114
118
  maxCounter: z.number().int().min(1).max(2e3).default(100)
115
119
  });
116
120
  var LoginAccessConfigSchema = z.object({
121
+ /** 是否限制新建登录会话的时段和可信客户端地址;不在窗口结束时自动踢掉已登录用户。 */
117
122
  enabled: z.boolean().default(false),
123
+ /** 允许登录的 IP/CIDR 列表,支持 IPv4/IPv6;空数组不限制地址,配置后须提供可信客户端 IP。 */
118
124
  allowIps: z.array(z.union([z.ipv4(), z.ipv6(), z.cidrv4(), z.cidrv6()])).max(1e3).default([]),
125
+ /** 拒绝登录的 IP/CIDR 列表;与允许列表同时匹配时拒绝优先。 */
119
126
  denyIps: z.array(z.union([z.ipv4(), z.ipv6(), z.cidrv4(), z.cidrv6()])).max(1e3).default([]),
127
+ /** 登录时段使用的 IANA 时区;Asia/Shanghai 为北京时间,默认 UTC。 */
120
128
  timezone: z.string().default("UTC").refine((value) => {
121
129
  try {
122
130
  new Intl.DateTimeFormat("en-US", { timeZone: value }).format();
@@ -125,9 +133,13 @@ var LoginAccessConfigSchema = z.object({
125
133
  return false;
126
134
  }
127
135
  }, "Invalid IANA timezone"),
136
+ /** 允许新登录的时间窗口;空数组不限制时间,跨午夜窗口按开始日归属。 */
128
137
  windows: z.array(z.object({
138
+ /** 允许的星期,周一为 1、周日为 7;跨午夜时填写开始日。 */
129
139
  days: z.array(z.number().int().min(1).max(7)).min(1).max(7),
140
+ /** 窗口开始时间,HH:mm,包含此时刻;按上方 timezone 解释。 */
130
141
  start: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/),
142
+ /** 窗口结束时间,HH:mm,不包含此时刻;小于开始值表示跨午夜。 */
131
143
  end: z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/)
132
144
  }).refine((value) => value.start !== value.end, "Time window start and end must differ")).max(100).default([])
133
145
  });
@@ -168,6 +180,10 @@ var SessionConfigSchema = z.object({
168
180
  singleDeviceMode: z.enum(["replace", "reject"]).default("replace"),
169
181
  /** 每个用户的最大有效会话数,0 不限制;包含 access 到期但仍可刷新的会话。 */
170
182
  maxSessions: z.number().int().min(0).max(1e4).default(0),
183
+ /** 各终端类型的最大会话数,例如 {web:2,app:1};空对象关闭。启用后未列终端不能新登录,旧会话归为 web;仍受全局上限约束。 */
184
+ terminalLimits: z.partialRecord(z.enum(["web", "app", "desktop", "api"]), z.number().int().min(1).max(1e4)).default({}),
185
+ /** 终端配额超限时 reject 拒绝新登录;replace 仅替换同类最早会话,不影响其他终端。 */
186
+ terminalLimitMode: z.enum(["reject", "replace"]).default("reject"),
171
187
  /** 闲置期限(秒),0 关闭;真实访问记录活动,自动刷新不延长闲置;access/refresh 均执行。 */
172
188
  idleTimeout: z.number().int().min(0).max(31536e3).default(0),
173
189
  /** 从首次登录起的绝对期限(秒),0 关闭;刷新不能重新起算。 */
@@ -191,11 +207,17 @@ var AccountConfigSchema = z.object({
191
207
  });
192
208
  var IamConfigSchema = z.object({
193
209
  /** 管理端可用单次邀请代替共享初始密码;需要已配置的密码重置通知通道。 */
194
- initialCredentials: z.object({ enabled: z.boolean().default(false) }).optional(),
210
+ initialCredentials: z.object({
211
+ /** 是否通过一次性邀请设密;开启前配置密码重置入口、用户联系方式与真实通知通道,不显示共享初始密码。 */
212
+ enabled: z.boolean().default(false)
213
+ }).optional(),
195
214
  /** 指定敏感操作额外验证原密码;凭据短期且单次使用。 */
196
215
  reauthentication: z.object({
216
+ /** 是否让已登录用户在敏感动作前弹窗验证当前密码;不创建另一个账号的登录,也不授予额外权限。 */
197
217
  enabled: z.boolean().default(false),
218
+ /** 需要二次认证的操作 ID 清单,例如 iam.users.delete;两端须已接入同名守卫,仅添加名字不会保护自定义接口。 */
198
219
  operations: z.array(z.string().trim().min(1).max(128)).max(1e3).default([]),
220
+ /** 二次认证凭据的有效秒数;仅供当前用户/会话的指定操作单次使用,取消不发送敏感业务请求。 */
199
221
  ttlSeconds: z.number().int().min(1).max(900).default(300)
200
222
  }).optional(),
201
223
  /** 宿主提供可信客户端地址;绝不从凭据 JSON 或任意转发头取得 IP。 */
@@ -315,5 +337,5 @@ var IamErrorInfo = {
315
337
  var HaiIamError = core.error.buildHaiErrorsDef("iam", IamErrorInfo);
316
338
 
317
339
  export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema };
318
- //# sourceMappingURL=chunk-22SERPFD.js.map
319
- //# sourceMappingURL=chunk-22SERPFD.js.map
340
+ //# sourceMappingURL=chunk-KPG3M34Q.js.map
341
+ //# sourceMappingURL=chunk-KPG3M34Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/iam-config.ts","../src/iam-types.ts"],"names":[],"mappings":"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","file":"chunk-KPG3M34Q.js","sourcesContent":["/**\n * @h-ai/iam — 配置 Schema\n *\n * 本文件定义 IAM 模块的配置结构,使用 Zod 进行运行时校验。\n * @module iam-config\n */\n\nimport { z } from 'zod'\n\n/** 浏览器可访问链接仅接受 HTTP(S),避免配置被用于脚本协议。 */\nconst WebUrlSchema = z.url().refine(value => ['http:', 'https:'].includes(new URL(value).protocol))\n\n// ─── 认证策略配置 ───\n\n/**\n * 认证策略类型\n *\n * 支持的认证方式:\n * - `password` - 用户名/邮箱 + 密码\n * - `otp` - 邮箱/短信 + 验证码\n * - `ldap` - LDAP 目录认证\n * - `apikey` - API Key 认证并按 scopes 收敛权限\n * @example\n * ```ts\n * import { AuthStrategyTypeSchema } from '@h-ai/iam'\n * const result = AuthStrategyTypeSchema.safeParse('password')\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const AuthStrategyTypeSchema = z.enum(['password', 'otp', 'ldap', 'apikey'])\n\n/**\n * 认证策略类型\n * @example\n * ```ts\n * import { AuthStrategyTypeSchema, type AuthStrategyType } from '@h-ai/iam'\n * const value: AuthStrategyType = AuthStrategyTypeSchema.parse('password')\n * ```\n */\nexport type AuthStrategyType = z.infer<typeof AuthStrategyTypeSchema>\n\n/**\n * 密码配置 Schema\n * @example\n * ```ts\n * import { PasswordConfigSchema } from '@h-ai/iam'\n * const result = PasswordConfigSchema.safeParse({ minLength: 12, requireSpecialChar: true })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const PasswordConfigSchema = z.object({\n /** 新设密码的最少字符数;提高后只约束新建、重置和改密,不会反查已有密码的散列。 */\n minLength: z.number().int().min(1).default(8),\n /** 新设密码的最多字符数,须不小于 minLength;避免超长输入消耗鉴别资源。 */\n maxLength: z.number().int().min(1).max(256).default(128),\n /** 是否必须包含大写字母;与 minCharacterClasses 同时执行,false 仅取消这一项强制要求。 */\n requireUppercase: z.boolean().default(true),\n /** 是否必须包含小写字母;false 不代表取消长度或其他字符类别条件。 */\n requireLowercase: z.boolean().default(true),\n /** 是否必须包含数字;开启后所有服务端设密入口统一拒绝缺少数字的新密码。 */\n requireNumber: z.boolean().default(true),\n /** 是否必须包含特殊字符;开启会增加密码输入要求,不影响已经保存的密码散列。 */\n requireSpecialChar: z.boolean().default(false),\n /** 至少包含的字符类别数(大小写、数字、特殊字符),0 不额外限制。 */\n minCharacterClasses: z.number().int().min(0).max(4).default(0),\n /** 密码过期天数(0 表示不过期) */\n expirationDays: z.number().int().min(0).default(0),\n /** 拒绝新旧密码相同;启用口令期限或首次强改时也始终拒绝相同密码。 */\n preventSamePassword: z.boolean().default(false),\n /** 管理员创建或重置的初始密码必须先更换;不影响用户自行注册设密。 */\n requireInitialChange: z.boolean().default(false),\n}).refine(config => config.maxLength >= config.minLength, { message: 'maxLength must be >= minLength', path: ['maxLength'] })\n\n/**\n * 密码配置类型\n * @example\n * ```ts\n * import { PasswordConfigSchema, type PasswordConfig } from '@h-ai/iam'\n * const value: PasswordConfig = PasswordConfigSchema.parse({ minLength: 12, requireSpecialChar: true })\n * ```\n */\nexport type PasswordConfig = z.infer<typeof PasswordConfigSchema>\n\n/**\n * OTP 配置 Schema\n * @example\n * ```ts\n * import { OtpConfigSchema } from '@h-ai/iam'\n * const result = OtpConfigSchema.safeParse({ email: true, sms: false, length: 6 })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const OtpConfigSchema = z.object({\n /** 分别启用邮件、短信验证码 */\n email: z.boolean().default(true),\n sms: z.boolean().default(true),\n /** 验证码长度(默认 6) */\n length: z.number().int().min(4).max(8).default(6),\n /** 验证码过期时间(秒,默认 300) */\n expiresIn: z.number().int().min(60).default(300),\n /** 最大重试次数(默认 3) */\n maxAttempts: z.number().int().min(1).default(3),\n /** 发送间隔(秒,默认 60) */\n resendInterval: z.number().int().min(30).default(60),\n})\n\n/**\n * OTP 配置类型\n * @example\n * ```ts\n * import { OtpConfigSchema, type OtpConfig } from '@h-ai/iam'\n * const value: OtpConfig = OtpConfigSchema.parse({ email: true, sms: false, length: 6 })\n * ```\n */\nexport type OtpConfig = z.infer<typeof OtpConfigSchema>\n\n/**\n * 密码重置配置 Schema\n * @example\n * ```ts\n * import { PasswordResetConfigSchema } from '@h-ai/iam'\n * const result = PasswordResetConfigSchema.safeParse({ enabled: true, mode: 'email', resetUrl: 'https://app.example.com/reset-password' })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const PasswordResetConfigSchema = z.object({\n /** 重置通知渠道,phone 使用 password_reset_sms 模板。 */\n mode: z.enum(['email', 'phone']).default('email'),\n /** 重置页面的绝对地址,通知模板可使用 resetUrl。 */\n resetUrl: WebUrlSchema.optional(),\n /** 是否启用密码重置 */\n enabled: z.boolean().default(true),\n /** 重置令牌有效期(秒,默认 3600 = 1小时) */\n tokenExpiresIn: z.number().int().min(300).default(3600),\n /** 最大验证尝试次数(默认 3) */\n maxAttempts: z.number().int().min(1).default(3),\n})\n\n/**\n * 密码重置配置类型\n * @example\n * ```ts\n * import { PasswordResetConfigSchema, type PasswordResetConfig } from '@h-ai/iam'\n * const value: PasswordResetConfig = PasswordResetConfigSchema.parse({ enabled: true, mode: 'email', resetUrl: 'https://app.example.com/reset-password' })\n * ```\n */\nexport type PasswordResetConfig = z.infer<typeof PasswordResetConfigSchema>\n\n/**\n * LDAP 配置 Schema\n * @example\n * ```ts\n * import { LdapConfigSchema } from '@h-ai/iam'\n * const bindPassword = process.env.LDAP_BIND_PASSWORD\n * if (bindPassword) {\n * const result = LdapConfigSchema.safeParse({ url: 'ldaps://directory.example.com', bindDn: 'cn=service,dc=example,dc=com', bindPassword: bindPassword, searchBase: 'ou=users,dc=example,dc=com' })\n * if (result.success)\n * result.data.searchBase\n * }\n * ```\n */\nexport const LdapConfigSchema = z.object({\n /** LDAP 服务器 URL */\n url: z.url().refine(value => ['ldap:', 'ldaps:'].includes(new URL(value).protocol)),\n /** 绑定 DN */\n bindDn: z.string(),\n /** 绑定密码 */\n bindPassword: z.string(),\n /** 搜索基础 DN */\n searchBase: z.string(),\n /** 搜索过滤器(默认使用 uid) */\n searchFilter: z.string().default('(uid={{username}})'),\n /** 用户名属性 */\n usernameAttribute: z.string().default('uid'),\n /** 邮箱属性 */\n emailAttribute: z.string().default('mail'),\n /** 显示名称属性 */\n displayNameAttribute: z.string().default('cn'),\n /** 启用 TLS */\n useTls: z.boolean().default(false),\n /** 连接超时(毫秒) */\n connectTimeout: z.number().int().min(1000).default(5000),\n})\n\n/**\n * LDAP 配置类型\n * @example\n * ```ts\n * import { LdapConfigSchema, type LdapConfig } from '@h-ai/iam'\n * const bindPassword = process.env.LDAP_BIND_PASSWORD\n * if (bindPassword) {\n * const value: LdapConfig = LdapConfigSchema.parse({ url: 'ldaps://directory.example.com', bindDn: 'cn=service,dc=example,dc=com', bindPassword: bindPassword, searchBase: 'ou=users,dc=example,dc=com' })\n * value.url\n * }\n * ```\n */\nexport type LdapConfig = z.infer<typeof LdapConfigSchema>\n\n/**\n * API Key 配置 Schema\n * @example\n * ```ts\n * import { ApiKeyConfigSchema } from '@h-ai/iam'\n * const result = ApiKeyConfigSchema.safeParse({ maxKeysPerUser: 5, defaultExpirationDays: 30 })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const ApiKeyConfigSchema = z.object({\n /** 单用户最大 API Key 数量(默认 10) */\n maxKeysPerUser: z.number().int().min(1).default(10),\n /** API Key 默认有效期(天,0 表示永不过期,默认 0) */\n defaultExpirationDays: z.number().int().min(0).default(0),\n /** API Key 前缀(默认 'hai_') */\n prefix: z.string().default('hai_'),\n})\n\n/**\n * API Key 配置类型\n * @example\n * ```ts\n * import { ApiKeyConfigSchema, type ApiKeyConfig } from '@h-ai/iam'\n * const value: ApiKeyConfig = ApiKeyConfigSchema.parse({ maxKeysPerUser: 5, defaultExpirationDays: 30 })\n * ```\n */\nexport type ApiKeyConfig = z.infer<typeof ApiKeyConfigSchema>\n\n// ─── 登录/注册与安全策略配置 ───\n\n/**\n * 登录类型启用配置\n * @example\n * ```ts\n * import { LoginConfigSchema } from '@h-ai/iam'\n * const result = LoginConfigSchema.safeParse({ password: true, otp: false })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const LoginConfigSchema = z.object({\n /** 是否启用密码登录 */\n password: z.boolean().default(true),\n /** 是否启用 OTP 登录 */\n otp: z.boolean().default(true),\n /** 是否启用 LDAP 登录;需要显式开启并提供 ldap 配置。 */\n ldap: z.boolean().default(false),\n /** 是否启用 API Key 登录 */\n apikey: z.boolean().default(false),\n})\n\n/**\n * 登录方式开关配置。\n * @example\n * ```ts\n * import { LoginConfigSchema, type LoginConfig } from '@h-ai/iam'\n * const value: LoginConfig = LoginConfigSchema.parse({ password: true, otp: false })\n * ```\n */\nexport type LoginConfig = z.infer<typeof LoginConfigSchema>\n\n/**\n * 注册配置\n * @example\n * ```ts\n * import { RegisterConfigSchema } from '@h-ai/iam'\n * const result = RegisterConfigSchema.safeParse({ enabled: false })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const RegisterConfigSchema = z.object({\n /** 是否允许访客自助注册;关闭后隐藏注册入口并由服务端拒绝注册,不影响已有账号登录或管理员创建账号。 */\n enabled: z.boolean().default(true),\n /** 自助注册的新账号是否立即启用;false 时账号已创建但不能登录,须由管理员审核启用,不改变已有账号。 */\n defaultEnabled: z.boolean().default(true),\n})\n\n/**\n * 注册开关及新用户默认状态。\n * @example\n * ```ts\n * import { RegisterConfigSchema, type RegisterConfig } from '@h-ai/iam'\n * const value: RegisterConfig = RegisterConfigSchema.parse({ enabled: false })\n * ```\n */\nexport type RegisterConfig = z.infer<typeof RegisterConfigSchema>\n\n/**\n * 安全策略配置\n * @example\n * ```ts\n * import { SecurityConfigSchema } from '@h-ai/iam'\n * const result = SecurityConfigSchema.safeParse({ maxLoginAttempts: 5, lockoutDuration: 900 })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const SecurityConfigSchema = z.object({\n /** 是否累计失败并暂时锁定密码鉴别;关闭忽略历史锁,成功密码验证会重置计数。 */\n lockoutEnabled: z.boolean().default(true),\n /** 从首次失败开始的固定计数窗口,单位秒;86400 为24小时,不是滚动窗口。 */\n failureWindow: z.number().int().min(1).max(31536000).default(86400),\n /** 固定窗口内达到此失败次数后锁定密码鉴别;验证码失败等独立入口按各自策略处理。 */\n maxLoginAttempts: z.number().int().min(1).default(5),\n /** 达到失败阈值后禁止密码鉴别的秒数;至少60秒,1200表示20分钟,锁定期间正确密码也不能登录。 */\n lockoutDuration: z.number().int().min(60).default(900),\n})\n\n/**\n * 自托管 ALTCHA 计算挑战;默认关闭,强度与有效期由应用设置。\n * @example\n * ```ts\n * import { CaptchaConfigSchema } from '@h-ai/iam'\n * const config = CaptchaConfigSchema.parse({ enabled: true, ttlSeconds: 120 })\n * ```\n */\nexport const CaptchaConfigSchema = z.object({\n /** 是否在认证提交前校验自托管计算挑战;开启会有验证等待,关闭时不加载浏览器求解器。 */\n enabled: z.boolean().default(false),\n /** 挑战证明有效期,单位秒;到期或重复使用需重新计算,不是登录会话期限。 */\n ttlSeconds: z.number().int().min(1).max(600).default(120),\n /** 单次挑战的计算工作量;越高越难自动滥用,但低性能客户端等待也越久。 */\n cost: z.number().int().min(100).max(50000).default(5000),\n /** 求解随机计数范围;与 cost 共同决定客户端计算负担,不是密码失败次数。 */\n maxCounter: z.number().int().min(1).max(2000).default(100),\n})\n\n/**\n * 服务端账户失败计数与锁定策略。\n * @example\n * ```ts\n * import { SecurityConfigSchema, type SecurityConfig } from '@h-ai/iam'\n * const value: SecurityConfig = SecurityConfigSchema.parse({ maxLoginAttempts: 5, lockoutDuration: 900 })\n * ```\n */\nexport type SecurityConfig = z.infer<typeof SecurityConfigSchema>\n\n/**\n * 登录时段与地址策略;空地址/时段列表不限制对应维度。周一为1,周日为7。\n * @example\n * ```ts\n * import { LoginAccessConfigSchema } from '@h-ai/iam'\n * const policy = LoginAccessConfigSchema.parse({ enabled: true, allowIps: ['192.0.2.0/24'], timezone: 'Asia/Shanghai' })\n * ```\n */\nexport const LoginAccessConfigSchema = z.object({\n /** 是否限制新建登录会话的时段和可信客户端地址;不在窗口结束时自动踢掉已登录用户。 */\n enabled: z.boolean().default(false),\n /** 允许登录的 IP/CIDR 列表,支持 IPv4/IPv6;空数组不限制地址,配置后须提供可信客户端 IP。 */\n allowIps: z.array(z.union([z.ipv4(), z.ipv6(), z.cidrv4(), z.cidrv6()])).max(1000).default([]),\n /** 拒绝登录的 IP/CIDR 列表;与允许列表同时匹配时拒绝优先。 */\n denyIps: z.array(z.union([z.ipv4(), z.ipv6(), z.cidrv4(), z.cidrv6()])).max(1000).default([]),\n /** 登录时段使用的 IANA 时区;Asia/Shanghai 为北京时间,默认 UTC。 */\n timezone: z.string().default('UTC').refine((value) => {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: value }).format()\n return true\n }\n catch { return false }\n }, 'Invalid IANA timezone'),\n /** 允许新登录的时间窗口;空数组不限制时间,跨午夜窗口按开始日归属。 */\n windows: z.array(z.object({\n /** 允许的星期,周一为 1、周日为 7;跨午夜时填写开始日。 */\n days: z.array(z.number().int().min(1).max(7)).min(1).max(7),\n /** 窗口开始时间,HH:mm,包含此时刻;按上方 timezone 解释。 */\n start: z.string().regex(/^(?:[01]\\d|2[0-3]):[0-5]\\d$/),\n /** 窗口结束时间,HH:mm,不包含此时刻;小于开始值表示跨午夜。 */\n end: z.string().regex(/^(?:[01]\\d|2[0-3]):[0-5]\\d$/),\n }).refine(value => value.start !== value.end, 'Time window start and end must differ')).max(100).default([]),\n})\n\n/**\n * 协议展示配置\n * @example\n * ```ts\n * import { AgreementConfigSchema } from '@h-ai/iam'\n * const result = AgreementConfigSchema.safeParse({ userAgreementUrl: 'https://app.example.com/terms', showOnRegister: true })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const AgreementConfigSchema = z.object({\n /** 用户协议 Markdown 正文;同时配置 URL 时优先显示正文。 */\n userAgreementMarkdown: z.string().min(1).optional(),\n /** 隐私协议 Markdown 正文;YAML 使用 | 保留段落和换行。 */\n privacyPolicyMarkdown: z.string().min(1).optional(),\n /** 用户协议 URL */\n userAgreementUrl: WebUrlSchema.optional(),\n /** 隐私协议 URL */\n privacyPolicyUrl: WebUrlSchema.optional(),\n /** 注册时展示协议 */\n showOnRegister: z.boolean().default(true),\n /** 登录时展示协议 */\n showOnLogin: z.boolean().default(false),\n})\n\n/**\n * 登录和注册的协议展示设置。\n * @example\n * ```ts\n * import { AgreementConfigSchema, type AgreementConfig } from '@h-ai/iam'\n * const value: AgreementConfig = AgreementConfigSchema.parse({ userAgreementUrl: 'https://app.example.com/terms', showOnRegister: true })\n * ```\n */\nexport type AgreementConfig = z.infer<typeof AgreementConfigSchema>\n\n/** 认证页面外观;文本由应用配置或 i18n 提供,图片只接受 HTTP(S) 或站内路径。 */\nconst AuthImageSchema = z.string().refine(value => /^https?:\\/\\//.test(value) || (value.startsWith('/') && !value.startsWith('//')))\n/**\n * 认证页面外观 Schema;图片仅接受 HTTP(S) 或站内路径,输出包含默认展示模式。\n * @example\n * ```ts\n * import { AuthUiConfigSchema } from '@h-ai/iam'\n * const result = AuthUiConfigSchema.safeParse({ variant: 'split', logoUrl: '/logo.svg' })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const AuthUiConfigSchema = z.object({\n variant: z.enum(['card', 'split']).default('card'),\n title: z.string().optional(),\n subtitle: z.string().optional(),\n brandTitle: z.string().optional(),\n brandText: z.string().optional(),\n logoUrl: AuthImageSchema.optional(),\n illustrationUrl: AuthImageSchema.optional(),\n})\n/**\n * 认证页外观设置;不包含服务端秘密。\n * @example\n * ```ts\n * import { AuthUiConfigSchema, type AuthUiConfig } from '@h-ai/iam'\n * const value: AuthUiConfig = AuthUiConfigSchema.parse({ variant: 'split', logoUrl: '/logo.svg' })\n * ```\n */\nexport type AuthUiConfig = z.infer<typeof AuthUiConfigSchema>\n\n// ─── 会话配置 ───\n\n/**\n * 会话配置 Schema\n * @example\n * ```ts\n * import { SessionConfigSchema } from '@h-ai/iam'\n * const result = SessionConfigSchema.safeParse({ maxAge: 3600, refreshTokenMaxAge: 604800 })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const SessionConfigSchema = z.object({\n /** 新会话的默认 access 时长(秒);创建时可用 maxAge 覆盖,滑动/刷新沿用会话固有时长。 */\n maxAge: z.number().int().min(60).default(86400),\n /** 页面离开时尽力撤销会话;浏览器关闭不可保证,仍应配置服务端闲置/绝对期限。 */\n endOnPageHide: z.boolean().default(false),\n /** 是否滑动窗口(每次访问刷新过期时间) */\n sliding: z.boolean().default(true),\n /** 单设备登录(踢掉其他设备) */\n singleDevice: z.boolean().default(false),\n /** 单设备超限策略:replace 顶旧保新,reject 保留旧会话并拒绝新登录。 */\n singleDeviceMode: z.enum(['replace', 'reject']).default('replace'),\n /** 每个用户的最大有效会话数,0 不限制;包含 access 到期但仍可刷新的会话。 */\n maxSessions: z.number().int().min(0).max(10000).default(0),\n /** 各终端类型的最大会话数,例如 {web:2,app:1};空对象关闭。启用后未列终端不能新登录,旧会话归为 web;仍受全局上限约束。 */\n terminalLimits: z.partialRecord(z.enum(['web', 'app', 'desktop', 'api']), z.number().int().min(1).max(10000)).default({}),\n /** 终端配额超限时 reject 拒绝新登录;replace 仅替换同类最早会话,不影响其他终端。 */\n terminalLimitMode: z.enum(['reject', 'replace']).default('reject'),\n /** 闲置期限(秒),0 关闭;真实访问记录活动,自动刷新不延长闲置;access/refresh 均执行。 */\n idleTimeout: z.number().int().min(0).max(31536000).default(0),\n /** 从首次登录起的绝对期限(秒),0 关闭;刷新不能重新起算。 */\n absoluteTimeout: z.number().int().min(0).max(31536000).default(0),\n /** refreshToken 独立有效期(秒,默认 604800 = 7天);从签发时刻起算,access 到期仍可刷新。 */\n refreshTokenMaxAge: z.number().int().min(3600).default(604800),\n})\n\n/**\n * 会话配置类型\n * @example\n * ```ts\n * import { SessionConfigSchema, type SessionConfig } from '@h-ai/iam'\n * const value: SessionConfig = SessionConfigSchema.parse({ maxAge: 3600, refreshTokenMaxAge: 604800 })\n * ```\n */\nexport type SessionConfig = z.infer<typeof SessionConfigSchema>\n\n// ─── RBAC 配置 ───\n\n/**\n * RBAC 配置 Schema\n * @example\n * ```ts\n * import { RbacConfigSchema } from '@h-ai/iam'\n * const result = RbacConfigSchema.safeParse({ enabled: true, defaultRole: 'user' })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const RbacConfigSchema = z.object({\n /** 是否启用 RBAC */\n enabled: z.boolean().default(true),\n /** 超级管理员角色代码 */\n superAdminRole: z.string().default('super_admin'),\n /** 默认用户角色 */\n defaultRole: z.string().default('user'),\n})\n\n/**\n * RBAC 配置类型\n * @example\n * ```ts\n * import { RbacConfigSchema, type RbacConfig } from '@h-ai/iam'\n * const value: RbacConfig = RbacConfigSchema.parse({ enabled: true, defaultRole: 'user' })\n * ```\n */\nexport type RbacConfig = z.infer<typeof RbacConfigSchema>\n\n/**\n * 可选账号闲置扫描;0天关闭自动休眠,不影响明确设置的账号状态与到期时间。\n * @example\n * ```ts\n * import { AccountConfigSchema } from '@h-ai/iam'\n * const config = AccountConfigSchema.parse({ dormantAfterDays: 90, scanIntervalSeconds: 3600 })\n * ```\n */\nexport const AccountConfigSchema = z.object({\n /** 连续未成功登录多少天后休眠,0 关闭。未登录账号从创建时间起算。 */\n dormantAfterDays: z.number().int().min(0).max(36500).default(0),\n /** 自动扫描间隔(秒);开启自动休眠时执行,默认每小时。 */\n scanIntervalSeconds: z.number().int().min(60).max(86400).default(3600),\n})\n\n// ─── 统一配置 ───\n\n/**\n * IAM 统一配置 Schema\n * @example\n * ```ts\n * import { IamConfigSchema } from '@h-ai/iam'\n * const result = IamConfigSchema.safeParse({ seedDefaultData: false, session: { maxAge: 3600 } })\n * if (result.success) {\n * result.data // 输出已应用 Schema 默认值。\n * }\n * ```\n */\nexport const IamConfigSchema = z.object({\n /** 管理端可用单次邀请代替共享初始密码;需要已配置的密码重置通知通道。 */\n initialCredentials: z.object({\n /** 是否通过一次性邀请设密;开启前配置密码重置入口、用户联系方式与真实通知通道,不显示共享初始密码。 */\n enabled: z.boolean().default(false),\n }).optional(),\n /** 指定敏感操作额外验证原密码;凭据短期且单次使用。 */\n reauthentication: z.object({\n /** 是否让已登录用户在敏感动作前弹窗验证当前密码;不创建另一个账号的登录,也不授予额外权限。 */\n enabled: z.boolean().default(false),\n /** 需要二次认证的操作 ID 清单,例如 iam.users.delete;两端须已接入同名守卫,仅添加名字不会保护自定义接口。 */\n operations: z.array(z.string().trim().min(1).max(128)).max(1000).default([]),\n /** 二次认证凭据的有效秒数;仅供当前用户/会话的指定操作单次使用,取消不发送敏感业务请求。 */\n ttlSeconds: z.number().int().min(1).max(900).default(300),\n }).optional(),\n /** 宿主提供可信客户端地址;绝不从凭据 JSON 或任意转发头取得 IP。 */\n loginAccess: LoginAccessConfigSchema.optional(),\n /** 账号自动休眠策略;未配置时不启动扫描。 */\n accounts: AccountConfigSchema.optional(),\n /** 认证页外观 */\n ui: AuthUiConfigSchema.optional(),\n // ─── 认证策略配置 ───\n\n /** 密码配置 */\n password: PasswordConfigSchema.optional(),\n\n /** OTP 配置 */\n otp: OtpConfigSchema.optional(),\n\n /** LDAP 配置 */\n ldap: LdapConfigSchema.optional(),\n\n /** API Key 配置 */\n apikey: ApiKeyConfigSchema.optional(),\n\n /** 密码重置配置 */\n passwordReset: PasswordResetConfigSchema.optional(),\n\n /** 登录启用配置 */\n login: LoginConfigSchema.default({\n password: true,\n otp: true,\n ldap: false,\n apikey: false,\n }),\n\n /** 注册配置 */\n register: RegisterConfigSchema.default({\n enabled: true,\n defaultEnabled: true,\n }),\n\n /** 协议展示配置 */\n agreements: AgreementConfigSchema.default({\n showOnRegister: true,\n showOnLogin: false,\n }),\n\n /** 可选的自托管交互挑战。 */\n captcha: CaptchaConfigSchema.default(() => CaptchaConfigSchema.parse({})),\n\n /** 安全策略配置 */\n security: SecurityConfigSchema.default({\n lockoutEnabled: true,\n failureWindow: 86400,\n maxLoginAttempts: 5,\n lockoutDuration: 900,\n }),\n\n // ─── 会话配置 ───\n\n /** 会话配置 */\n session: SessionConfigSchema.optional(),\n\n // ─── 授权配置 ───\n\n /** RBAC 配置 */\n rbac: RbacConfigSchema.default(() => RbacConfigSchema.parse({})),\n\n // ─── 运行时选项 ───\n\n /** 是否初始化默认角色和权限(默认 true) */\n seedDefaultData: z.boolean().default(true),\n})\n\n/**\n * IAM 配置类型\n * @example\n * ```ts\n * import { IamConfigSchema, type IamConfig } from '@h-ai/iam'\n * const value: IamConfig = IamConfigSchema.parse({ seedDefaultData: false, session: { maxAge: 3600 } })\n * ```\n */\nexport type IamConfig = z.infer<typeof IamConfigSchema>\n\n/**\n * IAM 配置设置输入类型(仅设置字段,不含运行时依赖)\n *\n * 说明:Zod 的 default 会让输入端字段可省略,但输出端字段为必填。\n * @example\n * ```ts\n * import type { IamConfigSettingsInput } from '@h-ai/iam'\n * const value: IamConfigSettingsInput = { session: { maxAge: 3600 }, seedDefaultData: false }\n * ```\n */\nexport type IamConfigSettingsInput = z.input<typeof IamConfigSchema>\n","/**\n * @h-ai/iam — 公共类型定义\n *\n * 包含:\n * - 错误类型(IamError)\n * - 函数接口(IamFunctions)\n * - 初始化配置(IamConfigInput)\n * - 子功能类型 re-export\n * @module iam-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\n\nimport type { ReachFunctions } from '@h-ai/reach'\nimport type { ApiKeyOperations } from './authn/apikey/iam-authn-apikey-types.js'\nimport type { AuthnOperations } from './authn/iam-authn-types.js'\nimport type { LdapClientFactory } from './authn/ldap/iam-authn-ldap-strategy.js'\nimport type { AuthzOperations } from './authz/iam-authz-types.js'\nimport type { IamConfig, IamConfigSettingsInput } from './iam-config.js'\nimport type { IamPublicConfig } from './iam-public-config.js'\nimport type { SessionOperations } from './session/iam-session-types.js'\nimport type { TicketOperations } from './ticket/iam-ticket-types.js'\nimport type { User, UserOperations } from './user/iam-user-types.js'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst IamErrorInfo = {\n AUTH_FAILED: '001:401',\n INVALID_CREDENTIALS: '002:401',\n USER_NOT_FOUND: '003:404',\n USER_DISABLED: '004:403',\n USER_LOCKED: '005:403',\n USER_ALREADY_EXISTS: '006:409',\n PASSWORD_EXPIRED: '007:401',\n PASSWORD_POLICY_VIOLATION: '008:400',\n OTP_INVALID: '009:400',\n NOT_INITIALIZED: '010:500',\n OTP_RESEND_TOO_FAST: '011:429',\n LOGIN_DISABLED: '012:400',\n REGISTER_DISABLED: '013:403',\n STRATEGY_NOT_SUPPORTED: '014:400',\n APIKEY_INVALID: '015:401',\n APIKEY_EXPIRED: '016:401',\n APIKEY_DISABLED: '017:403',\n APIKEY_NOT_FOUND: '018:404',\n RESET_TOKEN_INVALID: '019:400',\n RESET_TOKEN_EXPIRED: '020:400',\n RESET_TOKEN_MAX_ATTEMPTS: '021:429',\n OTP_EXPIRED: '022:400',\n SECURITY_BUSY: '023:429',\n PASSWORD_RESET_DISABLED: '024:403',\n PASSWORD_CHANGE_REQUIRED: '025:401',\n LOGIN_ACCESS_DENIED: '026:403',\n REAUTHENTICATION_REQUIRED: '027:401',\n INVITATION_DELIVERY_UNKNOWN: '028:502',\n INVITATION_DISABLED: '029:403',\n CAPTCHA_REQUIRED: '030:400',\n CAPTCHA_INVALID: '031:400',\n CAPTCHA_DISABLED: '032:403',\n CAPTCHA_UNAVAILABLE: '033:503',\n\n SESSION_NOT_FOUND: '101:401',\n SESSION_EXPIRED: '102:401',\n SESSION_INVALID: '103:401',\n SESSION_CREATE_FAILED: '104:500',\n TOKEN_EXPIRED: '105:401',\n TOKEN_INVALID: '106:401',\n TOKEN_REFRESH_FAILED: '107:401',\n SESSION_LIMIT_EXCEEDED: '108:409',\n\n PERMISSION_DENIED: '201:403',\n ROLE_NOT_FOUND: '202:404',\n PERMISSION_NOT_FOUND: '203:404',\n ROLE_ALREADY_EXISTS: '204:409',\n PERMISSION_ALREADY_EXISTS: '205:409',\n\n LDAP_CONNECTION_FAILED: '301:500',\n LDAP_BIND_FAILED: '302:401',\n LDAP_SEARCH_FAILED: '303:500',\n\n TICKET_INVALID: '110:401',\n TICKET_EXPIRED: '111:401',\n TICKET_ISSUE_FAILED: '112:500',\n\n REPOSITORY_ERROR: '401:500',\n NOT_FOUND: '402:404',\n CONFLICT: '403:409',\n\n FORBIDDEN: '501:403',\n INVALID_ARGUMENT: '502:400',\n\n CONFIG_ERROR: '901:500',\n INTERNAL_ERROR: '999:500',\n} as const satisfies ErrorInfo\n\n/**\n * IAM 错误码及 HTTP 状态定义;调用方按 code 分支,底层原因只用于脱敏诊断。\n * @example\n * ```ts\n * import { HaiIamError, iam } from '@h-ai/iam'\n * const result = await iam.auth.verifyToken('token-from-authorization-header')\n * if (!result.success && result.error.code === HaiIamError.SESSION_INVALID.code) {\n * // 由应用的认证边界转为重新登录响应,不泄露底层 cause。\n * }\n * ```\n */\nexport const HaiIamError = core.error.buildHaiErrorsDef('iam', IamErrorInfo)\n\n// ─── 初始化配置 ───\n\n/**\n * 已成功发布的认证通知上下文;发送服务必须按幂等键持久化去重。\n * 回调抛错代表送达结果未知;IAM 不自动重发,也不回滚已经发布的凭据。\n * @example\n * ```ts\n * import type { IamNotificationContext } from '@h-ai/iam'\n * async function deliver(context: IamNotificationContext, enqueue: (key: string, expiresAt: Date) => Promise<void>) {\n * // 接收端持久去重;回调失败代表送达未知,不能擅自生成新的幂等键重发。\n * await enqueue(context.idempotencyKey, context.expiresAt)\n * }\n * ```\n */\nexport interface IamNotificationContext {\n /** 同一次凭据发布的稳定 ID,不包含验证码或明文令牌。 */\n readonly idempotencyKey: string\n /** 凭据过期时间;迟到通知不得延长其有效期。 */\n readonly expiresAt: Date\n}\n\n/**\n * IAM 初始化配置\n *\n * 包含 Zod 校验的设置字段和运行时依赖。\n * 传入 `iam.init()` 的唯一参数。\n * @example\n * ```ts\n * import type { IamConfigInput } from '@h-ai/iam'\n * const value: IamConfigInput = { login: { password: true, otp: false }, register: { enabled: false }, seedDefaultData: false }\n * ```\n */\nexport interface IamConfigInput extends IamConfigSettingsInput {\n /**\n * 可选、已初始化的触达模块;由应用安装并注入,IAM 不导入或关闭该模块。\n * 自动使用 otp_email、otp_sms、password_reset、password_reset_sms 模板;显式发送回调优先。\n */\n reach?: ReachFunctions\n /** LDAP 客户端工厂(可选);启用 LDAP 且未注入时才加载应用安装的 ldapts。 */\n ldapClientFactory?: LdapClientFactory\n /** LDAP 用户同步开关(默认 true) */\n ldapSyncUser?: boolean\n /**\n * 密码重置令牌回调(可选)\n *\n * 当用户请求密码重置时,框架生成令牌后通过此回调通知业务层,\n * 业务层负责将令牌通过邮件/短信等渠道发送给用户。\n * 也可传入已初始化的 reach 自动发送;两者均未配置时重置功能不可用。\n *\n * @param user - 请求重置的用户信息\n * @param token - 重置令牌(明文,需发送给用户)\n * @param context - 令牌有效期及接收端必须复用的幂等键;发送失败/超时视为结果未知。\n */\n onPasswordResetRequest?: (user: User, token: string, context: IamNotificationContext) => Promise<void>\n\n /**\n * OTP 邮件验证码发送回调(可选)\n *\n * 当用户请求 OTP 验证码时,框架生成验证码后通过此回调通知业务层,\n * 业务层负责将验证码通过邮件发送给用户。\n * 显式回调覆盖 Reach 的 otp_email 模板;两者均不可用时该登录入口关闭。\n * 投递失败须拒绝 Promise,异常由 IAM 转为业务结果,不要在回调中吞掉失败。\n *\n * @param email - 目标邮箱\n * @param code - 验证码\n */\n onOtpSendEmail?: (email: string, code: string, context: IamNotificationContext) => Promise<void>\n\n /**\n * OTP 短信验证码发送回调(可选)\n *\n * 当用户请求 OTP 验证码时,框架生成验证码后通过此回调通知业务层,\n * 业务层负责将验证码通过短信发送给用户。\n * 显式回调覆盖 Reach 的 otp_sms 模板;两者均不可用时该登录入口关闭。\n * 投递失败须拒绝 Promise,异常由 IAM 转为业务结果,不要在回调中吞掉失败。\n *\n * @param phone - 目标手机号\n * @param code - 验证码\n */\n onOtpSendSms?: (phone: string, code: string, context: IamNotificationContext) => Promise<void>\n}\n\n// ─── 函数接口 ───\n\n/**\n * IAM 函数接口\n *\n * 统一聚合所有 IAM 功能。\n * @example\n * ```ts\n * import { iam, type IamFunctions } from '@h-ai/iam'\n * const service: IamFunctions = iam\n * // 应用先初始化 Cache、RelDB、Crypto;关闭时先停止接收请求。\n * const initialized = await service.init({ seedDefaultData: false })\n * if (initialized.success) {\n * const users = await service.user.listUsers({ page: 1, pageSize: 20 })\n * if (!users.success)\n * users.error.code\n * await service.close() // IAM 不关闭应用注入的依赖。\n * }\n * ```\n */\nexport interface IamFunctions {\n /**\n * 初始化 IAM 服务\n *\n * 已初始化时先关闭旧内部状态再重新初始化;并发初始化返回配置错误。\n * 内部按依赖顺序创建 session → authz → authn → user 子功能,\n * 并可选执行种子数据初始化。\n *\n * @param config - IAM 初始化配置(可选 session/password/LDAP 等策略配置)\n * @returns 成功返回 `ok(undefined)`;失败返回含错误码的 `err`\n *\n * @example\n * ```ts\n * import { iam } from '@h-ai/iam'\n * // 共享 RelDB、Cache 已由应用初始化;IAM 会确保 Crypto 可用。\n * const result = await iam.init({ session: { maxAge: 86400 } })\n * if (!result.success)\n * result.error.code // 交给应用统一错误边界,不能继续接收认证请求。\n * ```\n */\n init: (config: IamConfigInput) => Promise<HaiResult<void>>\n\n /**\n * 关闭 IAM 服务\n *\n * 释放所有内部子功能引用,将模块恢复到未初始化状态。\n * 关闭后访问 `iam.auth` 等属性将返回未初始化错误。\n */\n close: () => Promise<void>\n\n /** 当前脱敏后的配置快照(未初始化时为 null) */\n readonly config: IamConfig | null\n /** 安全的文件配置快照;含数据库敏感规则的有效配置使用 getPublicConfig()。 */\n readonly publicConfig: IamPublicConfig | null\n /**\n * 读取包含文件与当前RBAC数据库敏感操作并集的公开配置;数据库读取失败返回失败。\n * @returns 独立公开快照;未初始化或启用策略时数据库故障返回 HaiResult 失败。\n * @example\n * ```ts\n * import { iam } from '@h-ai/iam'\n * const config = await iam.getPublicConfig()\n * if (config.success) config.data.reauthentication.operations\n * ```\n */\n getPublicConfig: () => Promise<HaiResult<IamPublicConfig>>\n /** 是否已初始化 */\n readonly isInitialized: boolean\n /** 注册功能是否启用(未初始化或未配置时默认启用) */\n readonly isRegisterEnabled: boolean\n /** 认证操作(登录、登出、令牌验证等) */\n readonly auth: AuthnOperations\n /** 用户管理(注册、查询、更新、密码管理等) */\n readonly user: UserOperations\n /** 授权管理(角色/权限 CRUD、用户角色分配、权限检查) */\n readonly authz: AuthzOperations\n /** 会话管理(创建、查询、验证、删除会话) */\n readonly session: SessionOperations\n /** API Key 管理(创建、列表、吊销、验证),未启用 apikey 登录时返回未初始化代理 */\n readonly apiKey: ApiKeyOperations\n /** 一次性能力票据(签发 / 原子消费短期票据,用于 WebSocket 等无法携带 Bearer Token 的通道) */\n readonly ticket: TicketOperations\n}\n\n// ─── 子功能类型 re-export ───\n\nexport type { ApiKey, ApiKeyOperations, ApiKeySortDirection, ApiKeySortField, CreateApiKeyOptions, CreateApiKeyResult, ListApiKeysOptions } from './authn/apikey/iam-authn-apikey-types.js'\nexport type { ApiKeyCredentials, AuthnOperations, AuthRequestContext, AuthStrategy, CaptchaOperation, Credentials, IamCaptchaChallenge, LdapCredentials, OtpCredentials, PasswordCredentials } from './authn/iam-authn-types.js'\nexport type { LdapClientFactory } from './authn/ldap/iam-authn-ldap-strategy.js'\nexport type { AuthzOperations, Permission, PermissionQueryOptions, PermissionType, Role } from './authz/iam-authz-types.js'\nexport type { IamPublicConfig } from './iam-public-config.js'\nexport type { AuthResult, CreateSessionOptions, Session, SessionData, SessionFieldUpdates, SessionOperations, TokenPair } from './session/iam-session-types.js'\nexport type { ConsumedTicket, ConsumeTicketOptions, IssuedTicket, IssueTicketOptions, TicketGrant, TicketOperations } from './ticket/iam-ticket-types.js'\nexport type { AgreementDisplay, ListUsersOptions, RegisterOptions, RegisterResult, StoredUser, UpdateCurrentUserInput, User, UserOperations, UserSortDirection, UserSortField } from './user/iam-user-types.js'\n"]}