@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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { HaiIamError, IamConfigSchema, SessionConfigSchema, SecurityConfigSchema, LoginConfigSchema, OtpConfigSchema, ApiKeyConfigSchema, AuthUiConfigSchema, PasswordConfigSchema, RbacConfigSchema, AgreementConfigSchema, PasswordResetConfigSchema, RegisterConfigSchema } from './chunk-22SERPFD.js';
2
- export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-22SERPFD.js';
1
+ import { HaiIamError, IamConfigSchema, SessionConfigSchema, SecurityConfigSchema, LoginConfigSchema, OtpConfigSchema, ApiKeyConfigSchema, AuthUiConfigSchema, PasswordConfigSchema, RbacConfigSchema, AgreementConfigSchema, PasswordResetConfigSchema, RegisterConfigSchema } from './chunk-KPG3M34Q.js';
2
+ export { AccountConfigSchema, AgreementConfigSchema, ApiKeyConfigSchema, AuthStrategyTypeSchema, AuthUiConfigSchema, CaptchaConfigSchema, HaiIamError, IamConfigSchema, LdapConfigSchema, LoginAccessConfigSchema, LoginConfigSchema, OtpConfigSchema, PasswordConfigSchema, PasswordResetConfigSchema, RbacConfigSchema, RegisterConfigSchema, SecurityConfigSchema, SessionConfigSchema } from './chunk-KPG3M34Q.js';
3
3
  import { cache } from '@h-ai/cache';
4
4
  import { core, err, ok } from '@h-ai/core';
5
5
  import { crypto as crypto$1 } from '@h-ai/crypto';
@@ -213,7 +213,9 @@ var en_US_default = {
213
213
  iam_captchaRequired: "Complete the security check first",
214
214
  iam_captchaInvalid: "Security check is invalid or expired; try again",
215
215
  iam_captchaDisabled: "Security challenges are disabled",
216
- iam_captchaUnavailable: "Security check is temporarily unavailable"
216
+ iam_captchaUnavailable: "Security check is temporarily unavailable",
217
+ iam_sensitiveOperationsInvalid: "Invalid sensitive operation IDs: use at most 1000 entries of 1\u2013128 characters each.",
218
+ iam_terminalTypeInvalid: "Invalid terminal type: use web, app, desktop or api."
217
219
  };
218
220
 
219
221
  // messages/ja-JP.json
@@ -419,7 +421,9 @@ var ja_JP_default = {
419
421
  iam_captchaRequired: "\u5148\u306B\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u78BA\u8A8D\u3092\u5B8C\u4E86\u3057\u3066\u304F\u3060\u3055\u3044",
420
422
  iam_captchaInvalid: "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u78BA\u8A8D\u304C\u7121\u52B9\u307E\u305F\u306F\u671F\u9650\u5207\u308C\u3067\u3059\u3002\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044",
421
423
  iam_captchaDisabled: "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u78BA\u8A8D\u306F\u7121\u52B9\u3067\u3059",
422
- iam_captchaUnavailable: "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u78BA\u8A8D\u3092\u4E00\u6642\u7684\u306B\u5229\u7528\u3067\u304D\u307E\u305B\u3093"
424
+ iam_captchaUnavailable: "\u30BB\u30AD\u30E5\u30EA\u30C6\u30A3\u78BA\u8A8D\u3092\u4E00\u6642\u7684\u306B\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
425
+ iam_sensitiveOperationsInvalid: "\u91CD\u8981\u64CD\u4F5C ID \u306F\u6700\u59271000\u4EF6\u3001\u54041\uFF5E128\u6587\u5B57\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
426
+ iam_terminalTypeInvalid: "\u7AEF\u672B\u7A2E\u5225\u306F web\u3001app\u3001desktop\u3001api \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
423
427
  };
424
428
 
425
429
  // messages/zh-CN.json
