@h-ai/iam 0.1.0-alpha.36 → 0.1.0-alpha.37

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.md CHANGED
@@ -4,14 +4,15 @@
4
4
 
5
5
  ## 功能特性
6
6
 
7
- | 功能 | 说明 |
8
- | -------------- | ------------------------------------------------------ |
9
- | **认证** | 密码、OTP(邮箱/短信验证码)、LDAP、API Key 多策略认证 |
10
- | **会话** | 有状态会话(随机访问令牌 + 缓存,滑动续期可选) |
11
- | **授权** | RBAC 角色与权限管理(DB + 缓存,通配符权限匹配) |
12
- | **用户管理** | 注册、查询、更新、密码重置、管理员重置密码 |
13
- | **API Key** | API Key 创建、吊销、验证,支持 scope 与过期时间 |
14
- | **前端客户端** | HTTP API 契约定义,支持独立前端使用 |
7
+ | 功能 | 说明 |
8
+ | -------------- | -------------------------------------------------------------------------------------- |
9
+ | **认证** | 密码、OTP(邮箱/短信验证码)、LDAP、API Key 多策略认证 |
10
+ | **会话** | 有状态会话(随机访问令牌 + 缓存,滑动续期可选) |
11
+ | **授权** | RBAC 角色与权限管理(DB + 缓存,通配符权限匹配) |
12
+ | **用户管理** | 注册、查询、更新、密码重置、管理员重置密码 |
13
+ | **API Key** | API Key 创建、吊销、验证,支持 scope 与过期时间 |
14
+ | **一次性票据** | 短期、一次性能力票据(签发 / 原子消费),用于 WebSocket 等无法携带 Bearer Token 的通道 |
15
+ | **前端客户端** | HTTP API 契约定义,支持独立前端使用 |
15
16
 
16
17
  ## 安装
17
18
 
@@ -488,6 +489,30 @@ const result = await iam.user.confirmPasswordReset(token, 'NewPassword456')
488
489
  await iam.user.adminResetPassword(userId, 'TempPassword123')