@@ -625,7 +629,9 @@ var zh_CN_default = {
625
629
  iam_captchaRequired: "\u8BF7\u5148\u5B8C\u6210\u5B89\u5168\u9A8C\u8BC1",
626
630
  iam_captchaInvalid: "\u5B89\u5168\u9A8C\u8BC1\u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u8BD5",
627
631
  iam_captchaDisabled: "\u5B89\u5168\u9A8C\u8BC1\u672A\u542F\u7528",
628
- iam_captchaUnavailable: "\u5B89\u5168\u9A8C\u8BC1\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
632
+ iam_captchaUnavailable: "\u5B89\u5168\u9A8C\u8BC1\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
633
+ iam_sensitiveOperationsInvalid: "\u654F\u611F\u64CD\u4F5C ID \u5217\u8868\u65E0\u6548\uFF1A\u6700\u591A 1000 \u9879\uFF0C\u6BCF\u9879\u4E3A 1\u2013128 \u5B57\u7B26\u3002",
634
+ iam_terminalTypeInvalid: "\u7EC8\u7AEF\u7C7B\u578B\u65E0\u6548\uFF0C\u8BF7\u4F7F\u7528 web\u3001app\u3001desktop \u6216 api\u3002"
629
635
  };
630
636
 
631
637
  // src/iam-i18n.ts
@@ -882,6 +888,7 @@ function buildSession(options, now, sessionTtl, accessToken, credentialVersion)
882
888
  roles: options.roles ?? [],
883
889
  permissions: options.permissions ?? [],
884
890
  source: options.source,
891
+ terminalType: options.terminalType ?? "web",
885
892
  accessToken,
886
893
  createdAt: now,
887
894
  startedAt: now,
@@ -1490,7 +1497,7 @@ function createApiKeyStrategy(config) {
1490
1497
  if (!isUserActive(userResult.data)) {
1491
1498
  return err(HaiIamError.USER_DISABLED, iamM("iam_accountDisabled"));
1492
1499
  }
1493
- logger2.info("API Key authentication succeeded", { userId: userResult.data.id, keyId: apiKey.id });
1500
+ logger2.debug("API Key authentication succeeded", { userId: userResult.data.id, keyId: apiKey.id });
1494
1501
  return ok({ user: { ...toUser(userResult.data), credentialVersion: userResult.data.credentialVersion ?? "" }, apiKey });
1495
1502
  }
1496
1503
  };
@@ -1892,7 +1899,7 @@ function createLdapStrategy(config) {
1892
1899
  const resetResult = await resetLoginFailures(userRepository, storedUser, failurePolicy);
1893
1900
  if (!resetResult.success)
1894
1901
  return resetResult;
1895
- logger4.info("LDAP authentication succeeded", { userId: storedUser.id });
1902
+ logger4.debug("LDAP authentication succeeded", { userId: storedUser.id });
1896
1903
  return ok({ ...toUser(storedUser), credentialVersion: storedUser.credentialVersion ?? "" });
1897
1904
  } finally {
1898
1905
  await client.unbind();
@@ -2080,7 +2087,7 @@ function createOtpStrategy(config) {
2080
2087
  const resetResult = await resetLoginFailures(config.userRepository, storedUser, failurePolicy);
2081
2088
  if (!resetResult.success)
2082
2089
  return resetResult;
2083
- logger5.info("OTP authentication succeeded", { userId: storedUser.id });
2090
+ logger5.debug("OTP authentication succeeded", { userId: storedUser.id });
2084
2091
  return ok({ ...toUser(storedUser), credentialVersion: storedUser.credentialVersion ?? "" });
2085
2092
  }
2086
2093
  };
@@ -2243,7 +2250,7 @@ function createPasswordStrategy(config) {
2243
2250
  const resetResult = await resetLoginFailures(config.userRepository, storedUser, failurePolicy);
2244
2251
  if (!resetResult.success)
2245
2252
  return resetResult;
2246
- logger6.info("Password authentication succeeded", { userId: storedUser.id });
2253
+ logger6.debug("Password authentication succeeded", { userId: storedUser.id });
2247
2254
  return ok({ ...toUser(storedUser), credentialVersion: storedUser.credentialVersion ?? "" });
2248
2255
  }
2249
2256
  };
@@ -2430,7 +2437,8 @@ function buildAuthnOperations(deps) {
2430
2437
  avatarUrl: user.avatarUrl,
2431
2438
  ...effective,
2432
2439
  apiKeyId: apiKey?.id,
2433
- apiKeyScopes: apiKey?.scopes
2440
+ apiKeyScopes: apiKey?.scopes,
2441
+ terminalType: context?.terminalType ?? (apiKey ? "api" : "web")
2434
2442
  });
2435
2443
  if (!sessionResult.success) {
2436
2444
  return sessionResult;
@@ -2476,6 +2484,8 @@ function buildAuthnOperations(deps) {
2476
2484
  logger7.warn("Login failed", { type, reason: authResult.error.code });
2477
2485
  return authResult;
2478
2486
  }
2487
+ if (credentials.expectedUserId && credentials.expectedUserId !== authResult.data.id)
2488
+ return err(HaiIamError.INVALID_CREDENTIALS, iamM("iam_passwordWrong"));
2479
2489
  const result = await buildAuthResult(authResult.data, void 0, context);
2480
2490
  if (result.success) {
2481
2491
  logger7.info("Login succeeded", { type, userId: authResult.data.id });
@@ -2503,7 +2513,14 @@ function buildAuthnOperations(deps) {
2503
2513
  }
2504
2514
  const tickets = createTicketOperations();
2505
2515
  const sensitive = config.reauthentication;
2506
- const requiresProof = (operation) => sensitive?.enabled && sensitive.operations.includes(operation);
2516
+ async function requiresProof(operation) {
2517
+ if (!sensitive?.enabled)
2518
+ return ok(false);
2519
+ const configured = await authzFunctions.getSensitiveOperations();
2520
+ if (!configured.success)
2521
+ return configured;
2522
+ return ok(sensitive.operations.includes(operation) || configured.data.includes(operation));
2523
+ }
2507
2524
  return { loginRegisteredUser: async (user, context) => {
2508
2525
  const stored = await userRepository.findById(user.id);
2509
2526
  if (!stored.success)
@@ -2514,7 +2531,10 @@ function buildAuthnOperations(deps) {
2514
2531
  }, authn: {
2515
2532
  createCaptcha: (input) => createIamCaptcha(config.captcha, input),
2516
2533
  async reauthenticate(accessToken, password, operation) {
2517
- if (!requiresProof(operation))
2534
+ const required = await requiresProof(operation);
2535
+ if (!required.success)
2536
+ return required;
2537
+ if (!required.data)
2518
2538
  return err(HaiIamError.PERMISSION_DENIED, iamM("iam_reauthenticationRequired"));
2519
2539
  const current = await verifyCurrentSession(accessToken);
2520
2540
  if (!current.success)
@@ -2535,7 +2555,10 @@ function buildAuthnOperations(deps) {
2535
2555
  const current = await verifyCurrentSession(accessToken);
2536
2556
  if (!current.success)
2537
2557
  return current;
2538
- if (!requiresProof(operation))
2558
+ const required = await requiresProof(operation);
2559
+ if (!required.success)
2560
+ return required;
2561
+ if (!required.data)
2539
2562
  return ok(void 0);
2540
2563
  if (!ticket)
2541
2564
  return err(HaiIamError.REAUTHENTICATION_REQUIRED, iamM("iam_reauthenticationRequired"));
@@ -2590,6 +2613,7 @@ function buildAuthnOperations(deps) {
2590
2613
  }
2591
2614
  var TABLE_NAME3 = "hai_iam_permissions";
2592
2615
  var PERMISSION_FIELDS = [
2616
+ { fieldName: "sensitiveOperations", columnName: "sensitive_operations", def: { type: "JSON" }, select: true, create: true, update: true },
2593
2617
  {
2594
2618
  fieldName: "id",
2595
2619
  columnName: "id",
@@ -2679,6 +2703,27 @@ async function createDbPermissionRepository() {
2679
2703
  return repo;
2680
2704
  }
2681
2705
  var DbPermissionRepository = class extends BaseReldbCrudRepository {
2706
+ async getSensitiveOperations() {
2707
+ const result = await reldb.sql.query("SELECT sensitive_operations FROM hai_iam_permissions WHERE sensitive_operations IS NOT NULL");
2708
+ if (!result.success)
2709
+ return result;
2710
+ const operations = /* @__PURE__ */ new Set();
2711
+ try {
2712
+ for (const row of result.data) {
2713
+ const values = typeof row.sensitive_operations === "string" ? JSON.parse(row.sensitive_operations) : row.sensitive_operations;
2714
+ if (!Array.isArray(values) || values.length > 1e3)
2715
+ return err(HaiIamError.REPOSITORY_ERROR, iamM("iam_reauthenticationRequired"));
2716
+ for (const value of values) {
2717
+ if (typeof value !== "string" || !value.trim() || value.length > 128)
2718
+ return err(HaiIamError.REPOSITORY_ERROR, iamM("iam_reauthenticationRequired"));
2719
+ operations.add(value.trim());
2720
+ }
2721
+ }
2722
+ return ok([...operations].sort());
2723
+ } catch {
2724
+ return err(HaiIamError.REPOSITORY_ERROR, iamM("iam_reauthenticationRequired"));
2725
+ }
2726
+ }
2682
2727
  constructor() {
2683
2728
  super(reldb, {
2684
2729
  table: TABLE_NAME3,
@@ -3428,6 +3473,22 @@ function createRbacManager(config) {
3428
3473
  return ok(void 0);
3429
3474
  }
3430
3475
  return {
3476
+ getSensitiveOperations: () => rbacConfig.enabled ? permissionRepository.getSensitiveOperations() : Promise.resolve(ok([])),
3477
+ async setPermissionSensitiveOperations(permissionId, operations) {
3478
+ if (!Array.isArray(operations) || operations.length > 1e3 || operations.some((value) => typeof value !== "string" || !value.trim() || value.length > 128))
3479
+ return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sensitiveOperationsInvalid"));
3480
+ const updated = await permissionRepository.updateById(permissionId, { sensitiveOperations: [...new Set(operations.map((value) => value.trim()))] });
3481
+ if (!updated.success)
3482
+ return updated;
3483
+ if (updated.data.changes === 0) {
3484
+ const existing = await permissionRepository.findById(permissionId);
3485
+ if (!existing.success)
3486
+ return existing;
3487
+ if (!existing.data)
3488
+ return err(HaiIamError.PERMISSION_NOT_FOUND, iamM("iam_permissionNotExist"));
3489
+ }
3490
+ return ok(void 0);
3491
+ },
3431
3492
  async checkPermission(userId, permission) {
3432
3493
  if (!rbacConfig.enabled) {
3433
3494
  return ok(true);
@@ -3779,6 +3840,10 @@ function createRbacManager(config) {
3779
3840
  },
3780
3841
  // ─── 权限管理 ───
3781
3842
  async createPermission(permission, tx) {
3843
+ const operations = permission.sensitiveOperations ?? [];
3844
+ if (!Array.isArray(operations) || operations.length > 1e3 || operations.some((value) => typeof value !== "string" || !value.trim() || value.length > 128))
3845
+ return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sensitiveOperationsInvalid"));
3846
+ permission = { ...permission, sensitiveOperations: [...new Set(operations.map((value) => value.trim()))] };
3782
3847
  const ownTx = !tx;
3783
3848
  if (!tx) {
3784
3849
  const txResult = await reldb.tx.begin();
@@ -4065,6 +4130,7 @@ function observeAuthorization(operations) {
4065
4130
  updateRole: (id, data, tx) => observe(() => operations.updateRole(id, data, tx), { action: "role.update", resource: "iam.roles", resourceId: id, details: { fields: Object.keys(data), permissionIds: data.permissionIds } }, tx),
4066
4131
  deleteRole: (id, tx) => observe(() => operations.deleteRole(id, tx), { action: "role.delete", resource: "iam.roles", resourceId: id }, tx),
4067
4132
  createPermission: (input, tx) => observe(() => operations.createPermission(input, tx), (result) => ({ action: "permission.create", resource: "iam.permissions", resourceId: result.success ? result.data.id : void 0, details: { code: input.code } }), tx),
4133
+ setPermissionSensitiveOperations: (id, operationIds) => observe(() => operations.setPermissionSensitiveOperations(id, operationIds), { action: "permission.sensitive-operations", resource: "iam.permissions", resourceId: id, details: { operations: operationIds } }),
4068
4134
  deletePermission: (id, tx) => observe(() => operations.deletePermission(id, tx), { action: "permission.delete", resource: "iam.permissions", resourceId: id }, tx),
4069
4135
  assignPermissionToRole: (id, permissionId, tx) => observe(() => operations.assignPermissionToRole(id, permissionId, tx), { action: "permission.assign", resource: "iam.role-permissions", resourceId: id, details: { permissionId } }, tx),
4070
4136
  removePermissionFromRole: (id, permissionId, tx) => observe(() => operations.removePermissionFromRole(id, permissionId, tx), { action: "permission.remove", resource: "iam.role-permissions", resourceId: id, details: { permissionId } }, tx)
@@ -4151,6 +4217,17 @@ var steps = [{
4151
4217
  const added = await sql.execute("ALTER TABLE hai_iam_users ADD COLUMN first_login_failed_at TIMESTAMP");
4152
4218
  return added.success ? ok(void 0) : added;
4153
4219
  }
4220
+ }, {
4221
+ version: 5,
4222
+ name: "permission-sensitive-operations",
4223
+ async needed(sql) {
4224
+ const found = await schemaColumns(reldb, "hai_iam_permissions", sql);
4225
+ return found.success ? ok(found.data.length > 0 && !found.data.includes("sensitive_operations")) : found;
4226
+ },
4227
+ async apply(sql) {
4228
+ const added = await sql.execute("ALTER TABLE hai_iam_permissions ADD COLUMN sensitive_operations JSON");
4229
+ return added.success ? ok(void 0) : added;
4230
+ }
4154
4231
  }];
4155
4232
  function migrateIam(options = {}) {
4156
4233
  return migrateSchema(reldb, "iam", steps, options);
@@ -4331,7 +4408,7 @@ function retainedUntil(entry) {
4331
4408
  }
4332
4409
  function restoreSession(session) {
4333
4410
  const result = { ...session, createdAt: new Date(session.createdAt), startedAt: new Date(session.startedAt ?? session.createdAt), lastActiveAt: new Date(session.lastActiveAt), expiresAt: new Date(session.expiresAt) };
4334
- if (typeof result.credentialVersion !== "string" || !result.version || !Number.isSafeInteger(result.maxAge) || result.maxAge <= 0 || !Array.isArray(result.roles) || !result.roles.every((role) => typeof role === "string") || !Array.isArray(result.permissions) || !result.permissions.every((permission) => typeof permission === "string") || ![result.createdAt, result.startedAt, result.lastActiveAt, result.expiresAt].every((date) => Number.isFinite(date.getTime()))) {
4411
+ if (result.terminalType !== void 0 && !["web", "app", "desktop", "api"].includes(result.terminalType) || typeof result.credentialVersion !== "string" || !result.version || !Number.isSafeInteger(result.maxAge) || result.maxAge <= 0 || !Array.isArray(result.roles) || !result.roles.every((role) => typeof role === "string") || !Array.isArray(result.permissions) || !result.permissions.every((permission) => typeof permission === "string") || ![result.createdAt, result.startedAt, result.lastActiveAt, result.expiresAt].every((date) => Number.isFinite(date.getTime()))) {
4335
4412
  throw new TypeError("Invalid session state");
4336
4413
  }
4337
4414
  return result;
@@ -4410,12 +4487,29 @@ function createCacheSessionRepository(refreshTokenMaxAge, policy = {}) {
4410
4487
  }
4411
4488
  return {
4412
4489
  save: (session, tokens, singleDevice = false) => mutate(getUserSessionScope(session.userId), (state) => {
4413
- const count = Object.values(state.sessions).filter((entry) => entry.session.credentialVersion === session.credentialVersion).length;
4414
- if (singleDevice && policy.singleDeviceMode !== "reject") {
4415
- state.sessions = {};
4416
- } else if (singleDevice && count > 0 || maxSessions > 0 && count >= maxSessions) {
4490
+ const active = Object.entries(state.sessions).filter(([, entry]) => entry.session.credentialVersion === session.credentialVersion);
4491
+ const terminal = session.terminalType ?? "web";
4492
+ const limits = policy.terminalLimits ?? {};
4493
+ const cap = limits[terminal];
4494
+ if (Object.keys(limits).length && cap === void 0)
4417
4495
  return err(HaiIamError.SESSION_LIMIT_EXCEEDED, iamM("iam_sessionLimitExceeded"));
4496
+ const removed = /* @__PURE__ */ new Set();
4497
+ if (singleDevice) {
4498
+ if (policy.singleDeviceMode === "reject" && active.length)
4499
+ return err(HaiIamError.SESSION_LIMIT_EXCEEDED, iamM("iam_sessionLimitExceeded"));
4500
+ if (policy.singleDeviceMode !== "reject")
4501
+ Object.keys(state.sessions).forEach((token) => removed.add(token));
4502
+ } else if (cap) {
4503
+ const same = active.filter(([, entry]) => (entry.session.terminalType ?? "web") === terminal).sort(([firstToken, first], [secondToken, second]) => new Date(first.session.startedAt ?? first.session.createdAt).getTime() - new Date(second.session.startedAt ?? second.session.createdAt).getTime() || firstToken.localeCompare(secondToken));
4504
+ if (same.length >= cap) {
4505
+ if (policy.terminalLimitMode !== "replace")
4506
+ return err(HaiIamError.SESSION_LIMIT_EXCEEDED, iamM("iam_sessionLimitExceeded"));
4507
+ same.slice(0, same.length - cap + 1).forEach(([token]) => removed.add(token));
4508
+ }
4418
4509
  }
4510
+ if (maxSessions > 0 && active.filter(([token]) => !removed.has(token)).length >= maxSessions)
4511
+ return err(HaiIamError.SESSION_LIMIT_EXCEEDED, iamM("iam_sessionLimitExceeded"));
4512
+ removed.forEach((token) => delete state.sessions[token]);
4419
4513
  return storeSession(state, session, tokens);
4420
4514
  }),
4421
4515
  rotate: (previous, refreshToken, session, tokens) => mutate(getUserSessionScope(previous.userId), (state) => {
@@ -4466,6 +4560,8 @@ function createCacheSessionRepository(refreshTokenMaxAge, policy = {}) {
4466
4560
  return err(HaiIamError.SESSION_NOT_FOUND, iamM("iam_sessionNotExist"));
4467
4561
  if (!expectedVersion || entry.session.version !== expectedVersion)
4468
4562
  return err(HaiIamError.CONFLICT, iamM("iam_sessionStateConflict"));
4563
+ if (patch.terminalType !== void 0 && patch.terminalType !== (entry.session.terminalType ?? "web"))
4564
+ return err(HaiIamError.CONFLICT, iamM("iam_sessionStateConflict"));
4469
4565
  const updated = applySessionPatch(restoreSession(entry.session), patch);
4470
4566
  if (![updated.expiresAt, updated.lastActiveAt].every((date) => Number.isFinite(date.getTime())))
4471
4567
  return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_sessionMaxAgeInvalid"));
@@ -4580,6 +4676,8 @@ function buildSessionFunctions(config) {
4580
4676
  const singleDevice = config.singleDevice ?? false;
4581
4677
  const repo = config.sessionRepository;
4582
4678
  function candidate(options, credentialVersion) {
4679
+ if (options.terminalType !== void 0 && !["web", "app", "desktop", "api"].includes(options.terminalType))
4680
+ return err(HaiIamError.INVALID_ARGUMENT, iamM("iam_terminalTypeInvalid"));
4583
4681
  const now = /* @__PURE__ */ new Date();
4584
4682
  const ttl = options.maxAge ?? maxAge;
4585
4683
  if (!Number.isSafeInteger(ttl) || ttl <= 0 || !Number.isFinite(new Date(now.getTime() + ttl * 1e3).getTime()))
@@ -4738,6 +4836,7 @@ function buildSessionFunctions(config) {
4738
4836
  apiKeyId: effectiveSession.apiKeyId,
4739
4837
  apiKeyScopes: effectiveSession.apiKeyScopes,
4740
4838
  source: oldSession.source,
4839
+ terminalType: oldSession.terminalType ?? "web",
4741
4840
  maxAge: oldSession.maxAge,
4742
4841
  data: oldSession.data ? { ...oldSession.data, _tokenPair: void 0 } : void 0
4743
4842
  }, oldSession.credentialVersion);
@@ -5701,6 +5800,18 @@ var iam = {
5701
5800
  get auth() {
5702
5801
  return currentAuth ?? notInitializedAuth;
5703
5802
  },
5803
+ async getPublicConfig() {
5804
+ const snapshot = currentPublicConfig ? structuredClone(currentPublicConfig) : null;
5805
+ const authz = currentAuthz;
5806
+ if (!snapshot || !authz)
5807
+ return notInitialized.result();
5808
+ if (!snapshot.reauthentication.enabled)
5809
+ return ok(snapshot);
5810
+ const configured = await authz.getSensitiveOperations();
5811
+ if (!configured.success)
5812
+ return configured;
5813
+ return ok({ ...snapshot, reauthentication: { ...snapshot.reauthentication, operations: [.../* @__PURE__ */ new Set([...snapshot.reauthentication.operations, ...configured.data])].sort() } });
5814
+ },
5704
5815
  get user() {
5705
5816
  return currentUser ?? notInitializedUser;
5706
5817
  },