489
490
  ```
490
491
 
492
+ ## 一次性票据 — `iam.ticket`
493
+
494
+ 短期、一次性能力票据。用于无法携带 Bearer Token 的通道(如 WebSocket URL):由已认证的 HTTP 请求签发一个绑定主体、用途与操作的票据,服务端在首次校验时**原子消费**(单次有效)。
495
+
496
+ ```ts
497
+ // 签发(已登录 HTTP 请求内):绑定主体、用途与本次操作,30 秒过期
498
+ const issued = await iam.ticket.issue({
499
+ subjectId: session.userId,
500
+ purpose: 'ai-audio',
501
+ grant: { operation: 'transcribe', model: 'whisper-1' },
502
+ ttlMs: 30_000,
503
+ })
504
+ // issued.data.ticket 交给客户端,用于建立 WebSocket 连接
505
+
506
+ // 消费(服务端建连时):原子单次,返回主体 + 用途 + grant
507
+ const consumed = await iam.ticket.consume(ticket, { purpose: 'ai-audio' })
508
+ if (consumed.success) {
509
+ const { subjectId, grant } = consumed.data
510
+ // 用 grant.operation / grant.model 交叉校验客户端请求
511
+ }
512
+ ```
513
+
514
+ 特性:密码学安全随机值、TTL、原子单次消费(并发消费仅一个成功)、用途绑定(防跨用途重放)。
515
+
491
516
  ## 错误处理
492
517
 
493
518
  所有操作返回 `HaiResult<T>`,通过 `HaiIamError` 做分支判断:
package/dist/index.d.ts CHANGED
@@ -1240,6 +1240,99 @@ interface LdapSearchEntry {
1240
1240
  */
1241
1241
  type LdapClientFactory = (config: LdapConfig) => Promise<HaiResult<LdapClient>>;
1242
1242
 
1243
+ /**
1244
+ * @h-ai/iam — 一次性票据类型
1245
+ *
1246
+ * 定义通用的短期、一次性能力票据(capability ticket):由已认证的上下文签发,
1247
+ * 绑定主体、用途与授权信息,在无法携带 Bearer Token 的通道(如 WebSocket URL)中
1248
+ * 用作一次性入场凭证。典型用途:AI 语音 WebSocket 接入。
1249
+ * @module ticket/iam-ticket-types
1250
+ */
1251
+
1252
+ /**
1253
+ * 票据授权绑定信息
1254
+ *
1255
+ * 消费时原样返回,供服务端确认本次操作、模型、会话等参数与签发时一致。
1256
+ * 除固定字段外允许应用自定义键值。
1257
+ */
1258
+ interface TicketGrant {
1259
+ /** 操作类型(如 `'transcribe'` | `'synthesize'` 或应用自定义) */
1260
+ operation?: string;
1261
+ /** 模型 ID */
1262
+ model?: string;
1263
+ /** 会话 ID */
1264
+ sessionId?: string;
1265
+ /** 应用自定义绑定字段 */
1266
+ [key: string]: unknown;
1267
+ }
1268
+ /**
1269
+ * 签发一次性票据的选项
1270
+ */
1271
+ interface IssueTicketOptions {
1272
+ /** 主体 ID(票据绑定的用户 / 主体,消费时原样返回) */
1273
+ subjectId: string;
1274
+ /** 票据用途(如 `'ai-audio'`),消费时校验一致 */
1275
+ purpose: string;
1276
+ /** 授权绑定信息(操作 / 模型 / 会话等,消费时原样返回) */
1277
+ grant?: TicketGrant;
1278
+ /** 有效期(毫秒,默认 30000) */
1279
+ ttlMs?: number;
1280
+ }
1281
+ /**
1282
+ * 签发结果
1283
+ */
1284
+ interface IssuedTicket {
1285
+ /** 一次性票据值(密码学安全随机,base64url) */
1286
+ ticket: string;
1287
+ /** 过期时间(Unix 毫秒) */
1288
+ expiresAt: number;
1289
+ }
1290
+ /**
1291
+ * 消费票据的选项
1292
+ */
1293
+ interface ConsumeTicketOptions {
1294
+ /** 期望用途;与签发用途不一致时返回 `TICKET_INVALID`(防止跨用途重放) */
1295
+ purpose?: string;
1296
+ }
1297
+ /**
1298
+ * 消费结果(校验通过并原子消费后返回)
1299
+ */
1300
+ interface ConsumedTicket {
1301
+ /** 票据绑定的主体 ID */
1302
+ subjectId: string;
1303
+ /** 票据用途 */
1304
+ purpose: string;
1305
+ /** 授权绑定信息 */
1306
+ grant: TicketGrant;
1307
+ }
1308
+ /**
1309
+ * 一次性票据操作接口(通过 `iam.ticket` 访问)
1310
+ *
1311
+ * 提供密码学安全、带 TTL、原子单次消费的能力票据签发与消费。
1312
+ */
1313
+ interface TicketOperations {
1314
+ /**
1315
+ * 签发一次性票据
1316
+ *
1317
+ * 生成密码学安全随机值,绑定主体 / 用途 / 授权信息并按 TTL 存入 cache。
1318
+ *
1319
+ * @param options - 签发选项
1320
+ * @returns 票据值与过期时间
1321
+ */
1322
+ issue: (options: IssueTicketOptions) => Promise<HaiResult<IssuedTicket>>;
1323
+ /**
1324
+ * 原子消费一次性票据
1325
+ *
1326
+ * 单次有效:并发消费同一票据时只有一个成功,其余返回 `TICKET_INVALID`。
1327
+ * 票据不存在 / 已过期 / 已消费 / 用途不匹配时返回错误。
1328
+ *
1329
+ * @param ticket - 票据值
1330
+ * @param options - 消费选项(可校验用途)
1331
+ * @returns 主体 + 用途 + 授权信息
1332
+ */
1333
+ consume: (ticket: string, options?: ConsumeTicketOptions) => Promise<HaiResult<ConsumedTicket>>;
1334
+ }
1335
+
1243
1336
  declare const HaiIamError: {
1244
1337
  readonly AUTH_FAILED: _h_ai_core.HaiErrorDef;
1245
1338
  readonly INVALID_CREDENTIALS: _h_ai_core.HaiErrorDef;
@@ -1278,6 +1371,9 @@ declare const HaiIamError: {
1278
1371
  readonly LDAP_CONNECTION_FAILED: _h_ai_core.HaiErrorDef;
1279
1372
  readonly LDAP_BIND_FAILED: _h_ai_core.HaiErrorDef;
1280
1373
  readonly LDAP_SEARCH_FAILED: _h_ai_core.HaiErrorDef;
1374
+ readonly TICKET_INVALID: _h_ai_core.HaiErrorDef;
1375
+ readonly TICKET_EXPIRED: _h_ai_core.HaiErrorDef;
1376
+ readonly TICKET_ISSUE_FAILED: _h_ai_core.HaiErrorDef;
1281
1377
  readonly REPOSITORY_ERROR: _h_ai_core.HaiErrorDef;
1282
1378
  readonly NOT_FOUND: _h_ai_core.HaiErrorDef;
1283
1379
  readonly CONFLICT: _h_ai_core.HaiErrorDef;
@@ -1380,6 +1476,8 @@ interface IamFunctions {
1380
1476
  readonly session: SessionOperations;
1381
1477
  /** API Key 管理(创建、列表、吐销、验证),未启用 apikey 登录时返回未初始化代理 */
1382
1478
  readonly apiKey: ApiKeyOperations;
1479
+ /** 一次性能力票据(签发 / 原子消费短期票据,用于 WebSocket 等无法携带 Bearer Token 的通道) */
1480
+ readonly ticket: TicketOperations;
1383
1481
  }
1384
1482
 
1385
1483
  /**
@@ -1391,4 +1489,4 @@ interface IamFunctions {
1391
1489
 
1392
1490
  declare const iam: IamFunctions;
1393
1491
 
1394
- export { type AgreementConfig, AgreementConfigSchema, type AgreementDisplay, type ApiKey, type ApiKeyConfig, ApiKeyConfigSchema, type ApiKeyCredentials, type ApiKeyOperations, type AuthResult, type AuthStrategy, type AuthStrategyType, AuthStrategyTypeSchema, type AuthnOperations, type AuthzOperations, type CreateApiKeyOptions, type CreateApiKeyResult, type CreateSessionOptions, type Credentials, HaiIamError, type IamConfig, type IamConfigInput, IamConfigSchema, type IamConfigSettingsInput, type IamFunctions, type LdapClientFactory, type LdapConfig, LdapConfigSchema, type LdapCredentials, type ListUsersOptions, type LoginConfig, LoginConfigSchema, type OtpConfig, OtpConfigSchema, type OtpCredentials, type PasswordConfig, PasswordConfigSchema, type PasswordCredentials, type PasswordResetConfig, PasswordResetConfigSchema, type Permission, type PermissionQueryOptions, type PermissionType, type RbacConfig, RbacConfigSchema, type RegisterConfig, RegisterConfigSchema, type RegisterOptions, type RegisterResult, type Role, type SecurityConfig, SecurityConfigSchema, type Session, type SessionConfig, SessionConfigSchema, type SessionData, type SessionFieldUpdates, type SessionOperations, type StoredUser, type TokenPair, type UpdateCurrentUserInput, type User, type UserOperations, iam };
1492
+ export { type AgreementConfig, AgreementConfigSchema, type AgreementDisplay, type ApiKey, type ApiKeyConfig, ApiKeyConfigSchema, type ApiKeyCredentials, type ApiKeyOperations, type AuthResult, type AuthStrategy, type AuthStrategyType, AuthStrategyTypeSchema, type AuthnOperations, type AuthzOperations, type ConsumeTicketOptions, type ConsumedTicket, type CreateApiKeyOptions, type CreateApiKeyResult, type CreateSessionOptions, type Credentials, HaiIamError, type IamConfig, type IamConfigInput, IamConfigSchema, type IamConfigSettingsInput, type IamFunctions, type IssueTicketOptions, type IssuedTicket, type LdapClientFactory, type LdapConfig, LdapConfigSchema, type LdapCredentials, type ListUsersOptions, type LoginConfig, LoginConfigSchema, type OtpConfig, OtpConfigSchema, type OtpCredentials, type PasswordConfig, PasswordConfigSchema, type PasswordCredentials, type PasswordResetConfig, PasswordResetConfigSchema, type Permission, type PermissionQueryOptions, type PermissionType, type RbacConfig, RbacConfigSchema, type RegisterConfig, RegisterConfigSchema, type RegisterOptions, type RegisterResult, type Role, type SecurityConfig, SecurityConfigSchema, type Session, type SessionConfig, SessionConfigSchema, type SessionData, type SessionFieldUpdates, type SessionOperations, type StoredUser, type TicketGrant, type TicketOperations, type TokenPair, type UpdateCurrentUserInput, type User, type UserOperations, iam };
package/dist/index.js CHANGED
@@ -200,6 +200,9 @@ var en_US_default = {
200
200
  iam_ldapStrategyRequired: "LDAP authentication requires LDAP strategy configuration",
201
201
  iam_tokenExpired: "Token has expired",
202
202
  iam_tokenInvalid: "Invalid token",
203
+ iam_ticketInvalid: "Invalid, already used, or unknown ticket",
204
+ iam_ticketExpired: "Ticket has expired",
205
+ iam_ticketIssueFailed: "Failed to issue or read ticket",
203
206
  iam_createSessionFailed: "Failed to create session",
204
207
  iam_sessionExpired: "Session has expired",
205
208
  iam_invalidRefreshToken: "Invalid refresh token",
@@ -380,6 +383,9 @@ var zh_CN_default = {
380
383
  iam_ldapStrategyRequired: "LDAP \u8BA4\u8BC1\u9700\u8981\u914D\u7F6E LDAP \u7B56\u7565",
381
384
  iam_tokenExpired: "\u4EE4\u724C\u5DF2\u8FC7\u671F",
382
385
  iam_tokenInvalid: "\u4EE4\u724C\u65E0\u6548",
386
+ iam_ticketInvalid: "\u7968\u636E\u65E0\u6548\u3001\u5DF2\u4F7F\u7528\u6216\u4E0D\u5B58\u5728",
387
+ iam_ticketExpired: "\u7968\u636E\u5DF2\u8FC7\u671F",
388
+ iam_ticketIssueFailed: "\u7968\u636E\u7B7E\u53D1\u6216\u8BFB\u53D6\u5931\u8D25",
383
389
  iam_createSessionFailed: "\u521B\u5EFA\u4F1A\u8BDD\u5931\u8D25",
384
390
  iam_sessionExpired: "\u4F1A\u8BDD\u5DF2\u5931\u6548",
385
391
  iam_invalidRefreshToken: "\u65E0\u6548\u7684\u5237\u65B0\u4EE4\u724C",
@@ -568,6 +574,9 @@ var IamErrorInfo = {
568
574
  LDAP_CONNECTION_FAILED: "301:500",
569
575
  LDAP_BIND_FAILED: "302:401",
570
576
  LDAP_SEARCH_FAILED: "303:500",
577
+ TICKET_INVALID: "110:401",
578
+ TICKET_EXPIRED: "111:401",
579
+ TICKET_ISSUE_FAILED: "112:500",
571
580
  REPOSITORY_ERROR: "401:500",
572
581
  NOT_FOUND: "402:404",
573
582
  CONFLICT: "403:409",
@@ -685,7 +694,7 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
685
694
  });
686
695
  }
687
696
  async insert(data, tx) {
688
- const result = await this.create(data, tx);
697
+ const result = await this.create({ ...data }, tx);
689
698
  if (!result.success) {
690
699
  return err(
691
700
  HaiIamError.REPOSITORY_ERROR,
@@ -724,7 +733,7 @@ var DbApiKeyRepository = class extends BaseReldbCrudRepository {
724
733
  return ok(result.data);
725
734
  }
726
735
  async updateFields(id, data, tx) {
727
- const result = await this.updateById(id, data, tx);
736
+ const result = await this.updateById(id, { ...data }, tx);
728
737
  if (!result.success) {
729
738
  return err(
730
739
  HaiIamError.REPOSITORY_ERROR,
@@ -4000,6 +4009,62 @@ function buildSessionFunctions(config) {
4000
4009
  }
4001
4010
  };
4002
4011
  }
4012
+ var logger9 = core.logger.child({ module: "iam", scope: "ticket" });
4013
+ var TICKET_KEY_PREFIX = "hai:iam:ticket:";
4014
+ var DEFAULT_TICKET_TTL_MS = 3e4;
4015
+ function buildTicketKey(ticket) {
4016
+ return `${TICKET_KEY_PREFIX}${ticket}`;
4017
+ }
4018
+ function createTicketOperations() {
4019
+ return {
4020
+ async issue(options) {
4021
+ const ttlMs = options.ttlMs !== void 0 && options.ttlMs > 0 ? options.ttlMs : DEFAULT_TICKET_TTL_MS;
4022
+ const ticket = generateToken();
4023
+ const expiresAt = Date.now() + ttlMs;
4024
+ const record = {
4025
+ subjectId: options.subjectId,
4026
+ purpose: options.purpose,
4027
+ grant: options.grant ?? {},
4028
+ expiresAt
4029
+ };
4030
+ const saved = await cache.kv.set(buildTicketKey(ticket), record, { px: ttlMs, nx: true });
4031
+ if (!saved.success) {
4032
+ logger9.warn("Failed to issue ticket", { purpose: options.purpose });
4033
+ return err(HaiIamError.TICKET_ISSUE_FAILED, iamM("iam_ticketIssueFailed"));
4034
+ }
4035
+ return ok({ ticket, expiresAt });
4036
+ },
4037
+ async consume(ticket, options) {
4038
+ if (!ticket) {
4039
+ return err(HaiIamError.TICKET_INVALID, iamM("iam_ticketInvalid"));
4040
+ }
4041
+ const key = buildTicketKey(ticket);
4042
+ const found = await cache.kv.get(key);
4043
+ if (!found.success) {
4044
+ return err(HaiIamError.TICKET_ISSUE_FAILED, iamM("iam_ticketIssueFailed"));
4045
+ }
4046
+ const record = found.data;
4047
+ if (!record) {
4048
+ return err(HaiIamError.TICKET_INVALID, iamM("iam_ticketInvalid"));
4049
+ }
4050
+ const removed = await cache.kv.del(key);
4051
+ if (!removed.success || removed.data !== 1) {
4052
+ return err(HaiIamError.TICKET_INVALID, iamM("iam_ticketInvalid"));
4053
+ }
4054
+ if (record.expiresAt <= Date.now()) {
4055
+ return err(HaiIamError.TICKET_EXPIRED, iamM("iam_ticketExpired"));
4056
+ }
4057
+ if (options?.purpose !== void 0 && options.purpose !== record.purpose) {
4058
+ return err(HaiIamError.TICKET_INVALID, iamM("iam_ticketInvalid"));
4059
+ }
4060
+ return ok({
4061
+ subjectId: record.subjectId,
4062
+ purpose: record.purpose,
4063
+ grant: record.grant
4064
+ });
4065
+ }
4066
+ };
4067
+ }
4003
4068
  function hashResetToken(token) {
4004
4069
  const result = crypto$1.hash.hash(token);
4005
4070
  if (!result.success) {
@@ -4181,7 +4246,7 @@ function createCacheResetTokenRepository() {
4181
4246
  }
4182
4247
 
4183
4248
  // src/user/iam-user-functions.ts
4184
- var logger9 = core.logger.child({ module: "iam", scope: "user" });
4249
+ var logger10 = core.logger.child({ module: "iam", scope: "user" });
4185
4250
  async function createUserOperations(deps) {
4186
4251
  try {
4187
4252
  const { config, passwordStrategyResult, sessionFunctions, authzFunctions, onPasswordResetRequest } = deps;
@@ -4196,10 +4261,10 @@ async function createUserOperations(deps) {
4196
4261
  config,
4197
4262
  onPasswordResetRequest
4198
4263
  });
4199
- logger9.info("User sub-feature initialized");
4264
+ logger10.info("User sub-feature initialized");
4200
4265
  return ok(functions);
4201
4266
  } catch (error) {
4202
- logger9.error("User sub-feature initialization failed", { error });
4267
+ logger10.error("User sub-feature initialization failed", { error });
4203
4268
  return err(
4204
4269
  HaiIamError.CONFIG_ERROR,
4205
4270
  iamM("iam_initComponentFailed"),
@@ -4345,7 +4410,7 @@ function buildRegistrationOps(ctx) {
4345
4410
  }
4346
4411
  const createdUser = createdUserResult.data;
4347
4412
  await assignDefaultRole(createdUser.id);
4348
- logger9.info("User registered", { userId: createdUser.id, username: options.username });
4413
+ logger10.info("User registered", { userId: createdUser.id, username: options.username });
4349
4414
  return ok({
4350
4415
  user: toUser(createdUser),
4351
4416
  agreements: buildAgreementDisplay(agreementConfig, agreementConfig.showOnRegister)
@@ -4540,7 +4605,7 @@ function buildUserMutationOps(ctx) {
4540
4605
  return ok(toUser(updatedResult.data));
4541
4606
  },
4542
4607
  async deleteUser(userId) {
4543
- logger9.debug("Deleting user", { userId });
4608
+ logger10.debug("Deleting user", { userId });
4544
4609
  const userResult = await userRepository.findById(userId);
4545
4610
  if (!userResult.success) {
4546
4611
  return mapRepositoryError("iam_queryUserFailed", userResult.error.message);
@@ -4580,7 +4645,7 @@ function buildUserMutationOps(ctx) {
4580
4645
  );
4581
4646
  }
4582
4647
  await sessionFunctions.deleteByUserId(userId);
4583
- logger9.info("User deleted", { userId });
4648
+ logger10.info("User deleted", { userId });
4584
4649
  return ok(void 0);
4585
4650
  }
4586
4651
  };
@@ -4590,7 +4655,7 @@ function buildPasswordChangeOps(ctx) {
4590
4655
  const { validatePassword, hashPassword } = ctx;
4591
4656
  return {
4592
4657
  async adminResetPassword(userId, newPassword) {
4593
- logger9.debug("Admin resetting user password", { userId });
4658
+ logger10.debug("Admin resetting user password", { userId });
4594
4659
  const userResult = await userRepository.findById(userId);
4595
4660
  if (!userResult.success) {
4596
4661
  return mapRepositoryError("iam_queryUserFailed", userResult.error.message);
@@ -4615,7 +4680,7 @@ function buildPasswordChangeOps(ctx) {
4615
4680
  return mapRepositoryError("iam_updateUserFailed", updateResult.error.message);
4616
4681
  }
4617
4682
  await sessionFunctions.deleteByUserId(userId);
4618
- logger9.info("Admin reset password", { userId });
4683
+ logger10.info("Admin reset password", { userId });
4619
4684
  return ok(void 0);
4620
4685
  },
4621
4686
  async changePassword(userId, oldPassword, newPassword) {
@@ -4668,7 +4733,7 @@ function buildPasswordChangeOps(ctx) {
4668
4733
  return mapRepositoryError("iam_updateUserFailed", updateResult.error.message);
4669
4734
  }
4670
4735
  await sessionFunctions.deleteByUserId(userId);
4671
- logger9.info("Password changed", { userId });
4736
+ logger10.info("Password changed", { userId });
4672
4737
  return ok(void 0);
4673
4738
  },
4674
4739
  /**
@@ -4693,15 +4758,15 @@ function buildPasswordResetOps(ctx) {
4693
4758
  const { validatePassword, hashPassword } = ctx;
4694
4759
  return {
4695
4760
  async requestPasswordReset(identifier) {
4696
- logger9.debug("Password reset requested", { identifier });
4761
+ logger10.debug("Password reset requested", { identifier });
4697
4762
  const resetConfig = ctx.resetConfig;
4698
4763
  const userResult = await userRepository.findByIdentifier(identifier);
4699
4764
  if (!userResult.success) {
4700
- logger9.warn("Failed to look up user for password reset", { identifier });
4765
+ logger10.warn("Failed to look up user for password reset", { identifier });
4701
4766
  return ok(void 0);
4702
4767
  }
4703
4768
  if (!userResult.data) {
4704
- logger9.debug("User not found for password reset, returning ok to prevent enumeration", { identifier });
4769
+ logger10.debug("User not found for password reset, returning ok to prevent enumeration", { identifier });
4705
4770
  return ok(void 0);
4706
4771
  }
4707
4772
  const user = toUser(userResult.data);
@@ -4715,16 +4780,16 @@ function buildPasswordResetOps(ctx) {
4715
4780
  try {
4716
4781
  await onPasswordResetRequest(user, token, expiresAt);
4717
4782
  } catch (callbackError) {
4718
- logger9.error("Password reset callback failed", { userId: user.id, error: callbackError });
4783
+ logger10.error("Password reset callback failed", { userId: user.id, error: callbackError });
4719
4784
  }
4720
4785
  } else {
4721
- logger9.warn("No password reset callback configured, token will not be delivered to user", { userId: user.id });
4786
+ logger10.warn("No password reset callback configured, token will not be delivered to user", { userId: user.id });
4722
4787
  }
4723
- logger9.info("Password reset token generated", { userId: user.id });
4788
+ logger10.info("Password reset token generated", { userId: user.id });
4724
4789
  return ok(void 0);
4725
4790
  },
4726
4791
  async confirmPasswordReset(token, newPassword) {
4727
- logger9.debug("Confirming password reset");
4792
+ logger10.debug("Confirming password reset");
4728
4793
  const resetConfig = ctx.resetConfig;
4729
4794
  const validateResult = validatePassword(newPassword);
4730
4795
  if (!validateResult.success)
@@ -4756,7 +4821,7 @@ function buildPasswordResetOps(ctx) {
4756
4821
  }
4757
4822
  await resetTokenRepository.removeToken(token);
4758
4823
  await sessionFunctions.deleteByUserId(userId);
4759
- logger9.info("Password reset confirmed", { userId });
4824
+ logger10.info("Password reset confirmed", { userId });
4760
4825
  return ok(void 0);
4761
4826
  }
4762
4827
  };
@@ -4782,7 +4847,7 @@ function buildUserFunctions(deps) {
4782
4847
  }
4783
4848
 
4784
4849
  // src/iam-main.ts
4785
- var logger10 = core.logger.child({ module: "iam", scope: "main" });
4850
+ var logger11 = core.logger.child({ module: "iam", scope: "main" });
4786
4851
  var initInProgress = false;
4787
4852
  var currentConfig = null;
4788
4853
  var currentAuth = null;
@@ -4790,6 +4855,7 @@ var currentUser = null;
4790
4855
  var currentAuthz = null;
4791
4856
  var currentSession = null;
4792
4857
  var currentApiKey = null;
4858
+ var currentTicket = null;
4793
4859
  var notInitialized = core.module.createNotInitializedKit(
4794
4860
  HaiIamError.NOT_INITIALIZED,
4795
4861
  () => iamM("iam_notInitialized")
@@ -4798,6 +4864,7 @@ var notInitializedAuth = notInitialized.proxy();
4798
4864
  var notInitializedAuthz = notInitialized.proxy();
4799
4865
  var notInitializedSession = notInitialized.proxy();
4800
4866
  var notInitializedApiKey = notInitialized.proxy();
4867
+ var notInitializedTicket = notInitialized.proxy();
4801
4868
  var syncUserProxy = notInitialized.proxy("sync");
4802
4869
  var asyncUserProxy = notInitialized.proxy();
4803
4870
  var notInitializedUser = new Proxy({}, {
@@ -4808,7 +4875,7 @@ var notInitializedUser = new Proxy({}, {
4808
4875
  var iam = {
4809
4876
  async init(config) {
4810
4877
  if (initInProgress) {
4811
- logger10.warn("IAM init already in progress, skipping concurrent call");
4878
+ logger11.warn("IAM init already in progress, skipping concurrent call");
4812
4879
  return err(
4813
4880
  HaiIamError.CONFIG_ERROR,
4814
4881
  iamM("iam_initInProgress")
@@ -4817,11 +4884,11 @@ var iam = {
4817
4884
  initInProgress = true;
4818
4885
  try {
4819
4886
  if (currentConfig !== null) {
4820
- logger10.warn("IAM module is already initialized, reinitializing");
4887
+ logger11.warn("IAM module is already initialized, reinitializing");
4821
4888
  await iam.close();
4822
4889
  }
4823
4890
  const { ldapClientFactory, ldapSyncUser, onPasswordResetRequest, onOtpSendEmail, onOtpSendSms, ...settingsInput } = config;
4824
- logger10.info("Initializing IAM module");
4891
+ logger11.info("Initializing IAM module");
4825
4892
  if (!reldb.isInitialized) {
4826
4893
  return err(
4827
4894
  HaiIamError.CONFIG_ERROR,
@@ -4846,7 +4913,7 @@ var iam = {
4846
4913
  }
4847
4914
  const parseResult = IamConfigSchema.safeParse(settingsInput);
4848
4915
  if (!parseResult.success) {
4849
- logger10.error("IAM config validation failed", { error: parseResult.error.message });
4916
+ logger11.error("IAM config validation failed", { error: parseResult.error.message });
4850
4917
  return err(
4851
4918
  HaiIamError.CONFIG_ERROR,
4852
4919
  iamM("iam_configError", { params: { error: parseResult.error.message } }),
@@ -4895,6 +4962,7 @@ var iam = {
4895
4962
  currentUser = userResult.data;
4896
4963
  currentConfig = parsed;
4897
4964
  currentApiKey = authnResult.data.apiKeyFunctions;
4965
+ currentTicket = createTicketOperations();
4898
4966
  const authn = authnResult.data.authn;
4899
4967
  currentAuth = {
4900
4968
  ...authn,
@@ -4906,10 +4974,10 @@ var iam = {
4906
4974
  return authn.login({ identifier: options.username, password: options.password });
4907
4975
  }
4908
4976
  };
4909
- logger10.info("IAM module initialized");
4977
+ logger11.info("IAM module initialized");
4910
4978
  return ok(void 0);
4911
4979
  } catch (error) {
4912
- logger10.error("IAM module initialization failed", { error });
4980
+ logger11.error("IAM module initialization failed", { error });
4913
4981
  return err(
4914
4982
  HaiIamError.CONFIG_ERROR,
4915
4983
  iamM("iam_initFailed"),
@@ -4934,6 +5002,9 @@ var iam = {
4934
5002
  get apiKey() {
4935
5003
  return currentApiKey ?? notInitializedApiKey;
4936
5004
  },
5005
+ get ticket() {
5006
+ return currentTicket ?? notInitializedTicket;
5007
+ },
4937
5008
  get config() {
4938
5009
  return currentConfig ? core.sanitize.sanitizeSensitiveFields(currentConfig) : null;
4939
5010
  },
@@ -4945,15 +5016,16 @@ var iam = {
4945
5016
  },
4946
5017
  async close() {
4947
5018
  if (currentConfig === null && currentAuth === null && currentUser === null && currentAuthz === null && currentSession === null) {
4948
- logger10.info("IAM module already closed, skipping");
5019
+ logger11.info("IAM module already closed, skipping");
4949
5020
  return;
4950
5021
  }
4951
- logger10.info("Closing IAM module");
5022
+ logger11.info("Closing IAM module");
4952
5023
  currentAuth = null;
4953
5024
  currentUser = null;
4954
5025
  currentAuthz = null;
4955
5026
  currentSession = null;
4956
5027
  currentApiKey = null;
5028
+ currentTicket = null;
4957
5029
  currentConfig = null;
4958
5030
  resetApiKeyRepoSingleton();
4959
5031
  resetOtpRepoSingleton();
@@ -4961,7 +5033,7 @@ var iam = {
4961
5033
  resetResetTokenRepoSingleton();
4962
5034
  resetRoleRepoSingleton();
4963
5035
  resetPermissionRepoSingleton();
4964
- logger10.info("IAM module closed");
5036
+ logger11.info("IAM module closed");
4965
5037
  }
4966
5038
  };
4967
5039