@h-ai/iam 0.1.0-alpha9 → 1.0.0-beta.10
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 +965 -0
- package/README.ja.md +961 -0
- package/README.md +678 -217
- package/dist/browser.d.ts +2627 -2
- package/dist/browser.js +1 -1
- package/dist/chunk-VR7JW2EW.js +320 -0
- package/dist/chunk-VR7JW2EW.js.map +1 -0
- package/dist/index.d.ts +29 -1377
- package/dist/index.js +2533 -1301
- package/dist/index.js.map +1 -1
- package/package.json +38 -20
- package/dist/api/index.d.ts +0 -746
- package/dist/api/index.js +0 -3
- package/dist/api/index.js.map +0 -1
- package/dist/chunk-CBZ4LHKU.js +0 -374
- package/dist/chunk-CBZ4LHKU.js.map +0 -1
package/dist/browser.d.ts
CHANGED
|
@@ -1,2 +1,2627 @@
|
|
|
1
|
-
|
|
2
|
-
import '
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import * as _h_ai_core from '@h-ai/core';
|
|
3
|
+
import { HaiResult, PaginationOptionsInput, PaginatedResult } from '@h-ai/core';
|
|
4
|
+
import { ReachFunctions } from '@h-ai/reach';
|
|
5
|
+
import { Challenge } from 'altcha/lib';
|
|
6
|
+
import { DmlWithTxOperations } from '@h-ai/reldb';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @h-ai/iam — 配置 Schema
|
|
10
|
+
*
|
|
11
|
+
* 本文件定义 IAM 模块的配置结构,使用 Zod 进行运行时校验。
|
|
12
|
+
* @module iam-config
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 认证策略类型
|
|
17
|
+
*
|
|
18
|
+
* 支持的认证方式:
|
|
19
|
+
* - `password` - 用户名/邮箱 + 密码
|
|
20
|
+
* - `otp` - 邮箱/短信 + 验证码
|
|
21
|
+
* - `ldap` - LDAP 目录认证
|
|
22
|
+
* - `apikey` - API Key 认证并按 scopes 收敛权限
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { AuthStrategyTypeSchema } from '@h-ai/iam'
|
|
26
|
+
* const result = AuthStrategyTypeSchema.safeParse('password')
|
|
27
|
+
* if (result.success) {
|
|
28
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare const AuthStrategyTypeSchema: z.ZodEnum<{
|
|
33
|
+
password: "password";
|
|
34
|
+
otp: "otp";
|
|
35
|
+
ldap: "ldap";
|
|
36
|
+
apikey: "apikey";
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* 认证策略类型
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { AuthStrategyTypeSchema, type AuthStrategyType } from '@h-ai/iam'
|
|
43
|
+
* const value: AuthStrategyType = AuthStrategyTypeSchema.parse('password')
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
type AuthStrategyType = z.infer<typeof AuthStrategyTypeSchema>;
|
|
47
|
+
/**
|
|
48
|
+
* 密码配置 Schema
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { PasswordConfigSchema } from '@h-ai/iam'
|
|
52
|
+
* const result = PasswordConfigSchema.safeParse({ minLength: 12, requireSpecialChar: true })
|
|
53
|
+
* if (result.success) {
|
|
54
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare const PasswordConfigSchema: z.ZodObject<{
|
|
59
|
+
minLength: z.ZodDefault<z.ZodNumber>;
|
|
60
|
+
maxLength: z.ZodDefault<z.ZodNumber>;
|
|
61
|
+
requireUppercase: z.ZodDefault<z.ZodBoolean>;
|
|
62
|
+
requireLowercase: z.ZodDefault<z.ZodBoolean>;
|
|
63
|
+
requireNumber: z.ZodDefault<z.ZodBoolean>;
|
|
64
|
+
requireSpecialChar: z.ZodDefault<z.ZodBoolean>;
|
|
65
|
+
minCharacterClasses: z.ZodDefault<z.ZodNumber>;
|
|
66
|
+
expirationDays: z.ZodDefault<z.ZodNumber>;
|
|
67
|
+
preventSamePassword: z.ZodDefault<z.ZodBoolean>;
|
|
68
|
+
requireInitialChange: z.ZodDefault<z.ZodBoolean>;
|
|
69
|
+
}, z.core.$strip>;
|
|
70
|
+
/**
|
|
71
|
+
* 密码配置类型
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { PasswordConfigSchema, type PasswordConfig } from '@h-ai/iam'
|
|
75
|
+
* const value: PasswordConfig = PasswordConfigSchema.parse({ minLength: 12, requireSpecialChar: true })
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
type PasswordConfig = z.infer<typeof PasswordConfigSchema>;
|
|
79
|
+
/**
|
|
80
|
+
* OTP 配置 Schema
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { OtpConfigSchema } from '@h-ai/iam'
|
|
84
|
+
* const result = OtpConfigSchema.safeParse({ email: true, sms: false, length: 6 })
|
|
85
|
+
* if (result.success) {
|
|
86
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
declare const OtpConfigSchema: z.ZodObject<{
|
|
91
|
+
email: z.ZodDefault<z.ZodBoolean>;
|
|
92
|
+
sms: z.ZodDefault<z.ZodBoolean>;
|
|
93
|
+
length: z.ZodDefault<z.ZodNumber>;
|
|
94
|
+
expiresIn: z.ZodDefault<z.ZodNumber>;
|
|
95
|
+
maxAttempts: z.ZodDefault<z.ZodNumber>;
|
|
96
|
+
resendInterval: z.ZodDefault<z.ZodNumber>;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
/**
|
|
99
|
+
* OTP 配置类型
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { OtpConfigSchema, type OtpConfig } from '@h-ai/iam'
|
|
103
|
+
* const value: OtpConfig = OtpConfigSchema.parse({ email: true, sms: false, length: 6 })
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
type OtpConfig = z.infer<typeof OtpConfigSchema>;
|
|
107
|
+
/**
|
|
108
|
+
* 密码重置配置 Schema
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* import { PasswordResetConfigSchema } from '@h-ai/iam'
|
|
112
|
+
* const result = PasswordResetConfigSchema.safeParse({ enabled: true, mode: 'email', resetUrl: 'https://app.example.com/reset-password' })
|
|
113
|
+
* if (result.success) {
|
|
114
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
115
|
+
* }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare const PasswordResetConfigSchema: z.ZodObject<{
|
|
119
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
120
|
+
email: "email";
|
|
121
|
+
phone: "phone";
|
|
122
|
+
}>>;
|
|
123
|
+
resetUrl: z.ZodOptional<z.ZodURL>;
|
|
124
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
125
|
+
tokenExpiresIn: z.ZodDefault<z.ZodNumber>;
|
|
126
|
+
maxAttempts: z.ZodDefault<z.ZodNumber>;
|
|
127
|
+
}, z.core.$strip>;
|
|
128
|
+
/**
|
|
129
|
+
* 密码重置配置类型
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* import { PasswordResetConfigSchema, type PasswordResetConfig } from '@h-ai/iam'
|
|
133
|
+
* const value: PasswordResetConfig = PasswordResetConfigSchema.parse({ enabled: true, mode: 'email', resetUrl: 'https://app.example.com/reset-password' })
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
type PasswordResetConfig = z.infer<typeof PasswordResetConfigSchema>;
|
|
137
|
+
/**
|
|
138
|
+
* LDAP 配置 Schema
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { LdapConfigSchema } from '@h-ai/iam'
|
|
142
|
+
* const bindPassword = process.env.LDAP_BIND_PASSWORD
|
|
143
|
+
* if (bindPassword) {
|
|
144
|
+
* 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' })
|
|
145
|
+
* if (result.success)
|
|
146
|
+
* result.data.searchBase
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
declare const LdapConfigSchema: z.ZodObject<{
|
|
151
|
+
url: z.ZodURL;
|
|
152
|
+
bindDn: z.ZodString;
|
|
153
|
+
bindPassword: z.ZodString;
|
|
154
|
+
searchBase: z.ZodString;
|
|
155
|
+
searchFilter: z.ZodDefault<z.ZodString>;
|
|
156
|
+
usernameAttribute: z.ZodDefault<z.ZodString>;
|
|
157
|
+
emailAttribute: z.ZodDefault<z.ZodString>;
|
|
158
|
+
displayNameAttribute: z.ZodDefault<z.ZodString>;
|
|
159
|
+
useTls: z.ZodDefault<z.ZodBoolean>;
|
|
160
|
+
connectTimeout: z.ZodDefault<z.ZodNumber>;
|
|
161
|
+
}, z.core.$strip>;
|
|
162
|
+
/**
|
|
163
|
+
* LDAP 配置类型
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* import { LdapConfigSchema, type LdapConfig } from '@h-ai/iam'
|
|
167
|
+
* const bindPassword = process.env.LDAP_BIND_PASSWORD
|
|
168
|
+
* if (bindPassword) {
|
|
169
|
+
* 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' })
|
|
170
|
+
* value.url
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
type LdapConfig = z.infer<typeof LdapConfigSchema>;
|
|
175
|
+
/**
|
|
176
|
+
* API Key 配置 Schema
|
|
177
|
+
* @example
|
|
178
|
+
* ```ts
|
|
179
|
+
* import { ApiKeyConfigSchema } from '@h-ai/iam'
|
|
180
|
+
* const result = ApiKeyConfigSchema.safeParse({ maxKeysPerUser: 5, defaultExpirationDays: 30 })
|
|
181
|
+
* if (result.success) {
|
|
182
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
declare const ApiKeyConfigSchema: z.ZodObject<{
|
|
187
|
+
maxKeysPerUser: z.ZodDefault<z.ZodNumber>;
|
|
188
|
+
defaultExpirationDays: z.ZodDefault<z.ZodNumber>;
|
|
189
|
+
prefix: z.ZodDefault<z.ZodString>;
|
|
190
|
+
}, z.core.$strip>;
|
|
191
|
+
/**
|
|
192
|
+
* API Key 配置类型
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* import { ApiKeyConfigSchema, type ApiKeyConfig } from '@h-ai/iam'
|
|
196
|
+
* const value: ApiKeyConfig = ApiKeyConfigSchema.parse({ maxKeysPerUser: 5, defaultExpirationDays: 30 })
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
type ApiKeyConfig = z.infer<typeof ApiKeyConfigSchema>;
|
|
200
|
+
/**
|
|
201
|
+
* 登录类型启用配置
|
|
202
|
+
* @example
|
|
203
|
+
* ```ts
|
|
204
|
+
* import { LoginConfigSchema } from '@h-ai/iam'
|
|
205
|
+
* const result = LoginConfigSchema.safeParse({ password: true, otp: false })
|
|
206
|
+
* if (result.success) {
|
|
207
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
declare const LoginConfigSchema: z.ZodObject<{
|
|
212
|
+
password: z.ZodDefault<z.ZodBoolean>;
|
|
213
|
+
otp: z.ZodDefault<z.ZodBoolean>;
|
|
214
|
+
ldap: z.ZodDefault<z.ZodBoolean>;
|
|
215
|
+
apikey: z.ZodDefault<z.ZodBoolean>;
|
|
216
|
+
}, z.core.$strip>;
|
|
217
|
+
/**
|
|
218
|
+
* 登录方式开关配置。
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* import { LoginConfigSchema, type LoginConfig } from '@h-ai/iam'
|
|
222
|
+
* const value: LoginConfig = LoginConfigSchema.parse({ password: true, otp: false })
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
type LoginConfig = z.infer<typeof LoginConfigSchema>;
|
|
226
|
+
/**
|
|
227
|
+
* 注册配置
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
* import { RegisterConfigSchema } from '@h-ai/iam'
|
|
231
|
+
* const result = RegisterConfigSchema.safeParse({ enabled: false })
|
|
232
|
+
* if (result.success) {
|
|
233
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
234
|
+
* }
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
declare const RegisterConfigSchema: z.ZodObject<{
|
|
238
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
239
|
+
defaultAccountStatus: z.ZodDefault<z.ZodEnum<{
|
|
240
|
+
active: "active";
|
|
241
|
+
dormant: "dormant";
|
|
242
|
+
}>>;
|
|
243
|
+
}, z.core.$strict>;
|
|
244
|
+
/**
|
|
245
|
+
* 注册开关及新用户默认状态。
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* import { RegisterConfigSchema, type RegisterConfig } from '@h-ai/iam'
|
|
249
|
+
* const value: RegisterConfig = RegisterConfigSchema.parse({ enabled: false })
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
type RegisterConfig = z.infer<typeof RegisterConfigSchema>;
|
|
253
|
+
/**
|
|
254
|
+
* 安全策略配置
|
|
255
|
+
* @example
|
|
256
|
+
* ```ts
|
|
257
|
+
* import { SecurityConfigSchema } from '@h-ai/iam'
|
|
258
|
+
* const result = SecurityConfigSchema.safeParse({ maxLoginAttempts: 5, lockoutDuration: 900 })
|
|
259
|
+
* if (result.success) {
|
|
260
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
261
|
+
* }
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
declare const SecurityConfigSchema: z.ZodObject<{
|
|
265
|
+
lockoutEnabled: z.ZodDefault<z.ZodBoolean>;
|
|
266
|
+
failureWindow: z.ZodDefault<z.ZodNumber>;
|
|
267
|
+
maxLoginAttempts: z.ZodDefault<z.ZodNumber>;
|
|
268
|
+
lockoutDuration: z.ZodDefault<z.ZodNumber>;
|
|
269
|
+
}, z.core.$strip>;
|
|
270
|
+
/**
|
|
271
|
+
* 自托管 ALTCHA 计算挑战;默认关闭,强度与有效期由应用设置。
|
|
272
|
+
* @example
|
|
273
|
+
* ```ts
|
|
274
|
+
* import { CaptchaConfigSchema } from '@h-ai/iam'
|
|
275
|
+
* const config = CaptchaConfigSchema.parse({ enabled: true, ttlSeconds: 120 })
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
declare const CaptchaConfigSchema: z.ZodObject<{
|
|
279
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
280
|
+
ttlSeconds: z.ZodDefault<z.ZodNumber>;
|
|
281
|
+
cost: z.ZodDefault<z.ZodNumber>;
|
|
282
|
+
maxCounter: z.ZodDefault<z.ZodNumber>;
|
|
283
|
+
}, z.core.$strip>;
|
|
284
|
+
/**
|
|
285
|
+
* 服务端账户失败计数与锁定策略。
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* import { SecurityConfigSchema, type SecurityConfig } from '@h-ai/iam'
|
|
289
|
+
* const value: SecurityConfig = SecurityConfigSchema.parse({ maxLoginAttempts: 5, lockoutDuration: 900 })
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
type SecurityConfig = z.infer<typeof SecurityConfigSchema>;
|
|
293
|
+
/**
|
|
294
|
+
* 登录时段与地址策略;空地址/时段列表不限制对应维度。周一为1,周日为7。
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* import { LoginAccessConfigSchema } from '@h-ai/iam'
|
|
298
|
+
* const policy = LoginAccessConfigSchema.parse({ enabled: true, allowIps: ['192.0.2.0/24'], timezone: 'Asia/Shanghai' })
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
declare const LoginAccessConfigSchema: z.ZodObject<{
|
|
302
|
+
allowIps: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodIPv4, z.ZodIPv6, z.ZodCIDRv4, z.ZodCIDRv6]>>>;
|
|
303
|
+
denyIps: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodIPv4, z.ZodIPv6, z.ZodCIDRv4, z.ZodCIDRv6]>>>;
|
|
304
|
+
timezone: z.ZodDefault<z.ZodString>;
|
|
305
|
+
windows: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
306
|
+
days: z.ZodArray<z.ZodNumber>;
|
|
307
|
+
start: z.ZodString;
|
|
308
|
+
end: z.ZodString;
|
|
309
|
+
}, z.core.$strict>>>;
|
|
310
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
311
|
+
}, z.core.$strict>;
|
|
312
|
+
/**
|
|
313
|
+
* 协议展示配置
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* import { AgreementConfigSchema } from '@h-ai/iam'
|
|
317
|
+
* const result = AgreementConfigSchema.safeParse({ userAgreementUrl: 'https://app.example.com/terms', showOnRegister: true })
|
|
318
|
+
* if (result.success) {
|
|
319
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
320
|
+
* }
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
declare const AgreementConfigSchema: z.ZodObject<{
|
|
324
|
+
userAgreementMarkdown: z.ZodOptional<z.ZodString>;
|
|
325
|
+
privacyPolicyMarkdown: z.ZodOptional<z.ZodString>;
|
|
326
|
+
userAgreementUrl: z.ZodOptional<z.ZodURL>;
|
|
327
|
+
privacyPolicyUrl: z.ZodOptional<z.ZodURL>;
|
|
328
|
+
showOnRegister: z.ZodDefault<z.ZodBoolean>;
|
|
329
|
+
showOnLogin: z.ZodDefault<z.ZodBoolean>;
|
|
330
|
+
}, z.core.$strip>;
|
|
331
|
+
/**
|
|
332
|
+
* 登录和注册的协议展示设置。
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* import { AgreementConfigSchema, type AgreementConfig } from '@h-ai/iam'
|
|
336
|
+
* const value: AgreementConfig = AgreementConfigSchema.parse({ userAgreementUrl: 'https://app.example.com/terms', showOnRegister: true })
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
type AgreementConfig = z.infer<typeof AgreementConfigSchema>;
|
|
340
|
+
/**
|
|
341
|
+
* 认证页面外观 Schema;图片仅接受 HTTP(S) 或站内路径,输出包含默认展示模式。
|
|
342
|
+
* @example
|
|
343
|
+
* ```ts
|
|
344
|
+
* import { AuthUiConfigSchema } from '@h-ai/iam'
|
|
345
|
+
* const result = AuthUiConfigSchema.safeParse({ variant: 'split', logoUrl: '/logo.svg' })
|
|
346
|
+
* if (result.success) {
|
|
347
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
348
|
+
* }
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
declare const AuthUiConfigSchema: z.ZodObject<{
|
|
352
|
+
variant: z.ZodDefault<z.ZodEnum<{
|
|
353
|
+
card: "card";
|
|
354
|
+
split: "split";
|
|
355
|
+
}>>;
|
|
356
|
+
title: z.ZodOptional<z.ZodString>;
|
|
357
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
358
|
+
brandTitle: z.ZodOptional<z.ZodString>;
|
|
359
|
+
brandText: z.ZodOptional<z.ZodString>;
|
|
360
|
+
logoUrl: z.ZodOptional<z.ZodString>;
|
|
361
|
+
illustrationUrl: z.ZodOptional<z.ZodString>;
|
|
362
|
+
}, z.core.$strip>;
|
|
363
|
+
/**
|
|
364
|
+
* 认证页外观设置;不包含服务端秘密。
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* import { AuthUiConfigSchema, type AuthUiConfig } from '@h-ai/iam'
|
|
368
|
+
* const value: AuthUiConfig = AuthUiConfigSchema.parse({ variant: 'split', logoUrl: '/logo.svg' })
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
type AuthUiConfig = z.infer<typeof AuthUiConfigSchema>;
|
|
372
|
+
/**
|
|
373
|
+
* 会话配置 Schema
|
|
374
|
+
* @example
|
|
375
|
+
* ```ts
|
|
376
|
+
* import { SessionConfigSchema } from '@h-ai/iam'
|
|
377
|
+
* const result = SessionConfigSchema.safeParse({ maxAge: 3600, refreshTokenMaxAge: 604800 })
|
|
378
|
+
* if (result.success) {
|
|
379
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
380
|
+
* }
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
declare const SessionConfigSchema: z.ZodObject<{
|
|
384
|
+
maxAge: z.ZodDefault<z.ZodNumber>;
|
|
385
|
+
endOnPageHide: z.ZodDefault<z.ZodBoolean>;
|
|
386
|
+
sliding: z.ZodDefault<z.ZodBoolean>;
|
|
387
|
+
singleDevice: z.ZodDefault<z.ZodBoolean>;
|
|
388
|
+
singleDeviceMode: z.ZodDefault<z.ZodEnum<{
|
|
389
|
+
replace: "replace";
|
|
390
|
+
reject: "reject";
|
|
391
|
+
}>>;
|
|
392
|
+
maxSessions: z.ZodDefault<z.ZodNumber>;
|
|
393
|
+
terminalLimits: z.ZodDefault<z.ZodRecord<z.ZodEnum<{
|
|
394
|
+
web: "web";
|
|
395
|
+
app: "app";
|
|
396
|
+
desktop: "desktop";
|
|
397
|
+
api: "api";
|
|
398
|
+
}> & z.core.$partial, z.ZodNumber>>;
|
|
399
|
+
terminalLimitMode: z.ZodDefault<z.ZodEnum<{
|
|
400
|
+
replace: "replace";
|
|
401
|
+
reject: "reject";
|
|
402
|
+
}>>;
|
|
403
|
+
idleTimeout: z.ZodDefault<z.ZodNumber>;
|
|
404
|
+
absoluteTimeout: z.ZodDefault<z.ZodNumber>;
|
|
405
|
+
refreshTokenMaxAge: z.ZodDefault<z.ZodNumber>;
|
|
406
|
+
}, z.core.$strip>;
|
|
407
|
+
/**
|
|
408
|
+
* 会话配置类型
|
|
409
|
+
* @example
|
|
410
|
+
* ```ts
|
|
411
|
+
* import { SessionConfigSchema, type SessionConfig } from '@h-ai/iam'
|
|
412
|
+
* const value: SessionConfig = SessionConfigSchema.parse({ maxAge: 3600, refreshTokenMaxAge: 604800 })
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
type SessionConfig = z.infer<typeof SessionConfigSchema>;
|
|
416
|
+
/**
|
|
417
|
+
* RBAC 配置 Schema
|
|
418
|
+
* @example
|
|
419
|
+
* ```ts
|
|
420
|
+
* import { RbacConfigSchema } from '@h-ai/iam'
|
|
421
|
+
* const result = RbacConfigSchema.safeParse({ enabled: true, defaultRole: 'user' })
|
|
422
|
+
* if (result.success) {
|
|
423
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
424
|
+
* }
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
declare const RbacConfigSchema: z.ZodObject<{
|
|
428
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
429
|
+
superAdminRole: z.ZodDefault<z.ZodString>;
|
|
430
|
+
defaultRole: z.ZodDefault<z.ZodString>;
|
|
431
|
+
}, z.core.$strip>;
|
|
432
|
+
/**
|
|
433
|
+
* RBAC 配置类型
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* import { RbacConfigSchema, type RbacConfig } from '@h-ai/iam'
|
|
437
|
+
* const value: RbacConfig = RbacConfigSchema.parse({ enabled: true, defaultRole: 'user' })
|
|
438
|
+
* ```
|
|
439
|
+
*/
|
|
440
|
+
type RbacConfig = z.infer<typeof RbacConfigSchema>;
|
|
441
|
+
/**
|
|
442
|
+
* 可选账号闲置扫描;0天关闭自动休眠,不影响明确设置的账号状态与到期时间。
|
|
443
|
+
* @example
|
|
444
|
+
* ```ts
|
|
445
|
+
* import { AccountConfigSchema } from '@h-ai/iam'
|
|
446
|
+
* const config = AccountConfigSchema.parse({ dormantAfterDays: 90, scanIntervalSeconds: 3600 })
|
|
447
|
+
* ```
|
|
448
|
+
*/
|
|
449
|
+
declare const AccountConfigSchema: z.ZodObject<{
|
|
450
|
+
dormantAfterDays: z.ZodDefault<z.ZodNumber>;
|
|
451
|
+
scanIntervalSeconds: z.ZodDefault<z.ZodNumber>;
|
|
452
|
+
}, z.core.$strip>;
|
|
453
|
+
/**
|
|
454
|
+
* IAM 统一配置 Schema
|
|
455
|
+
* @example
|
|
456
|
+
* ```ts
|
|
457
|
+
* import { IamConfigSchema } from '@h-ai/iam'
|
|
458
|
+
* const result = IamConfigSchema.safeParse({ seedDefaultData: false, session: { maxAge: 3600 } })
|
|
459
|
+
* if (result.success) {
|
|
460
|
+
* result.data // 输出已应用 Schema 默认值。
|
|
461
|
+
* }
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
declare const IamConfigSchema: z.ZodObject<{
|
|
465
|
+
initialCredentials: z.ZodOptional<z.ZodObject<{
|
|
466
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
467
|
+
}, z.core.$strip>>;
|
|
468
|
+
reauthentication: z.ZodOptional<z.ZodObject<{
|
|
469
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
470
|
+
operations: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
471
|
+
ttlSeconds: z.ZodDefault<z.ZodNumber>;
|
|
472
|
+
}, z.core.$strip>>;
|
|
473
|
+
loginAccess: z.ZodOptional<z.ZodObject<{
|
|
474
|
+
allowIps: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodIPv4, z.ZodIPv6, z.ZodCIDRv4, z.ZodCIDRv6]>>>;
|
|
475
|
+
denyIps: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodIPv4, z.ZodIPv6, z.ZodCIDRv4, z.ZodCIDRv6]>>>;
|
|
476
|
+
timezone: z.ZodDefault<z.ZodString>;
|
|
477
|
+
windows: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
478
|
+
days: z.ZodArray<z.ZodNumber>;
|
|
479
|
+
start: z.ZodString;
|
|
480
|
+
end: z.ZodString;
|
|
481
|
+
}, z.core.$strict>>>;
|
|
482
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
483
|
+
}, z.core.$strict>>;
|
|
484
|
+
accounts: z.ZodOptional<z.ZodObject<{
|
|
485
|
+
dormantAfterDays: z.ZodDefault<z.ZodNumber>;
|
|
486
|
+
scanIntervalSeconds: z.ZodDefault<z.ZodNumber>;
|
|
487
|
+
}, z.core.$strip>>;
|
|
488
|
+
ui: z.ZodOptional<z.ZodObject<{
|
|
489
|
+
variant: z.ZodDefault<z.ZodEnum<{
|
|
490
|
+
card: "card";
|
|
491
|
+
split: "split";
|
|
492
|
+
}>>;
|
|
493
|
+
title: z.ZodOptional<z.ZodString>;
|
|
494
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
495
|
+
brandTitle: z.ZodOptional<z.ZodString>;
|
|
496
|
+
brandText: z.ZodOptional<z.ZodString>;
|
|
497
|
+
logoUrl: z.ZodOptional<z.ZodString>;
|
|
498
|
+
illustrationUrl: z.ZodOptional<z.ZodString>;
|
|
499
|
+
}, z.core.$strip>>;
|
|
500
|
+
password: z.ZodOptional<z.ZodObject<{
|
|
501
|
+
minLength: z.ZodDefault<z.ZodNumber>;
|
|
502
|
+
maxLength: z.ZodDefault<z.ZodNumber>;
|
|
503
|
+
requireUppercase: z.ZodDefault<z.ZodBoolean>;
|
|
504
|
+
requireLowercase: z.ZodDefault<z.ZodBoolean>;
|
|
505
|
+
requireNumber: z.ZodDefault<z.ZodBoolean>;
|
|
506
|
+
requireSpecialChar: z.ZodDefault<z.ZodBoolean>;
|
|
507
|
+
minCharacterClasses: z.ZodDefault<z.ZodNumber>;
|
|
508
|
+
expirationDays: z.ZodDefault<z.ZodNumber>;
|
|
509
|
+
preventSamePassword: z.ZodDefault<z.ZodBoolean>;
|
|
510
|
+
requireInitialChange: z.ZodDefault<z.ZodBoolean>;
|
|
511
|
+
}, z.core.$strip>>;
|
|
512
|
+
otp: z.ZodOptional<z.ZodObject<{
|
|
513
|
+
email: z.ZodDefault<z.ZodBoolean>;
|
|
514
|
+
sms: z.ZodDefault<z.ZodBoolean>;
|
|
515
|
+
length: z.ZodDefault<z.ZodNumber>;
|
|
516
|
+
expiresIn: z.ZodDefault<z.ZodNumber>;
|
|
517
|
+
maxAttempts: z.ZodDefault<z.ZodNumber>;
|
|
518
|
+
resendInterval: z.ZodDefault<z.ZodNumber>;
|
|
519
|
+
}, z.core.$strip>>;
|
|
520
|
+
ldap: z.ZodOptional<z.ZodObject<{
|
|
521
|
+
url: z.ZodURL;
|
|
522
|
+
bindDn: z.ZodString;
|
|
523
|
+
bindPassword: z.ZodString;
|
|
524
|
+
searchBase: z.ZodString;
|
|
525
|
+
searchFilter: z.ZodDefault<z.ZodString>;
|
|
526
|
+
usernameAttribute: z.ZodDefault<z.ZodString>;
|
|
527
|
+
emailAttribute: z.ZodDefault<z.ZodString>;
|
|
528
|
+
displayNameAttribute: z.ZodDefault<z.ZodString>;
|
|
529
|
+
useTls: z.ZodDefault<z.ZodBoolean>;
|
|
530
|
+
connectTimeout: z.ZodDefault<z.ZodNumber>;
|
|
531
|
+
}, z.core.$strip>>;
|
|
532
|
+
apikey: z.ZodOptional<z.ZodObject<{
|
|
533
|
+
maxKeysPerUser: z.ZodDefault<z.ZodNumber>;
|
|
534
|
+
defaultExpirationDays: z.ZodDefault<z.ZodNumber>;
|
|
535
|
+
prefix: z.ZodDefault<z.ZodString>;
|
|
536
|
+
}, z.core.$strip>>;
|
|
537
|
+
passwordReset: z.ZodOptional<z.ZodObject<{
|
|
538
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
539
|
+
email: "email";
|
|
540
|
+
phone: "phone";
|
|
541
|
+
}>>;
|
|
542
|
+
resetUrl: z.ZodOptional<z.ZodURL>;
|
|
543
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
544
|
+
tokenExpiresIn: z.ZodDefault<z.ZodNumber>;
|
|
545
|
+
maxAttempts: z.ZodDefault<z.ZodNumber>;
|
|
546
|
+
}, z.core.$strip>>;
|
|
547
|
+
login: z.ZodDefault<z.ZodObject<{
|
|
548
|
+
password: z.ZodDefault<z.ZodBoolean>;
|
|
549
|
+
otp: z.ZodDefault<z.ZodBoolean>;
|
|
550
|
+
ldap: z.ZodDefault<z.ZodBoolean>;
|
|
551
|
+
apikey: z.ZodDefault<z.ZodBoolean>;
|
|
552
|
+
}, z.core.$strip>>;
|
|
553
|
+
register: z.ZodDefault<z.ZodObject<{
|
|
554
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
555
|
+
defaultAccountStatus: z.ZodDefault<z.ZodEnum<{
|
|
556
|
+
active: "active";
|
|
557
|
+
dormant: "dormant";
|
|
558
|
+
}>>;
|
|
559
|
+
}, z.core.$strict>>;
|
|
560
|
+
agreements: z.ZodDefault<z.ZodObject<{
|
|
561
|
+
userAgreementMarkdown: z.ZodOptional<z.ZodString>;
|
|
562
|
+
privacyPolicyMarkdown: z.ZodOptional<z.ZodString>;
|
|
563
|
+
userAgreementUrl: z.ZodOptional<z.ZodURL>;
|
|
564
|
+
privacyPolicyUrl: z.ZodOptional<z.ZodURL>;
|
|
565
|
+
showOnRegister: z.ZodDefault<z.ZodBoolean>;
|
|
566
|
+
showOnLogin: z.ZodDefault<z.ZodBoolean>;
|
|
567
|
+
}, z.core.$strip>>;
|
|
568
|
+
captcha: z.ZodDefault<z.ZodObject<{
|
|
569
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
570
|
+
ttlSeconds: z.ZodDefault<z.ZodNumber>;
|
|
571
|
+
cost: z.ZodDefault<z.ZodNumber>;
|
|
572
|
+
maxCounter: z.ZodDefault<z.ZodNumber>;
|
|
573
|
+
}, z.core.$strip>>;
|
|
574
|
+
security: z.ZodDefault<z.ZodObject<{
|
|
575
|
+
lockoutEnabled: z.ZodDefault<z.ZodBoolean>;
|
|
576
|
+
failureWindow: z.ZodDefault<z.ZodNumber>;
|
|
577
|
+
maxLoginAttempts: z.ZodDefault<z.ZodNumber>;
|
|
578
|
+
lockoutDuration: z.ZodDefault<z.ZodNumber>;
|
|
579
|
+
}, z.core.$strip>>;
|
|
580
|
+
session: z.ZodOptional<z.ZodObject<{
|
|
581
|
+
maxAge: z.ZodDefault<z.ZodNumber>;
|
|
582
|
+
endOnPageHide: z.ZodDefault<z.ZodBoolean>;
|
|
583
|
+
sliding: z.ZodDefault<z.ZodBoolean>;
|
|
584
|
+
singleDevice: z.ZodDefault<z.ZodBoolean>;
|
|
585
|
+
singleDeviceMode: z.ZodDefault<z.ZodEnum<{
|
|
586
|
+
replace: "replace";
|
|
587
|
+
reject: "reject";
|
|
588
|
+
}>>;
|
|
589
|
+
maxSessions: z.ZodDefault<z.ZodNumber>;
|
|
590
|
+
terminalLimits: z.ZodDefault<z.ZodRecord<z.ZodEnum<{
|
|
591
|
+
web: "web";
|
|
592
|
+
app: "app";
|
|
593
|
+
desktop: "desktop";
|
|
594
|
+
api: "api";
|
|
595
|
+
}> & z.core.$partial, z.ZodNumber>>;
|
|
596
|
+
terminalLimitMode: z.ZodDefault<z.ZodEnum<{
|
|
597
|
+
replace: "replace";
|
|
598
|
+
reject: "reject";
|
|
599
|
+
}>>;
|
|
600
|
+
idleTimeout: z.ZodDefault<z.ZodNumber>;
|
|
601
|
+
absoluteTimeout: z.ZodDefault<z.ZodNumber>;
|
|
602
|
+
refreshTokenMaxAge: z.ZodDefault<z.ZodNumber>;
|
|
603
|
+
}, z.core.$strip>>;
|
|
604
|
+
rbac: z.ZodDefault<z.ZodObject<{
|
|
605
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
606
|
+
superAdminRole: z.ZodDefault<z.ZodString>;
|
|
607
|
+
defaultRole: z.ZodDefault<z.ZodString>;
|
|
608
|
+
}, z.core.$strip>>;
|
|
609
|
+
seedDefaultData: z.ZodDefault<z.ZodBoolean>;
|
|
610
|
+
}, z.core.$strip>;
|
|
611
|
+
/**
|
|
612
|
+
* IAM 配置类型
|
|
613
|
+
* @example
|
|
614
|
+
* ```ts
|
|
615
|
+
* import { IamConfigSchema, type IamConfig } from '@h-ai/iam'
|
|
616
|
+
* const value: IamConfig = IamConfigSchema.parse({ seedDefaultData: false, session: { maxAge: 3600 } })
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
619
|
+
type IamConfig = z.infer<typeof IamConfigSchema>;
|
|
620
|
+
/**
|
|
621
|
+
* IAM 配置设置输入类型(仅设置字段,不含运行时依赖)
|
|
622
|
+
*
|
|
623
|
+
* 说明:Zod 的 default 会让输入端字段可省略,但输出端字段为必填。
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* import type { IamConfigSettingsInput } from '@h-ai/iam'
|
|
627
|
+
* const value: IamConfigSettingsInput = { session: { maxAge: 3600 }, seedDefaultData: false }
|
|
628
|
+
* ```
|
|
629
|
+
*/
|
|
630
|
+
type IamConfigSettingsInput = z.input<typeof IamConfigSchema>;
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* @h-ai/iam — API Key 类型定义
|
|
634
|
+
*
|
|
635
|
+
* 包含 API Key 实体、创建选项、查询选项等类型。
|
|
636
|
+
* @module iam-authn-apikey-types
|
|
637
|
+
*/
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* API Key 实体(对外展示)
|
|
641
|
+
*
|
|
642
|
+
* 不含密钥哈希等敏感信息。
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* import { iam, type ApiKey } from '@h-ai/iam'
|
|
646
|
+
* // 已初始化依赖和 IAM,并启用 login.apikey。
|
|
647
|
+
* const result = await iam.apiKey.getApiKey('key-1')
|
|
648
|
+
* if (result.success && result.data) {
|
|
649
|
+
* const key: ApiKey = result.data
|
|
650
|
+
* key.keyPrefix // 展示前缀,不包含 rawKey 或哈希。
|
|
651
|
+
* }
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
interface ApiKey {
|
|
655
|
+
/** API Key ID */
|
|
656
|
+
id: string;
|
|
657
|
+
/** 所属用户 ID */
|
|
658
|
+
userId: string;
|
|
659
|
+
/** API Key 名称(用户自定义标识) */
|
|
660
|
+
name: string;
|
|
661
|
+
/** 密钥前缀(用于展示,如 'hai_abc1****') */
|
|
662
|
+
keyPrefix: string;
|
|
663
|
+
/** 是否启用 */
|
|
664
|
+
enabled: boolean;
|
|
665
|
+
/** 过期时间(null 表示永不过期) */
|
|
666
|
+
expiresAt: Date | null;
|
|
667
|
+
/** 创建时间 */
|
|
668
|
+
createdAt: Date;
|
|
669
|
+
/** 最后使用时间 */
|
|
670
|
+
lastUsedAt: Date | null;
|
|
671
|
+
/** 权限代码范围,支持 * / 命名空间 :*;空数组无操作权限,* 不超出主体权限。 */
|
|
672
|
+
scopes: string[];
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* 创建 API Key 的返回结果
|
|
676
|
+
*
|
|
677
|
+
* 仅在创建时返回明文密钥,之后无法再获取。
|
|
678
|
+
* @example
|
|
679
|
+
* ```ts
|
|
680
|
+
* import { iam, type CreateApiKeyResult } from '@h-ai/iam'
|
|
681
|
+
* // 已初始化依赖和 IAM、启用 login.apikey;user-1 为已存在的用户。
|
|
682
|
+
* const created = await iam.apiKey.createApiKey('user-1', { name: 'report-reader', scopes: ['report:read'] })
|
|
683
|
+
* if (created.success) {
|
|
684
|
+
* const value: CreateApiKeyResult = created.data
|
|
685
|
+
* const oneTimeSecret = value.rawKey // 仅本次交给调用者安全保存,不能写日志。
|
|
686
|
+
* }
|
|
687
|
+
* ```
|
|
688
|
+
*/
|
|
689
|
+
interface CreateApiKeyResult {
|
|
690
|
+
/** API Key 元数据 */
|
|
691
|
+
apiKey: ApiKey;
|
|
692
|
+
/** 明文密钥(仅此一次展示) */
|
|
693
|
+
rawKey: string;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* 创建 API Key 选项
|
|
697
|
+
* @example
|
|
698
|
+
* ```ts
|
|
699
|
+
* import type { CreateApiKeyOptions } from '@h-ai/iam'
|
|
700
|
+
* const value: CreateApiKeyOptions = { name: 'report-reader', expirationDays: 30, scopes: ['report:read'] }
|
|
701
|
+
* ```
|
|
702
|
+
*/
|
|
703
|
+
interface CreateApiKeyOptions {
|
|
704
|
+
/** API Key 名称 */
|
|
705
|
+
name: string;
|
|
706
|
+
/** 有效期天数(0 或不传表示永不过期) */
|
|
707
|
+
expirationDays?: number;
|
|
708
|
+
/** 权限代码范围;默认空数组。受限 key 不保留角色授权入口,使用 permission 守卫。 */
|
|
709
|
+
scopes?: string[];
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* API Key 列表允许的服务端排序字段。
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* import type { ApiKeySortField } from '@h-ai/iam'
|
|
716
|
+
* const value: ApiKeySortField = 'createdAt'
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
type ApiKeySortField = 'name' | 'createdAt' | 'lastUsedAt' | 'expiresAt';
|
|
720
|
+
/**
|
|
721
|
+
* API Key 列表排序方向。
|
|
722
|
+
* @example
|
|
723
|
+
* ```ts
|
|
724
|
+
* import type { ApiKeySortDirection } from '@h-ai/iam'
|
|
725
|
+
* const value: ApiKeySortDirection = 'desc'
|
|
726
|
+
* ```
|
|
727
|
+
*/
|
|
728
|
+
type ApiKeySortDirection = 'asc' | 'desc';
|
|
729
|
+
/**
|
|
730
|
+
* API Key 列表查询选项
|
|
731
|
+
* @example
|
|
732
|
+
* ```ts
|
|
733
|
+
* import type { ListApiKeysOptions } from '@h-ai/iam'
|
|
734
|
+
* const value: ListApiKeysOptions = { page: 1, pageSize: 20, enabled: true, sortBy: 'createdAt', sortDirection: 'desc' }
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
interface ListApiKeysOptions extends PaginationOptionsInput {
|
|
738
|
+
/** 搜索关键字(模糊匹配名称与密钥前缀) */
|
|
739
|
+
search?: string;
|
|
740
|
+
/** 按启用状态过滤,不传则返回全部 */
|
|
741
|
+
enabled?: boolean;
|
|
742
|
+
/** 服务端排序字段;未指定时保持按创建时间倒序的历史行为。 */
|
|
743
|
+
sortBy?: ApiKeySortField;
|
|
744
|
+
/** 服务端排序方向;仅在指定 sortBy 时生效,默认 desc。 */
|
|
745
|
+
sortDirection?: ApiKeySortDirection;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* API Key 管理操作接口
|
|
749
|
+
* @example
|
|
750
|
+
* ```ts
|
|
751
|
+
* import { iam, type ApiKeyOperations } from '@h-ai/iam'
|
|
752
|
+
* // 应用已完成 RelDB、Cache、Crypto 和 IAM 初始化,并启用 login.apikey。
|
|
753
|
+
* const operations: ApiKeyOperations = iam.apiKey
|
|
754
|
+
* const result = await operations.listApiKeys('user-1', { page: 1, pageSize: 20 })
|
|
755
|
+
* if (!result.success) {
|
|
756
|
+
* result.error.code // 调用方将失败交给统一错误边界。
|
|
757
|
+
* }
|
|
758
|
+
* else {
|
|
759
|
+
* result.data
|
|
760
|
+
* }
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
interface ApiKeyOperations {
|
|
764
|
+
/**
|
|
765
|
+
* 创建 API Key
|
|
766
|
+
*
|
|
767
|
+
* @param userId - 用户 ID
|
|
768
|
+
* @param options - 创建选项
|
|
769
|
+
* @returns 成功返回 API Key 元数据和明文密钥
|
|
770
|
+
*/
|
|
771
|
+
createApiKey: (userId: string, options: CreateApiKeyOptions) => Promise<HaiResult<CreateApiKeyResult>>;
|
|
772
|
+
/**
|
|
773
|
+
* 分页列出用户的 API Key
|
|
774
|
+
*
|
|
775
|
+
* @param userId - 用户 ID
|
|
776
|
+
* @param options - 分页、搜索、过滤与排序选项
|
|
777
|
+
* @returns 分页 API Key 列表(不含密钥哈希)
|
|
778
|
+
*/
|
|
779
|
+
listApiKeys: (userId: string, options?: ListApiKeysOptions) => Promise<HaiResult<PaginatedResult<ApiKey>>>;
|
|
780
|
+
/**
|
|
781
|
+
* 获取 API Key 详情
|
|
782
|
+
*
|
|
783
|
+
* @param keyId - API Key ID
|
|
784
|
+
* @returns API Key 详情
|
|
785
|
+
*/
|
|
786
|
+
getApiKey: (keyId: string) => Promise<HaiResult<ApiKey | null>>;
|
|
787
|
+
/**
|
|
788
|
+
* 吊销/删除 API Key
|
|
789
|
+
*
|
|
790
|
+
* @param keyId - API Key ID
|
|
791
|
+
* @returns 操作结果
|
|
792
|
+
*/
|
|
793
|
+
revokeApiKey: (keyId: string) => Promise<HaiResult<void>>;
|
|
794
|
+
/**
|
|
795
|
+
* 验证 API Key 并返回关联用户 ID
|
|
796
|
+
*
|
|
797
|
+
* @param rawKey - 明文 API Key
|
|
798
|
+
* @returns 成功返回 API Key 实体(含用户 ID),失败返回错误码
|
|
799
|
+
*/
|
|
800
|
+
verifyApiKey: (rawKey: string) => Promise<HaiResult<ApiKey>>;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* @h-ai/iam — 授权子功能类型定义(RBAC)
|
|
805
|
+
*
|
|
806
|
+
* @module iam-authz-types
|
|
807
|
+
*/
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* 权限类型
|
|
811
|
+
*
|
|
812
|
+
* - menu:菜单/导航可见性控制
|
|
813
|
+
* - api:接口级访问控制
|
|
814
|
+
* - button:操作按钮显隐控制
|
|
815
|
+
* @example
|
|
816
|
+
* ```ts
|
|
817
|
+
* import type { PermissionType } from '@h-ai/iam'
|
|
818
|
+
* const value: PermissionType = 'api'
|
|
819
|
+
* ```
|
|
820
|
+
*/
|
|
821
|
+
type PermissionType = 'menu' | 'api' | 'button';
|
|
822
|
+
/**
|
|
823
|
+
* 权限查询参数
|
|
824
|
+
*
|
|
825
|
+
* 扩展分页参数,支持按类型和关键字筛选。
|
|
826
|
+
* @example
|
|
827
|
+
* ```ts
|
|
828
|
+
* import type { PermissionQueryOptions } from '@h-ai/iam'
|
|
829
|
+
* const value: PermissionQueryOptions = { page: 1, pageSize: 20, type: 'api', search: 'report' }
|
|
830
|
+
* ```
|
|
831
|
+
*/
|
|
832
|
+
interface PermissionQueryOptions extends PaginationOptionsInput {
|
|
833
|
+
/** 按权限类型筛选 */
|
|
834
|
+
type?: PermissionType;
|
|
835
|
+
/** 按关键字搜索(匹配 code 或 name) */
|
|
836
|
+
search?: string;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* 权限
|
|
840
|
+
* @example
|
|
841
|
+
* ```ts
|
|
842
|
+
* import type { Permission } from '@h-ai/iam'
|
|
843
|
+
* const value: Permission = { id: 'permission-1', code: 'report:read', name: '查看报表', type: 'api', createdAt: new Date(), updatedAt: new Date() }
|
|
844
|
+
* ```
|
|
845
|
+
*/
|
|
846
|
+
interface Permission {
|
|
847
|
+
/** 此资源权限要求二次认证的操作ID;与文件清单取并集,空或null表示不追加。 */
|
|
848
|
+
sensitiveOperations?: string[] | null;
|
|
849
|
+
/** 权限 ID */
|
|
850
|
+
id: string;
|
|
851
|
+
/** 权限代码(如 users:read, posts:write) */
|
|
852
|
+
code: string;
|
|
853
|
+
/** 权限名称 */
|
|
854
|
+
name: string;
|
|
855
|
+
/** 权限描述 */
|
|
856
|
+
description?: string;
|
|
857
|
+
/** 权限类型(menu / api / button) */
|
|
858
|
+
type?: PermissionType;
|
|
859
|
+
/** 资源类型 */
|
|
860
|
+
resource?: string;
|
|
861
|
+
/** 操作类型(create/read/update/delete) */
|
|
862
|
+
action?: string;
|
|
863
|
+
/** 创建时间 */
|
|
864
|
+
createdAt: Date;
|
|
865
|
+
/** 更新时间 */
|
|
866
|
+
updatedAt: Date;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* 角色
|
|
870
|
+
* @example
|
|
871
|
+
* ```ts
|
|
872
|
+
* import type { Role } from '@h-ai/iam'
|
|
873
|
+
* const value: Role = { id: 'role-1', code: 'report_reader', name: '报表读者', createdAt: new Date(), updatedAt: new Date() }
|
|
874
|
+
* ```
|
|
875
|
+
*/
|
|
876
|
+
interface Role {
|
|
877
|
+
/** 角色 ID */
|
|
878
|
+
id: string;
|
|
879
|
+
/** 角色代码 */
|
|
880
|
+
code: string;
|
|
881
|
+
/** 角色名称 */
|
|
882
|
+
name: string;
|
|
883
|
+
/** 角色描述 */
|
|
884
|
+
description?: string;
|
|
885
|
+
/** 是否系统角色(不可删除) */
|
|
886
|
+
isSystem?: boolean;
|
|
887
|
+
/** 创建时间 */
|
|
888
|
+
createdAt: Date;
|
|
889
|
+
/** 更新时间 */
|
|
890
|
+
updatedAt: Date;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* 授权子功能接口
|
|
894
|
+
* @example
|
|
895
|
+
* ```ts
|
|
896
|
+
* import { iam, type AuthzOperations } from '@h-ai/iam'
|
|
897
|
+
* // 应用已完成 RelDB、Cache、Crypto 和 IAM 初始化。
|
|
898
|
+
* const operations: AuthzOperations = iam.authz
|
|
899
|
+
* const result = await operations.checkPermission('user-1', 'report:read')
|
|
900
|
+
* if (!result.success) {
|
|
901
|
+
* result.error.code // 调用方将失败交给统一错误边界。
|
|
902
|
+
* }
|
|
903
|
+
* else {
|
|
904
|
+
* result.data
|
|
905
|
+
* }
|
|
906
|
+
* ```
|
|
907
|
+
*/
|
|
908
|
+
interface AuthzOperations {
|
|
909
|
+
/** 实时读取RBAC敏感操作;可仅查询一个操作,未传则返回全部。RBAC关闭时为空,读取失败须拒绝。 */
|
|
910
|
+
getSensitiveOperations: (operation?: string) => Promise<HaiResult<string[]>>;
|
|
911
|
+
/** 设置资源权限的敏感操作清单;空数组清除数据库追加规则,不清除文件中的规则。 */
|
|
912
|
+
setPermissionSensitiveOperations: (permissionId: string, operations: string[]) => Promise<HaiResult<void>>;
|
|
913
|
+
/** 查询当前角色与权限代码,一次读取角色并批量聚合权限,不依赖会话缓存。 */
|
|
914
|
+
getUserAuthorization: (userId: string) => Promise<HaiResult<Pick<Session, 'roles' | 'permissions'>>>;
|
|
915
|
+
/**
|
|
916
|
+
* 检查权限
|
|
917
|
+
*
|
|
918
|
+
* 超级管理员角色自动拥有所有权限。
|
|
919
|
+
* 支持通配符匹配(如 `user:*` 匹配 `user:read`)。
|
|
920
|
+
*
|
|
921
|
+
* **数据来源**:共享缓存;IAM 写入口在提交前后推进代际,冷读与失效期间查询数据库。
|
|
922
|
+
*
|
|
923
|
+
* @param userId - 用户 ID
|
|
924
|
+
* @param permission - 权限代码(如 `user:read`)
|
|
925
|
+
* @returns 成功返回 true/false
|
|
926
|
+
*/
|
|
927
|
+
checkPermission: (userId: string, permission: string) => Promise<HaiResult<boolean>>;
|
|
928
|
+
/**
|
|
929
|
+
* 获取用户权限列表
|
|
930
|
+
*
|
|
931
|
+
* 通过用户角色聚合所有权限(自动去重)。
|
|
932
|
+
*
|
|
933
|
+
* **数据来源**:直接查询数据库,返回最新数据。
|
|
934
|
+
*
|
|
935
|
+
* @param userId - 用户 ID
|
|
936
|
+
* @returns 成功返回去重后的权限列表
|
|
937
|
+
*/
|
|
938
|
+
getUserPermissions: (userId: string) => Promise<HaiResult<Permission[]>>;
|
|
939
|
+
/**
|
|
940
|
+
* 获取用户角色列表
|
|
941
|
+
*
|
|
942
|
+
* **数据来源**:直接查询数据库,返回最新数据。
|
|
943
|
+
*
|
|
944
|
+
* @param userId - 用户 ID
|
|
945
|
+
* @returns 成功返回角色列表
|
|
946
|
+
*/
|
|
947
|
+
getUserRoles: (userId: string) => Promise<HaiResult<Role[]>>;
|
|
948
|
+
/**
|
|
949
|
+
* 分配角色给用户
|
|
950
|
+
*
|
|
951
|
+
* @param userId - 用户 ID
|
|
952
|
+
* @param roleId - 角色 ID
|
|
953
|
+
* @returns 成功返回 ok;角色不存在返回 ROLE_NOT_FOUND
|
|
954
|
+
*/
|
|
955
|
+
assignRole: (userId: string, roleId: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
956
|
+
/**
|
|
957
|
+
* 移除用户角色
|
|
958
|
+
*
|
|
959
|
+
* @param userId - 用户 ID
|
|
960
|
+
* @param roleId - 角色 ID
|
|
961
|
+
* @returns 成功返回 ok
|
|
962
|
+
*/
|
|
963
|
+
removeRole: (userId: string, roleId: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
964
|
+
/**
|
|
965
|
+
* 同步用户角色(替换为目标角色列表)
|
|
966
|
+
*
|
|
967
|
+
* 自动计算当前角色与目标角色的差集,批量移除多余角色、添加缺失角色,
|
|
968
|
+
* 最终同步一次会话权限。若目标与当前一致则跳过操作。
|
|
969
|
+
*
|
|
970
|
+
* @param userId - 用户 ID
|
|
971
|
+
* @param roleIds - 目标角色 ID 列表
|
|
972
|
+
* @returns 成功返回 ok
|
|
973
|
+
*/
|
|
974
|
+
syncRoles: (userId: string, roleIds: string[], tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
975
|
+
/**
|
|
976
|
+
* 创建角色
|
|
977
|
+
*
|
|
978
|
+
* @param role - 角色数据(code、name、description、isSystem)
|
|
979
|
+
* @returns 成功返回创建的角色(含 id 和时间戳)
|
|
980
|
+
*/
|
|
981
|
+
createRole: (role: Omit<Role, 'id' | 'createdAt' | 'updatedAt'> & {
|
|
982
|
+
permissionIds?: string[];
|
|
983
|
+
}, tx?: DmlWithTxOperations) => Promise<HaiResult<Role>>;
|
|
984
|
+
/**
|
|
985
|
+
* 获取角色
|
|
986
|
+
*
|
|
987
|
+
* @param roleId - 角色 ID
|
|
988
|
+
* @returns 成功返回角色或 null(不存在时)
|
|
989
|
+
*/
|
|
990
|
+
getRole: (roleId: string) => Promise<HaiResult<Role | null>>;
|
|
991
|
+
/**
|
|
992
|
+
* 根据角色代码获取角色
|
|
993
|
+
*
|
|
994
|
+
* @param code - 角色代码(如 'admin'、'user')
|
|
995
|
+
* @returns 成功返回角色或 null(不存在时)
|
|
996
|
+
*/
|
|
997
|
+
getRoleByCode: (code: string) => Promise<HaiResult<Role | null>>;
|
|
998
|
+
/**
|
|
999
|
+
* 获取所有角色(分页)
|
|
1000
|
+
*
|
|
1001
|
+
* @param options - 分页参数,可选
|
|
1002
|
+
* @returns 成功返回分页角色列表
|
|
1003
|
+
*/
|
|
1004
|
+
getAllRoles: (options?: PaginationOptionsInput) => Promise<HaiResult<PaginatedResult<Role>>>;
|
|
1005
|
+
/** 批量统计角色成员数;成功时无关联返回 0,查询失败返回错误 */
|
|
1006
|
+
getRoleUserCounts: (roleIds: string[]) => Promise<HaiResult<Map<string, number>>>;
|
|
1007
|
+
/**
|
|
1008
|
+
* 更新角色
|
|
1009
|
+
*
|
|
1010
|
+
* @param roleId - 角色 ID
|
|
1011
|
+
* @param data - 要更新的字段;permissionIds 在同一事务内全量替换,未传则保持权限不变
|
|
1012
|
+
* @returns 成功返回更新后的角色;角色不存在返回 ROLE_NOT_FOUND
|
|
1013
|
+
*/
|
|
1014
|
+
updateRole: (roleId: string, data: Partial<Omit<Role, 'id' | 'createdAt' | 'updatedAt'>> & {
|
|
1015
|
+
permissionIds?: string[];
|
|
1016
|
+
}, tx?: DmlWithTxOperations) => Promise<HaiResult<Role>>;
|
|
1017
|
+
/**
|
|
1018
|
+
* 删除角色
|
|
1019
|
+
*
|
|
1020
|
+
* 级联删除用户-角色、角色-权限关联,并同步受影响用户的会话。
|
|
1021
|
+
*
|
|
1022
|
+
* @param roleId - 角色 ID
|
|
1023
|
+
* @returns 成功返回 ok;角色不存在返回 ROLE_NOT_FOUND
|
|
1024
|
+
*/
|
|
1025
|
+
deleteRole: (roleId: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
1026
|
+
/**
|
|
1027
|
+
* 创建权限
|
|
1028
|
+
*
|
|
1029
|
+
* @param permission - 权限数据(code、name、resource、action)
|
|
1030
|
+
* @returns 成功返回创建的权限(含 id 和时间戳)
|
|
1031
|
+
*/
|
|
1032
|
+
createPermission: (permission: Omit<Permission, 'id' | 'createdAt' | 'updatedAt'>, tx?: DmlWithTxOperations) => Promise<HaiResult<Permission>>;
|
|
1033
|
+
/**
|
|
1034
|
+
* 获取权限
|
|
1035
|
+
*
|
|
1036
|
+
* @param permissionId - 权限 ID
|
|
1037
|
+
* @returns 成功返回权限或 null(不存在时)
|
|
1038
|
+
*/
|
|
1039
|
+
getPermission: (permissionId: string) => Promise<HaiResult<Permission | null>>;
|
|
1040
|
+
/**
|
|
1041
|
+
* 根据权限代码获取权限
|
|
1042
|
+
*
|
|
1043
|
+
* @param code - 权限代码(如 `user:read`)
|
|
1044
|
+
* @returns 成功返回权限或 null(不存在时)
|
|
1045
|
+
*/
|
|
1046
|
+
getPermissionByCode: (code: string) => Promise<HaiResult<Permission | null>>;
|
|
1047
|
+
/**
|
|
1048
|
+
* 获取所有权限(分页)
|
|
1049
|
+
*
|
|
1050
|
+
* 支持按权限类型(menu/api/button)和关键字(code/name)筛选。
|
|
1051
|
+
*
|
|
1052
|
+
* @param options - 分页及筛选参数,可选
|
|
1053
|
+
* @returns 成功返回分页权限列表
|
|
1054
|
+
*/
|
|
1055
|
+
getAllPermissions: (options?: PermissionQueryOptions) => Promise<HaiResult<PaginatedResult<Permission>>>;
|
|
1056
|
+
/**
|
|
1057
|
+
* 删除权限
|
|
1058
|
+
*
|
|
1059
|
+
* 级联删除角色-权限关联,并同步受影响用户的会话。
|
|
1060
|
+
*
|
|
1061
|
+
* @param permissionId - 权限 ID
|
|
1062
|
+
* @returns 成功返回 ok;权限不存在返回 PERMISSION_NOT_FOUND
|
|
1063
|
+
*/
|
|
1064
|
+
deletePermission: (permissionId: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
1065
|
+
/**
|
|
1066
|
+
* 为角色分配权限
|
|
1067
|
+
*
|
|
1068
|
+
* @param roleId - 角色 ID
|
|
1069
|
+
* @param permissionId - 权限 ID
|
|
1070
|
+
* @returns 成功返回 ok;角色/权限不存在返回对应错误码
|
|
1071
|
+
*/
|
|
1072
|
+
assignPermissionToRole: (roleId: string, permissionId: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
1073
|
+
/**
|
|
1074
|
+
* 移除角色权限
|
|
1075
|
+
*
|
|
1076
|
+
* @param roleId - 角色 ID
|
|
1077
|
+
* @param permissionId - 权限 ID
|
|
1078
|
+
* @returns 成功返回 ok;权限不存在返回 PERMISSION_NOT_FOUND
|
|
1079
|
+
*/
|
|
1080
|
+
removePermissionFromRole: (roleId: string, permissionId: string, tx?: DmlWithTxOperations) => Promise<HaiResult<void>>;
|
|
1081
|
+
/**
|
|
1082
|
+
* 获取角色的权限列表
|
|
1083
|
+
*
|
|
1084
|
+
* **数据来源**:直接查询数据库,返回最新数据。
|
|
1085
|
+
*
|
|
1086
|
+
* @param roleId - 角色 ID
|
|
1087
|
+
* @returns 成功返回角色关联的权限列表
|
|
1088
|
+
*/
|
|
1089
|
+
getRolePermissions: (roleId: string) => Promise<HaiResult<Permission[]>>;
|
|
1090
|
+
/**
|
|
1091
|
+
* 批量获取多个用户的角色列表
|
|
1092
|
+
*
|
|
1093
|
+
* 单次查询替代 N 次 getUserRoles 调用,避免 N+1 问题。
|
|
1094
|
+
* 返回 Map:key 为 userId,value 为该用户的角色列表;无角色的用户返回空数组。
|
|
1095
|
+
*
|
|
1096
|
+
* **数据来源**:直接查询数据库,返回最新数据。
|
|
1097
|
+
*
|
|
1098
|
+
* @param userIds - 用户 ID 列表
|
|
1099
|
+
* @returns Map<userId, Role[]>
|
|
1100
|
+
*/
|
|
1101
|
+
getUserRolesForMany: (userIds: string[]) => Promise<HaiResult<Map<string, Role[]>>>;
|
|
1102
|
+
/**
|
|
1103
|
+
* 批量获取多个角色的权限列表
|
|
1104
|
+
*
|
|
1105
|
+
* 单次查询替代 N 次 getRolePermissions 调用,避免 N+1 问题。
|
|
1106
|
+
* 返回 Map:key 为 roleId,value 为该角色的权限列表;无权限的角色返回空数组。
|
|
1107
|
+
*
|
|
1108
|
+
* **数据来源**:直接查询数据库,返回最新数据。
|
|
1109
|
+
*
|
|
1110
|
+
* @param roleIds - 角色 ID 列表
|
|
1111
|
+
* @returns Map<roleId, Permission[]>
|
|
1112
|
+
*/
|
|
1113
|
+
getRolePermissionsForMany: (roleIds: string[]) => Promise<HaiResult<Map<string, Permission[]>>>;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* @h-ai/iam — 用户相关类型定义
|
|
1118
|
+
*
|
|
1119
|
+
* 包含: - 用户基础类型(User、StoredUser) - 注册选项(RegisterOptions) - 协议展示类型(AgreementDisplay) - 用户操作接口(UserOperations)
|
|
1120
|
+
* @module iam-user-types
|
|
1121
|
+
*/
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* 用户列表允许的服务端排序字段。
|
|
1125
|
+
* @example
|
|
1126
|
+
* ```ts
|
|
1127
|
+
* import type { UserSortField } from '@h-ai/iam'
|
|
1128
|
+
* const value: UserSortField = 'username'
|
|
1129
|
+
* ```
|
|
1130
|
+
*/
|
|
1131
|
+
type UserSortField = 'username' | 'email' | 'displayName' | 'accountStatus' | 'createdAt';
|
|
1132
|
+
/**
|
|
1133
|
+
* 用户列表排序方向。
|
|
1134
|
+
* @example
|
|
1135
|
+
* ```ts
|
|
1136
|
+
* import type { UserSortDirection } from '@h-ai/iam'
|
|
1137
|
+
* const value: UserSortDirection = 'asc'
|
|
1138
|
+
* ```
|
|
1139
|
+
*/
|
|
1140
|
+
type UserSortDirection = 'asc' | 'desc';
|
|
1141
|
+
/**
|
|
1142
|
+
* 用户列表查询选项
|
|
1143
|
+
* @example
|
|
1144
|
+
* ```ts
|
|
1145
|
+
* import type { ListUsersOptions } from '@h-ai/iam'
|
|
1146
|
+
* const value: ListUsersOptions = { page: 1, pageSize: 20, accountStatus: 'active', roleCode: 'user', include: ['roles'] }
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
1149
|
+
interface ListUsersOptions extends PaginationOptionsInput {
|
|
1150
|
+
/** 角色 code 精确筛选,在数据库分页前执行。 */
|
|
1151
|
+
roleCode?: string;
|
|
1152
|
+
/** 搜索关键字(模糊匹配用户名、邮箱、手机号、显示名称) */
|
|
1153
|
+
search?: string;
|
|
1154
|
+
/** 按账号生命周期过滤,不传则返回全部。 */
|
|
1155
|
+
accountStatus?: User['accountStatus'];
|
|
1156
|
+
/** 服务端排序字段;未指定时保持按创建时间倒序的历史行为。 */
|
|
1157
|
+
sortBy?: UserSortField;
|
|
1158
|
+
/** 服务端排序方向;仅在指定 sortBy 时生效,默认 desc。 */
|
|
1159
|
+
sortDirection?: UserSortDirection;
|
|
1160
|
+
/** 包含关联数据(如 'roles' 表示同时返回用户角色列表) */
|
|
1161
|
+
include?: ('roles')[];
|
|
1162
|
+
}
|
|
1163
|
+
/**
|
|
1164
|
+
* 用户基础信息
|
|
1165
|
+
* @example
|
|
1166
|
+
* ```ts
|
|
1167
|
+
* import type { User } from '@h-ai/iam'
|
|
1168
|
+
* const value: User = { id: 'user-1', username: 'alice', accountStatus: 'active', createdAt: new Date(), updatedAt: new Date() }
|
|
1169
|
+
* ```
|
|
1170
|
+
*/
|
|
1171
|
+
interface User {
|
|
1172
|
+
/** 用户 ID */
|
|
1173
|
+
id: string;
|
|
1174
|
+
/** 用户名 */
|
|
1175
|
+
username: string;
|
|
1176
|
+
/** 邮箱 */
|
|
1177
|
+
email?: string;
|
|
1178
|
+
/** 手机号 */
|
|
1179
|
+
phone?: string;
|
|
1180
|
+
/** 显示名称 */
|
|
1181
|
+
displayName?: string;
|
|
1182
|
+
/** 头像 URL */
|
|
1183
|
+
avatarUrl?: string;
|
|
1184
|
+
/** 账号类型;未设置时为长期账号。 */
|
|
1185
|
+
accountType?: 'permanent' | 'temporary';
|
|
1186
|
+
/** 唯一账号生命周期:active 可鉴别,dormant/closed 均拒绝;创建默认 active。 */
|
|
1187
|
+
accountStatus: 'active' | 'dormant' | 'closed';
|
|
1188
|
+
/** 临时账号到期时间;长期账号可为 null,临时账号必须设置。 */
|
|
1189
|
+
accountExpiresAt?: Date | null;
|
|
1190
|
+
/** 最近成功登录时间,失败尝试不更新。 */
|
|
1191
|
+
lastLoginAt?: Date | null;
|
|
1192
|
+
/** 初始凭据尚未更换;此时不能签发或使用普通业务会话。 */
|
|
1193
|
+
mustChangePassword?: boolean;
|
|
1194
|
+
/** 是否邮箱验证 */
|
|
1195
|
+
emailVerified?: boolean;
|
|
1196
|
+
/** 是否手机验证 */
|
|
1197
|
+
phoneVerified?: boolean;
|
|
1198
|
+
/** 创建时间 */
|
|
1199
|
+
createdAt: Date;
|
|
1200
|
+
/** 更新时间 */
|
|
1201
|
+
updatedAt: Date;
|
|
1202
|
+
/** 扩展属性 */
|
|
1203
|
+
metadata?: Record<string, unknown>;
|
|
1204
|
+
/** 用户角色列表(仅在 include: ['roles'] 时返回) */
|
|
1205
|
+
roles?: Role[];
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* 用户协议/隐私协议展示配置
|
|
1209
|
+
* @example
|
|
1210
|
+
* ```ts
|
|
1211
|
+
* import type { AgreementDisplay } from '@h-ai/iam'
|
|
1212
|
+
* const value: AgreementDisplay = { userAgreementUrl: 'https://app.example.com/terms', showOnRegister: true, showOnLogin: false }
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
1215
|
+
interface AgreementDisplay {
|
|
1216
|
+
/** 用户协议 Markdown 正文,显示优先于 URL。 */
|
|
1217
|
+
userAgreementMarkdown?: string;
|
|
1218
|
+
/** 隐私协议 Markdown 正文,显示优先于 URL。 */
|
|
1219
|
+
privacyPolicyMarkdown?: string;
|
|
1220
|
+
/** 用户协议 URL */
|
|
1221
|
+
userAgreementUrl?: string;
|
|
1222
|
+
/** 隐私协议 URL */
|
|
1223
|
+
privacyPolicyUrl?: string;
|
|
1224
|
+
/** 注册时展示协议 */
|
|
1225
|
+
showOnRegister: boolean;
|
|
1226
|
+
/** 登录时展示协议 */
|
|
1227
|
+
showOnLogin: boolean;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* 内部存储用户(包含密码哈希等敏感信息)
|
|
1231
|
+
* @example
|
|
1232
|
+
* ```ts
|
|
1233
|
+
* import type { StoredUser } from '@h-ai/iam'
|
|
1234
|
+
* // 服务端存储类型;不得直接序列化给浏览器。
|
|
1235
|
+
* function credentialVersion(row: StoredUser): string {
|
|
1236
|
+
* return row.credentialVersion ?? ''
|
|
1237
|
+
* }
|
|
1238
|
+
* ```
|
|
1239
|
+
*/
|
|
1240
|
+
interface StoredUser extends User {
|
|
1241
|
+
/** 密码凭据代际;密码提交时原子推进,旧会话及迟到认证不得复用。 */
|
|
1242
|
+
credentialVersion?: string;
|
|
1243
|
+
/** 密码重置操作的独立代际;发布/尝试推进,不影响现有登录。 */
|
|
1244
|
+
resetVersion?: string;
|
|
1245
|
+
/** 当前重置令牌的 SM3 摘要,仅内部存储。 */
|
|
1246
|
+
resetHash?: string;
|
|
1247
|
+
/** 重置令牌的 Unix 毫秒过期时间,由数据库时钟核验。 */
|
|
1248
|
+
resetExpiresAt?: number;
|
|
1249
|
+
/** 当前重置令牌已使用的尝试次数。 */
|
|
1250
|
+
resetAttempts?: number;
|
|
1251
|
+
/** 密码哈希 */
|
|
1252
|
+
passwordHash?: string;
|
|
1253
|
+
/** 密码更新时间 */
|
|
1254
|
+
passwordUpdatedAt?: Date;
|
|
1255
|
+
/** 登录失败次数 */
|
|
1256
|
+
loginFailedCount?: number;
|
|
1257
|
+
/** 当前固定失败计数窗口的起点。 */
|
|
1258
|
+
firstLoginFailedAt?: Date;
|
|
1259
|
+
/** 最后登录失败时间 */
|
|
1260
|
+
lastLoginFailedAt?: Date;
|
|
1261
|
+
/** 锁定截止时间 */
|
|
1262
|
+
lockedUntil?: Date;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* 用户注册选项
|
|
1266
|
+
* @example
|
|
1267
|
+
* ```ts
|
|
1268
|
+
* import type { RegisterOptions } from '@h-ai/iam'
|
|
1269
|
+
* const value: RegisterOptions = { username: 'alice', email: 'alice@example.com', password: 'ExampleOnly123!' }
|
|
1270
|
+
* ```
|
|
1271
|
+
*/
|
|
1272
|
+
interface RegisterOptions {
|
|
1273
|
+
/** 可选交互挑战证明;管理端 createUser/inviteUser 不使用。 */
|
|
1274
|
+
captcha?: string;
|
|
1275
|
+
/** 服务端管理入口可指定临时账号;公开注册接口不应允许修改生命周期字段。 */
|
|
1276
|
+
accountType?: 'permanent' | 'temporary';
|
|
1277
|
+
/** 管理入口指定初始状态;公开注册 Schema 不接受此字段。 */
|
|
1278
|
+
accountStatus?: 'active' | 'dormant' | 'closed';
|
|
1279
|
+
/** 临时账号的到期时间。 */
|
|
1280
|
+
accountExpiresAt?: Date;
|
|
1281
|
+
/** 用户名 */
|
|
1282
|
+
username: string;
|
|
1283
|
+
/** 邮箱 */
|
|
1284
|
+
email?: string;
|
|
1285
|
+
/** 手机号 */
|
|
1286
|
+
phone?: string;
|
|
1287
|
+
/** 密码 */
|
|
1288
|
+
password: string;
|
|
1289
|
+
/** 显示名称 */
|
|
1290
|
+
displayName?: string;
|
|
1291
|
+
/** 扩展属性 */
|
|
1292
|
+
metadata?: Record<string, unknown>;
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* 注册结果
|
|
1296
|
+
* @example
|
|
1297
|
+
* ```ts
|
|
1298
|
+
* import { iam, type RegisterResult } from '@h-ai/iam'
|
|
1299
|
+
* const result = await iam.user.register({ username: 'alice', password: 'ExampleOnly123!' })
|
|
1300
|
+
* if (result.success) {
|
|
1301
|
+
* const registered: RegisterResult = result.data
|
|
1302
|
+
* registered.user.id
|
|
1303
|
+
* }
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
interface RegisterResult {
|
|
1307
|
+
/** 用户信息 */
|
|
1308
|
+
user: User;
|
|
1309
|
+
/** 协议展示信息(可选) */
|
|
1310
|
+
agreements?: AgreementDisplay;
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* 当前登录用户可自行修改的字段白名单
|
|
1314
|
+
*
|
|
1315
|
+
* 仅允许修改个人资料相关字段,禁止修改安全字段(accountStatus、emailVerified、roles 等)。
|
|
1316
|
+
* @example
|
|
1317
|
+
* ```ts
|
|
1318
|
+
* import type { UpdateCurrentUserInput } from '@h-ai/iam'
|
|
1319
|
+
* const value: UpdateCurrentUserInput = { displayName: 'Alice', avatarUrl: 'https://app.example.com/avatar/alice.png' }
|
|
1320
|
+
* ```
|
|
1321
|
+
*/
|
|
1322
|
+
interface UpdateCurrentUserInput {
|
|
1323
|
+
/** 用户名 */
|
|
1324
|
+
username?: string;
|
|
1325
|
+
/** 邮箱 */
|
|
1326
|
+
email?: string;
|
|
1327
|
+
/** 显示名称 */
|
|
1328
|
+
displayName?: string;
|
|
1329
|
+
/** 头像 URL */
|
|
1330
|
+
avatarUrl?: string;
|
|
1331
|
+
/** 手机号 */
|
|
1332
|
+
phone?: string;
|
|
1333
|
+
/** 扩展属性 */
|
|
1334
|
+
metadata?: Record<string, unknown>;
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* 用户子功能接口
|
|
1338
|
+
* @example
|
|
1339
|
+
* ```ts
|
|
1340
|
+
* import { iam, type UserOperations } from '@h-ai/iam'
|
|
1341
|
+
* // 应用已完成 RelDB、Cache、Crypto 和 IAM 初始化。
|
|
1342
|
+
* const operations: UserOperations = iam.user
|
|
1343
|
+
* const result = await operations.listUsers({ page: 1, pageSize: 20, include: ['roles'] })
|
|
1344
|
+
* if (!result.success) {
|
|
1345
|
+
* result.error.code // 调用方将失败交给统一错误边界。
|
|
1346
|
+
* }
|
|
1347
|
+
* else {
|
|
1348
|
+
* result.data
|
|
1349
|
+
* }
|
|
1350
|
+
* ```
|
|
1351
|
+
*/
|
|
1352
|
+
interface UserOperations {
|
|
1353
|
+
/**
|
|
1354
|
+
* 创建独立随机初始凭据并发送短期单次邀请;不返回或通知原始初始密码。
|
|
1355
|
+
* initialCredentials.enabled、passwordReset 与通知通道必须就绪;接收人使用现有重置页面设密。
|
|
1356
|
+
* 投递未知时保留已创建账号并返回失败;核对后可通过重置通知流程恢复。
|
|
1357
|
+
* @param options - 可信管理端输入,必须包含通知通道对应的邮箱/手机号。
|
|
1358
|
+
* @returns 创建结果或失败;不能把通知结果未知当作未创建账号。
|
|
1359
|
+
*/
|
|
1360
|
+
inviteUser: (options: Omit<RegisterOptions, 'password'>) => Promise<HaiResult<RegisterResult>>;
|
|
1361
|
+
/** 批量查询最多 1000 个用户 ID,去重并保持输入顺序,忽略不存在的 ID;可批量包含角色。 */
|
|
1362
|
+
getUsers: (userIds: readonly string[], options?: {
|
|
1363
|
+
include?: ('roles')[];
|
|
1364
|
+
}) => Promise<HaiResult<User[]>>;
|
|
1365
|
+
/** 管理端创建账户,不受自助注册开关限制;调用端必须执行管理员授权。 */
|
|
1366
|
+
createUser: (options: RegisterOptions) => Promise<HaiResult<RegisterResult>>;
|
|
1367
|
+
/**
|
|
1368
|
+
* 注册用户
|
|
1369
|
+
*
|
|
1370
|
+
* 校验注册开关、密码强度、用户名/邮箱唯一性后,
|
|
1371
|
+
* 在事务中创建用户并分配默认角色。
|
|
1372
|
+
*
|
|
1373
|
+
* @param options - 注册选项(用户名、密码、邮箱等)
|
|
1374
|
+
* @returns 成功返回用户信息及可选的协议展示;失败返回对应错误码
|
|
1375
|
+
*/
|
|
1376
|
+
register: (options: RegisterOptions) => Promise<HaiResult<RegisterResult>>;
|
|
1377
|
+
/**
|
|
1378
|
+
* 获取当前用户
|
|
1379
|
+
*
|
|
1380
|
+
* 通过访问令牌验证会话后查询用户信息。
|
|
1381
|
+
*
|
|
1382
|
+
* @param accessToken - 访问令牌
|
|
1383
|
+
* @returns 成功返回用户信息;令牌无效/过期返回错误
|
|
1384
|
+
*/
|
|
1385
|
+
getCurrentUser: (accessToken: string) => Promise<HaiResult<User>>;
|
|
1386
|
+
/**
|
|
1387
|
+
* 更新当前登录用户信息(通过 accessToken 识别用户)
|
|
1388
|
+
*
|
|
1389
|
+
* 仅允许修改白名单字段(username、email、displayName、avatarUrl、phone、metadata),
|
|
1390
|
+
* 禁止修改安全字段(accountStatus、emailVerified 等)。修改 username/email 时自动校验唯一性。
|
|
1391
|
+
*
|
|
1392
|
+
* @param accessToken - 访问令牌
|
|
1393
|
+
* @param data - 要更新的字段(仅白名单字段)
|
|
1394
|
+
* @returns 成功返回更新后的用户信息
|
|
1395
|
+
*/
|
|
1396
|
+
updateCurrentUser: (accessToken: string, data: UpdateCurrentUserInput) => Promise<HaiResult<User>>;
|
|
1397
|
+
/**
|
|
1398
|
+
* 获取用户信息
|
|
1399
|
+
*
|
|
1400
|
+
* @param userId - 用户 ID
|
|
1401
|
+
* @param options - 查询选项(可选 include: ['roles'] 同时返回角色列表)
|
|
1402
|
+
* @param options.include - 指定需要额外展开的关联字段;传入 ['roles'] 时同时返回角色列表
|
|
1403
|
+
* @returns 成功返回用户信息或 null(用户不存在时)
|
|
1404
|
+
*/
|
|
1405
|
+
getUser: (userId: string, options?: {
|
|
1406
|
+
include?: ('roles')[];
|
|
1407
|
+
}) => Promise<HaiResult<User | null>>;
|
|
1408
|
+
/**
|
|
1409
|
+
* 获取用户列表(分页 + 搜索 + 过滤)
|
|
1410
|
+
*
|
|
1411
|
+
* @param options - 查询选项(页码、每页数量、搜索关键字、启用状态过滤)
|
|
1412
|
+
* @returns 成功返回分页用户列表
|
|
1413
|
+
*/
|
|
1414
|
+
listUsers: (options?: ListUsersOptions) => Promise<HaiResult<PaginatedResult<User>>>;
|
|
1415
|
+
/**
|
|
1416
|
+
* 依据 accounts.dormantAfterDays 原子休眠长期未登录的账号,返回数量;0天时不执行。
|
|
1417
|
+
* IAM 开启自动扫描时定时调用;外露管理接口须自行验证管理员权限。
|
|
1418
|
+
* @example
|
|
1419
|
+
* ```ts
|
|
1420
|
+
* import { iam } from '@h-ai/iam'
|
|
1421
|
+
* const scanned = await iam.user.scanInactiveAccounts()
|
|
1422
|
+
* if (!scanned.success) throw new Error(scanned.error.message)
|
|
1423
|
+
* ```
|
|
1424
|
+
*/
|
|
1425
|
+
scanInactiveAccounts: () => Promise<HaiResult<number>>;
|
|
1426
|
+
/**
|
|
1427
|
+
* 更新用户信息
|
|
1428
|
+
*
|
|
1429
|
+
* 空更新(无有效字段)时直接返回当前用户信息。
|
|
1430
|
+
*
|
|
1431
|
+
* @param userId - 用户 ID
|
|
1432
|
+
* @param data - 要更新的字段(displayName、email 等)
|
|
1433
|
+
* @returns 成功返回更新后的用户信息;用户不存在返回 USER_NOT_FOUND
|
|
1434
|
+
*/
|
|
1435
|
+
updateUser: (userId: string, data: Partial<User>) => Promise<HaiResult<User>>;
|
|
1436
|
+
/**
|
|
1437
|
+
* 删除用户
|
|
1438
|
+
*
|
|
1439
|
+
* 同时清理用户的角色关联数据。
|
|
1440
|
+
*
|
|
1441
|
+
* @param userId - 用户 ID
|
|
1442
|
+
* @returns 成功返回 ok;用户不存在返回 USER_NOT_FOUND
|
|
1443
|
+
*/
|
|
1444
|
+
deleteUser: (userId: string) => Promise<HaiResult<void>>;
|
|
1445
|
+
/**
|
|
1446
|
+
* 管理员重置用户密码
|
|
1447
|
+
*
|
|
1448
|
+
* 无需旧密码,直接设置新密码(仅限管理员操作)。
|
|
1449
|
+
*
|
|
1450
|
+
* @param userId - 用户 ID
|
|
1451
|
+
* @param newPassword - 新密码
|
|
1452
|
+
* @returns 成功返回 ok;用户不存在返回 USER_NOT_FOUND,复杂度不足返回 PASSWORD_POLICY_VIOLATION,策略禁止当前密码复用时返回 PASSWORD_REUSE_NOT_ALLOWED。
|
|
1453
|
+
*/
|
|
1454
|
+
adminResetPassword: (userId: string, newPassword: string) => Promise<HaiResult<void>>;
|
|
1455
|
+
/**
|
|
1456
|
+
* 使用原凭据完成首次或过期改密,不需要先获得普通会话。
|
|
1457
|
+
* 仍执行账号状态、旧密码校验、失败锁定及新密码策略;成功后需重新登录。
|
|
1458
|
+
* 不存在、无本地口令或受限账号统一返回 INVALID_CREDENTIALS,不以正确密码继续探测受限账号。
|
|
1459
|
+
* 复杂度不足返回 PASSWORD_POLICY_VIOLATION,复用当前密码返回 PASSWORD_REUSE_NOT_ALLOWED。
|
|
1460
|
+
* @example
|
|
1461
|
+
* ```ts
|
|
1462
|
+
* import { iam } from '@h-ai/iam'
|
|
1463
|
+
* const changed = await iam.user.renewPassword({ identifier: 'alice', password: 'Temporary123!', newPassword: 'ChosenPassword456!' })
|
|
1464
|
+
* if (!changed.success) throw new Error(changed.error.message)
|
|
1465
|
+
* ```
|
|
1466
|
+
*/
|
|
1467
|
+
renewPassword: (input: {
|
|
1468
|
+
identifier: string;
|
|
1469
|
+
password: string;
|
|
1470
|
+
newPassword: string;
|
|
1471
|
+
captcha?: string;
|
|
1472
|
+
}) => Promise<HaiResult<void>>;
|
|
1473
|
+
/**
|
|
1474
|
+
* 修改密码
|
|
1475
|
+
*
|
|
1476
|
+
* 验证旧密码 → 校验新密码强度 → 哈希并更新。
|
|
1477
|
+
*
|
|
1478
|
+
* @param userId - 用户 ID
|
|
1479
|
+
* @param oldPassword - 旧密码
|
|
1480
|
+
* @param newPassword - 新密码
|
|
1481
|
+
* @returns 成功返回 ok;旧密码错误返回 INVALID_CREDENTIALS,复杂度不足返回 PASSWORD_POLICY_VIOLATION,策略禁止当前密码复用时返回 PASSWORD_REUSE_NOT_ALLOWED。
|
|
1482
|
+
*/
|
|
1483
|
+
changePassword: (userId: string, oldPassword: string, newPassword: string) => Promise<HaiResult<void>>;
|
|
1484
|
+
/**
|
|
1485
|
+
* 当前登录用户修改密码
|
|
1486
|
+
*
|
|
1487
|
+
* 通过访问令牌识别用户 → 验证旧密码 → 校验新密码强度 → 哈希并更新 → 清除该用户所有会话。
|
|
1488
|
+
*
|
|
1489
|
+
* @param accessToken - 当前用户的访问令牌
|
|
1490
|
+
* @param oldPassword - 旧密码
|
|
1491
|
+
* @param newPassword - 新密码
|
|
1492
|
+
* @returns 成功返回 ok;旧密码错误返回 INVALID_CREDENTIALS,复杂度不足返回 PASSWORD_POLICY_VIOLATION,策略禁止当前密码复用时返回 PASSWORD_REUSE_NOT_ALLOWED。
|
|
1493
|
+
*/
|
|
1494
|
+
changeCurrentUserPassword: (accessToken: string, oldPassword: string, newPassword: string) => Promise<HaiResult<void>>;
|
|
1495
|
+
/**
|
|
1496
|
+
* 请求密码重置
|
|
1497
|
+
*
|
|
1498
|
+
* 查找用户 → 条件发布重置令牌(用户数据库行、SM3 摘要及 resetVersion)→ 通知业务层。
|
|
1499
|
+
* 即使用户不存在也返回 ok(防止用户枚举攻击)。
|
|
1500
|
+
*
|
|
1501
|
+
* @param identifier - 用户标识(邮箱)
|
|
1502
|
+
* @returns 用户不存在返回 ok;发布冲突、存储故障或投递未知返回失败,通知不自动重发。
|
|
1503
|
+
* @example
|
|
1504
|
+
* ```ts
|
|
1505
|
+
* import { iam } from '@h-ai/iam'
|
|
1506
|
+
* const requested = await iam.user.requestPasswordReset('alice@example.com')
|
|
1507
|
+
* if (!requested.success)
|
|
1508
|
+
* requested.error.code // 投递未知不能当作成功或由调用端擅自重发通知。
|
|
1509
|
+
* ```
|
|
1510
|
+
*/
|
|
1511
|
+
requestPasswordReset: (identifier: string, options?: {
|
|
1512
|
+
captcha?: string;
|
|
1513
|
+
}) => Promise<HaiResult<void>>;
|
|
1514
|
+
/**
|
|
1515
|
+
* 确认密码重置
|
|
1516
|
+
*
|
|
1517
|
+
* 条件计次并取得代际 → 验证密码策略 → 最终 SQL 同时校验代际/有效期/状态并改密、消费令牌。
|
|
1518
|
+
* 凭据代际已在数据库推进,后续缓存清理失败也不能重新授权旧会话。
|
|
1519
|
+
*
|
|
1520
|
+
* @param token - 重置令牌(明文,由 requestPasswordReset 生成并由业务层发送给用户)
|
|
1521
|
+
* @param newPassword - 新密码
|
|
1522
|
+
* @returns 成功返回 ok;令牌无效/过期返回 RESET_TOKEN_INVALID,尝试超限返回 RESET_TOKEN_MAX_ATTEMPTS,复杂度不足返回 PASSWORD_POLICY_VIOLATION,策略禁止当前密码复用时返回 PASSWORD_REUSE_NOT_ALLOWED。
|
|
1523
|
+
* @example
|
|
1524
|
+
* ```ts
|
|
1525
|
+
* import { iam } from '@h-ai/iam'
|
|
1526
|
+
* const result = await iam.user.confirmPasswordReset('token-from-recovery-link', 'ExampleOnly123!')
|
|
1527
|
+
* if (!result.success)
|
|
1528
|
+
* result.error.code
|
|
1529
|
+
* ```
|
|
1530
|
+
*/
|
|
1531
|
+
confirmPasswordReset: (token: string, newPassword: string) => Promise<HaiResult<void>>;
|
|
1532
|
+
/**
|
|
1533
|
+
* 验证密码强度(同步方法)
|
|
1534
|
+
*
|
|
1535
|
+
* 根据密码策略校验长度、大小写、数字、特殊字符等要求。
|
|
1536
|
+
*
|
|
1537
|
+
* @param password - 待校验的密码
|
|
1538
|
+
* @returns 通过返回 ok;不合规返回 PASSWORD_POLICY_VIOLATION
|
|
1539
|
+
*/
|
|
1540
|
+
validatePassword: (password: string) => HaiResult<void>;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* @h-ai/iam — 会话相关类型定义
|
|
1545
|
+
*
|
|
1546
|
+
* 包含: - 认证结果(AuthResult) - 会话类型(Session) - 会话子功能接口(SessionOperations)
|
|
1547
|
+
* @module iam-session-types
|
|
1548
|
+
*/
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* 认证令牌对(登录成功返回)
|
|
1552
|
+
*
|
|
1553
|
+
* 所有端(Web SSR / H5 SPA / Capacitor App / 未来小程序)
|
|
1554
|
+
* 使用完全相同的 Bearer Token 认证流程。
|
|
1555
|
+
* @example
|
|
1556
|
+
* ```ts
|
|
1557
|
+
* import { iam, type TokenPair } from '@h-ai/iam'
|
|
1558
|
+
* const login = await iam.auth.login({ identifier: 'alice', password: 'ExampleOnly123!' })
|
|
1559
|
+
* if (login.success) {
|
|
1560
|
+
* const tokens: TokenPair = login.data.tokens
|
|
1561
|
+
* const authorization = `${tokens.tokenType} ${tokens.accessToken}` // 仅用于受保护请求。
|
|
1562
|
+
* }
|
|
1563
|
+
* ```
|
|
1564
|
+
*/
|
|
1565
|
+
interface TokenPair {
|
|
1566
|
+
/** 访问令牌(短期,用于 API 认证) */
|
|
1567
|
+
accessToken: string;
|
|
1568
|
+
/** 刷新令牌(长期,用于换取新 accessToken) */
|
|
1569
|
+
refreshToken: string;
|
|
1570
|
+
/** accessToken 过期时间(秒) */
|
|
1571
|
+
expiresIn: number;
|
|
1572
|
+
/** 令牌类型,固定为 'Bearer' */
|
|
1573
|
+
tokenType: 'Bearer';
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* 认证结果
|
|
1577
|
+
*
|
|
1578
|
+
* 登录/注册成功后返回,包含用户信息、令牌、角色权限等。
|
|
1579
|
+
* `roles` / `permissions` 来源于会话创建时写入的 RBAC 数据,
|
|
1580
|
+
* 调用方无需再通过 `verifyToken` 获取。
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```ts
|
|
1583
|
+
* import { iam, type AuthResult } from '@h-ai/iam'
|
|
1584
|
+
* const login = await iam.auth.login({ identifier: 'alice', password: 'ExampleOnly123!' })
|
|
1585
|
+
* if (login.success) {
|
|
1586
|
+
* const authenticated: AuthResult = login.data
|
|
1587
|
+
* authenticated.user.displayName
|
|
1588
|
+
* }
|
|
1589
|
+
* ```
|
|
1590
|
+
*/
|
|
1591
|
+
interface AuthResult {
|
|
1592
|
+
/** 用户信息 */
|
|
1593
|
+
user: User;
|
|
1594
|
+
/** 令牌对(替代原来的单个 accessToken) */
|
|
1595
|
+
tokens: TokenPair;
|
|
1596
|
+
/** 角色 code 列表(登录时从 RBAC 写入) */
|
|
1597
|
+
roles: string[];
|
|
1598
|
+
/** 权限 code 列表(登录时从 RBAC 写入) */
|
|
1599
|
+
permissions: string[];
|
|
1600
|
+
/** 协议展示信息(可选) */
|
|
1601
|
+
agreements?: AgreementDisplay;
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* 会话数据
|
|
1605
|
+
* @example
|
|
1606
|
+
* ```ts
|
|
1607
|
+
* import { iam, type Session } from '@h-ai/iam'
|
|
1608
|
+
* const verified = await iam.auth.verifyToken('token-from-authorization-header')
|
|
1609
|
+
* if (verified.success) {
|
|
1610
|
+
* const session: Session = verified.data
|
|
1611
|
+
* session.permissions.includes('report:read')
|
|
1612
|
+
* }
|
|
1613
|
+
* ```
|
|
1614
|
+
*/
|
|
1615
|
+
interface Session {
|
|
1616
|
+
/** 登录时固定的终端分组;旧记录未设置时归为 web,刷新不能切换分组。 */
|
|
1617
|
+
terminalType?: 'web' | 'app' | 'desktop' | 'api';
|
|
1618
|
+
/** 会话内容版本;显式更新产生新版本,内部滑动续期保留版本。 */
|
|
1619
|
+
version: string;
|
|
1620
|
+
/** 认证快照绑定的持久凭据代;改密后的旧会话即使缓存清理失败也拒绝。 */
|
|
1621
|
+
credentialVersion: string;
|
|
1622
|
+
/** API Key 派生会话绑定的 key ID;普通用户登录时省略。 */
|
|
1623
|
+
apiKeyId?: string;
|
|
1624
|
+
/** 当前 key scopes;get/verify/refresh 每次重新验证持久化 key 并收敛权限。 */
|
|
1625
|
+
apiKeyScopes?: string[];
|
|
1626
|
+
/** 用户 ID */
|
|
1627
|
+
userId: string;
|
|
1628
|
+
/** 用户名 */
|
|
1629
|
+
username?: string;
|
|
1630
|
+
/** 显示名称 */
|
|
1631
|
+
displayName?: string;
|
|
1632
|
+
/** 头像 URL */
|
|
1633
|
+
avatarUrl?: string;
|
|
1634
|
+
/** 角色 code 列表(登录时写入,用于会话解析) */
|
|
1635
|
+
roles: string[];
|
|
1636
|
+
/** 权限 code 列表(登录时写入,用于会话解析) */
|
|
1637
|
+
permissions: string[];
|
|
1638
|
+
/** 来源(如 pc/android/ios) */
|
|
1639
|
+
source?: string;
|
|
1640
|
+
/** 访问令牌 */
|
|
1641
|
+
accessToken: string;
|
|
1642
|
+
/** 创建时间 */
|
|
1643
|
+
createdAt: Date;
|
|
1644
|
+
/** 首次签发时间;刷新保留,用于绝对期限。旧记录未设置时以 createdAt 为准。 */
|
|
1645
|
+
startedAt?: Date;
|
|
1646
|
+
/** 最后活动时间;启用闲置策略时,自动 refresh 不延长此值。 */
|
|
1647
|
+
lastActiveAt: Date;
|
|
1648
|
+
/** 过期时间 */
|
|
1649
|
+
expiresAt: Date;
|
|
1650
|
+
/** 本会话 access 的签发/滑动有效期,单位秒;创建时固化,刷新沿用该值。 */
|
|
1651
|
+
maxAge: number;
|
|
1652
|
+
/** 扩展数据(内部保留 _tokenPair 供 logout 时吊销 refreshToken) */
|
|
1653
|
+
data?: SessionData;
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* 会话扩展数据
|
|
1657
|
+
*
|
|
1658
|
+
* 支持开放的 Record 扩展,同时约束内部保留字段 _tokenPair。
|
|
1659
|
+
* @example
|
|
1660
|
+
* ```ts
|
|
1661
|
+
* import type { SessionData } from '@h-ai/iam'
|
|
1662
|
+
* const value: SessionData = { theme: 'dark', locale: 'zh-CN' }
|
|
1663
|
+
* ```
|
|
1664
|
+
*/
|
|
1665
|
+
type SessionData = Record<string, unknown> & {
|
|
1666
|
+
/** 令牌对(内部保留字段,由 session.create 写入,logout 时读取) */
|
|
1667
|
+
_tokenPair?: TokenPair;
|
|
1668
|
+
};
|
|
1669
|
+
/**
|
|
1670
|
+
* 创建会话选项
|
|
1671
|
+
* @example
|
|
1672
|
+
* ```ts
|
|
1673
|
+
* import type { CreateSessionOptions } from '@h-ai/iam'
|
|
1674
|
+
* const value: CreateSessionOptions = { userId: 'user-1', source: 'server', maxAge: 3600 }
|
|
1675
|
+
* ```
|
|
1676
|
+
*/
|
|
1677
|
+
interface CreateSessionOptions {
|
|
1678
|
+
/** 可信宿主提供的终端分组;会话签发后不可通过 update/refresh 改变。 */
|
|
1679
|
+
terminalType?: 'web' | 'app' | 'desktop' | 'api';
|
|
1680
|
+
/** 策略已验证的凭据代;创建前须与当前数据库核对,省略时仅供可信服务端按当前用户签发。 */
|
|
1681
|
+
expectedCredentialVersion?: string;
|
|
1682
|
+
/** 服务端已验证的 API Key 绑定;不得来自未验证客户端输入。 */
|
|
1683
|
+
apiKeyId?: string;
|
|
1684
|
+
apiKeyScopes?: string[];
|
|
1685
|
+
/** 用户 ID */
|
|
1686
|
+
userId: string;
|
|
1687
|
+
/** 用户名 */
|
|
1688
|
+
username?: string;
|
|
1689
|
+
/** 显示名称 */
|
|
1690
|
+
displayName?: string;
|
|
1691
|
+
/** 头像 URL */
|
|
1692
|
+
avatarUrl?: string;
|
|
1693
|
+
/** 角色 code 列表 */
|
|
1694
|
+
roles?: string[];
|
|
1695
|
+
/** 权限 code 列表 */
|
|
1696
|
+
permissions?: string[];
|
|
1697
|
+
/** 来源(如 pc/android/ios) */
|
|
1698
|
+
source?: string;
|
|
1699
|
+
/** 过期时间(秒) */
|
|
1700
|
+
maxAge?: number;
|
|
1701
|
+
/** 扩展数据 */
|
|
1702
|
+
data?: SessionData;
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* 会话子功能接口
|
|
1706
|
+
* @example
|
|
1707
|
+
* ```ts
|
|
1708
|
+
* import { iam, type SessionOperations } from '@h-ai/iam'
|
|
1709
|
+
* // 应用已完成 RelDB、Cache、Crypto 和 IAM 初始化。
|
|
1710
|
+
* const operations: SessionOperations = iam.session
|
|
1711
|
+
* const result = await operations.get('token-from-authorization-header')
|
|
1712
|
+
* if (!result.success) {
|
|
1713
|
+
* result.error.code // 调用方将失败交给统一错误边界。
|
|
1714
|
+
* }
|
|
1715
|
+
* else {
|
|
1716
|
+
* result.data
|
|
1717
|
+
* }
|
|
1718
|
+
* ```
|
|
1719
|
+
*/
|
|
1720
|
+
interface SessionOperations {
|
|
1721
|
+
/**
|
|
1722
|
+
* 创建会话
|
|
1723
|
+
*
|
|
1724
|
+
* 单设备模式在同一次原子提交内替换用户已有会话;提交失败保留旧会话。
|
|
1725
|
+
*
|
|
1726
|
+
* @param options - 会话创建选项(用户 ID、角色、来源等)
|
|
1727
|
+
* @returns 成功返回完整的 Session 对象(含生成的访问令牌和过期时间)
|
|
1728
|
+
*/
|
|
1729
|
+
create: (options: CreateSessionOptions) => Promise<HaiResult<Session>>;
|
|
1730
|
+
/**
|
|
1731
|
+
* 获取会话
|
|
1732
|
+
*
|
|
1733
|
+
* 查询缓存中的会话数据。API Key 会话先重新校验持久化 key 与 scopes;滑动窗口模式下自动续期。
|
|
1734
|
+
* access 到期返回 null;记录保留到 refresh 到期或撤销,允许独立刷新。
|
|
1735
|
+
*
|
|
1736
|
+
* @param accessToken - 访问令牌
|
|
1737
|
+
* @returns 成功返回会话对象或 null(会话不存在/已过期)
|
|
1738
|
+
*/
|
|
1739
|
+
get: (accessToken: string) => Promise<HaiResult<Session | null>>;
|
|
1740
|
+
/**
|
|
1741
|
+
* 验证访问令牌
|
|
1742
|
+
*
|
|
1743
|
+
* 等同于 get 但在会话不存在时返回错误而非 null;API Key 撤销/禁用/过期立即拒绝。
|
|
1744
|
+
*
|
|
1745
|
+
* @param accessToken - 访问令牌
|
|
1746
|
+
* @returns 成功返回会话;会话无效返回 SESSION_INVALID
|
|
1747
|
+
*/
|
|
1748
|
+
verifyToken: (accessToken: string) => Promise<HaiResult<Session>>;
|
|
1749
|
+
/**
|
|
1750
|
+
* 更新会话
|
|
1751
|
+
*
|
|
1752
|
+
* 合并允许的资料/授权/时间字段(data 浅合并);未传 lastActiveAt 时使用提交时刻。
|
|
1753
|
+
* expiresAt/lastActiveAt 必须是有效日期;不允许修改主体、令牌、固有 maxAge 和版本。
|
|
1754
|
+
*
|
|
1755
|
+
* @param accessToken - 访问令牌
|
|
1756
|
+
* @param data - 要更新的字段(roles、username、source、data 等)
|
|
1757
|
+
* @param expectedVersion - 读取到的 Session.version;过期版本失败,不能覆盖较新的修改
|
|
1758
|
+
* @returns 成功返回 ok;不存在返回 SESSION_NOT_FOUND,版本变化返回 CONFLICT
|
|
1759
|
+
* @example
|
|
1760
|
+
* ```ts
|
|
1761
|
+
* import { iam } from '@h-ai/iam'
|
|
1762
|
+
* // 已完成身份与更新权限校验的服务端上下文。
|
|
1763
|
+
* const token = 'token-from-authorization-header'
|
|
1764
|
+
* const found = await iam.session.get(token)
|
|
1765
|
+
* if (found.success && found.data) {
|
|
1766
|
+
* const updated = await iam.session.update(token, { data: { theme: 'dark' } }, found.data.version)
|
|
1767
|
+
* if (!updated.success)
|
|
1768
|
+
* updated.error.code // 版本冲突后重新读取,不能无条件覆盖。
|
|
1769
|
+
* }
|
|
1770
|
+
* ```
|
|
1771
|
+
*/
|
|
1772
|
+
update: (accessToken: string, data: Partial<Session>, expectedVersion: string) => Promise<HaiResult<void>>;
|
|
1773
|
+
/**
|
|
1774
|
+
* 删除会话
|
|
1775
|
+
*
|
|
1776
|
+
* 同时清除用户令牌映射关系。
|
|
1777
|
+
*
|
|
1778
|
+
* @param accessToken - 访问令牌
|
|
1779
|
+
* @returns 已撤销或令牌不存在时返回 ok;共享存储读取或提交失败时返回错误
|
|
1780
|
+
*/
|
|
1781
|
+
delete: (accessToken: string) => Promise<HaiResult<void>>;
|
|
1782
|
+
/**
|
|
1783
|
+
* 删除用户所有会话
|
|
1784
|
+
*
|
|
1785
|
+
* 原子删除用户分区内所有会话与 refresh 凭据。
|
|
1786
|
+
*
|
|
1787
|
+
* @param userId - 用户 ID
|
|
1788
|
+
* @returns 成功返回实际删除的会话数量
|
|
1789
|
+
*/
|
|
1790
|
+
deleteByUserId: (userId: string) => Promise<HaiResult<number>>;
|
|
1791
|
+
/**
|
|
1792
|
+
* 通过 refreshToken 换取新的 TokenPair
|
|
1793
|
+
*
|
|
1794
|
+
* 验证 refreshToken 有效性,签发新的 accessToken + refreshToken,
|
|
1795
|
+
* 旧 refreshToken 自动失效(Rotation 策略);access 过期不阻止仍有效的 refresh。
|
|
1796
|
+
*
|
|
1797
|
+
* @param refreshToken - 刷新令牌
|
|
1798
|
+
* @returns 成功返回新的 TokenPair;失败返回 TOKEN_REFRESH_FAILED / TOKEN_EXPIRED
|
|
1799
|
+
*/
|
|
1800
|
+
refresh: (refreshToken: string) => Promise<HaiResult<TokenPair>>;
|
|
1801
|
+
/**
|
|
1802
|
+
* 吊销 refreshToken(登出时调用)
|
|
1803
|
+
*
|
|
1804
|
+
* @param refreshToken - 刷新令牌
|
|
1805
|
+
* @returns 成功返回 ok;令牌不存在时静默成功
|
|
1806
|
+
*/
|
|
1807
|
+
revokeRefresh: (refreshToken: string) => Promise<HaiResult<void>>;
|
|
1808
|
+
/**
|
|
1809
|
+
* 批量更新用户所有活跃会话的 roles / permissions 字段
|
|
1810
|
+
*
|
|
1811
|
+
* 遍历用户缓存中的所有 session token,合并更新指定字段,
|
|
1812
|
+
* 同时清理已失效的会话令牌。用于角色/权限变更后的会话同步。
|
|
1813
|
+
*
|
|
1814
|
+
* @param userId - 用户 ID
|
|
1815
|
+
* @param updates - 要更新的字段(roles / permissions)
|
|
1816
|
+
* @returns 成功返回 ok
|
|
1817
|
+
*/
|
|
1818
|
+
patchUserSessions: (userId: string, updates: SessionFieldUpdates) => Promise<HaiResult<void>>;
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* 会话字段更新(仅限 roles / permissions)
|
|
1822
|
+
*
|
|
1823
|
+
* 用于角色/权限变更后的会话同步。
|
|
1824
|
+
* @example
|
|
1825
|
+
* ```ts
|
|
1826
|
+
* import type { SessionFieldUpdates } from '@h-ai/iam'
|
|
1827
|
+
* const value: SessionFieldUpdates = { roles: ['user'], permissions: ['report:read'] }
|
|
1828
|
+
* ```
|
|
1829
|
+
*/
|
|
1830
|
+
interface SessionFieldUpdates {
|
|
1831
|
+
/** 角色 code 列表 */
|
|
1832
|
+
roles?: string[];
|
|
1833
|
+
/** 权限 code 列表 */
|
|
1834
|
+
permissions?: string[];
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* @h-ai/iam — 一次性票据类型
|
|
1839
|
+
*
|
|
1840
|
+
* 定义通用的短期、一次性能力票据(capability ticket):由已认证的上下文签发,
|
|
1841
|
+
* 绑定主体、用途与授权信息,在无法携带 Bearer Token 的通道(如 WebSocket URL)中
|
|
1842
|
+
* 用作一次性入场凭证。典型用途:AI 语音 WebSocket 接入。
|
|
1843
|
+
* @module ticket/iam-ticket-types
|
|
1844
|
+
*/
|
|
1845
|
+
|
|
1846
|
+
/**
|
|
1847
|
+
* 票据授权绑定信息
|
|
1848
|
+
*
|
|
1849
|
+
* 消费时原样返回,供服务端确认本次操作、模型、会话等参数与签发时一致。
|
|
1850
|
+
* 除固定字段外允许应用自定义键值。
|
|
1851
|
+
* @example
|
|
1852
|
+
* ```ts
|
|
1853
|
+
* import type { TicketGrant } from '@h-ai/iam'
|
|
1854
|
+
* const value: TicketGrant = { operation: 'transcribe', model: 'speech-model', sessionId: 'conversation-1' }
|
|
1855
|
+
* ```
|
|
1856
|
+
*/
|
|
1857
|
+
interface TicketGrant {
|
|
1858
|
+
/** 操作类型(如 `'transcribe'` | `'synthesize'` 或应用自定义) */
|
|
1859
|
+
operation?: string;
|
|
1860
|
+
/** 模型 ID */
|
|
1861
|
+
model?: string;
|
|
1862
|
+
/** 会话 ID */
|
|
1863
|
+
sessionId?: string;
|
|
1864
|
+
/** 应用自定义绑定字段 */
|
|
1865
|
+
[key: string]: unknown;
|
|
1866
|
+
}
|
|
1867
|
+
/**
|
|
1868
|
+
* 签发一次性票据的选项
|
|
1869
|
+
* @example
|
|
1870
|
+
* ```ts
|
|
1871
|
+
* import type { IssueTicketOptions } from '@h-ai/iam'
|
|
1872
|
+
* const value: IssueTicketOptions = { subjectId: 'user-1', purpose: 'ai-audio', ttlMs: 30000, grant: { operation: 'transcribe' } }
|
|
1873
|
+
* ```
|
|
1874
|
+
*/
|
|
1875
|
+
interface IssueTicketOptions {
|
|
1876
|
+
/** 主体 ID(票据绑定的用户 / 主体,消费时原样返回) */
|
|
1877
|
+
subjectId: string;
|
|
1878
|
+
/** 票据用途(如 `'ai-audio'`),消费时校验一致 */
|
|
1879
|
+
purpose: string;
|
|
1880
|
+
/** 授权绑定信息(操作 / 模型 / 会话等,消费时原样返回) */
|
|
1881
|
+
grant?: TicketGrant;
|
|
1882
|
+
/** 有效期(毫秒,默认 30000) */
|
|
1883
|
+
ttlMs?: number;
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* 签发结果
|
|
1887
|
+
* @example
|
|
1888
|
+
* ```ts
|
|
1889
|
+
* import { iam, type IssuedTicket } from '@h-ai/iam'
|
|
1890
|
+
* const issued = await iam.ticket.issue({ subjectId: 'user-1', purpose: 'ai-audio' })
|
|
1891
|
+
* if (issued.success) {
|
|
1892
|
+
* const value: IssuedTicket = issued.data
|
|
1893
|
+
* value.expiresAt // 不能延长服务端签发期限;票据只能消费一次。
|
|
1894
|
+
* }
|
|
1895
|
+
* ```
|
|
1896
|
+
*/
|
|
1897
|
+
interface IssuedTicket {
|
|
1898
|
+
/** 一次性票据值(密码学安全随机,base64url) */
|
|
1899
|
+
ticket: string;
|
|
1900
|
+
/** 过期时间(Unix 毫秒) */
|
|
1901
|
+
expiresAt: number;
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* 消费票据的选项
|
|
1905
|
+
* @example
|
|
1906
|
+
* ```ts
|
|
1907
|
+
* import type { ConsumeTicketOptions } from '@h-ai/iam'
|
|
1908
|
+
* const value: ConsumeTicketOptions = { purpose: 'ai-audio' }
|
|
1909
|
+
* ```
|
|
1910
|
+
*/
|
|
1911
|
+
interface ConsumeTicketOptions {
|
|
1912
|
+
/** 期望用途;与签发用途不一致时返回 `TICKET_INVALID`(防止跨用途重放) */
|
|
1913
|
+
purpose?: string;
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1916
|
+
* 消费结果(校验通过并原子消费后返回)
|
|
1917
|
+
* @example
|
|
1918
|
+
* ```ts
|
|
1919
|
+
* import { iam, type ConsumedTicket } from '@h-ai/iam'
|
|
1920
|
+
* const consumed = await iam.ticket.consume('ticket-from-upgrade-request', { purpose: 'ai-audio' })
|
|
1921
|
+
* if (consumed.success) {
|
|
1922
|
+
* const authorization: ConsumedTicket = consumed.data
|
|
1923
|
+
* authorization.grant.operation // 校验与本次请求操作相同。
|
|
1924
|
+
* }
|
|
1925
|
+
* ```
|
|
1926
|
+
*/
|
|
1927
|
+
interface ConsumedTicket {
|
|
1928
|
+
/** 票据绑定的主体 ID */
|
|
1929
|
+
subjectId: string;
|
|
1930
|
+
/** 票据用途 */
|
|
1931
|
+
purpose: string;
|
|
1932
|
+
/** 授权绑定信息 */
|
|
1933
|
+
grant: TicketGrant;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* 一次性票据操作接口(通过 `iam.ticket` 访问)
|
|
1937
|
+
*
|
|
1938
|
+
* 提供密码学安全、带 TTL、原子单次消费的能力票据签发与消费。
|
|
1939
|
+
* @example
|
|
1940
|
+
* ```ts
|
|
1941
|
+
* import { iam, type TicketOperations } from '@h-ai/iam'
|
|
1942
|
+
* // 应用已完成 RelDB、Cache、Crypto 和 IAM 初始化。
|
|
1943
|
+
* const operations: TicketOperations = iam.ticket
|
|
1944
|
+
* const result = await operations.issue({ subjectId: 'user-1', purpose: 'ai-audio', ttlMs: 30000 })
|
|
1945
|
+
* if (!result.success) {
|
|
1946
|
+
* result.error.code // 调用方将失败交给统一错误边界。
|
|
1947
|
+
* }
|
|
1948
|
+
* else {
|
|
1949
|
+
* result.data
|
|
1950
|
+
* }
|
|
1951
|
+
* ```
|
|
1952
|
+
*/
|
|
1953
|
+
interface TicketOperations {
|
|
1954
|
+
/**
|
|
1955
|
+
* 签发一次性票据
|
|
1956
|
+
*
|
|
1957
|
+
* 生成密码学安全随机值,绑定主体 / 用途 / 授权信息并按 TTL 存入 cache。
|
|
1958
|
+
*
|
|
1959
|
+
* @param options - 签发选项
|
|
1960
|
+
* @returns 票据值与过期时间
|
|
1961
|
+
*/
|
|
1962
|
+
issue: (options: IssueTicketOptions) => Promise<HaiResult<IssuedTicket>>;
|
|
1963
|
+
/**
|
|
1964
|
+
* 原子消费一次性票据
|
|
1965
|
+
*
|
|
1966
|
+
* 单次有效:并发消费同一票据时只有一个成功,其余返回 `TICKET_INVALID`。
|
|
1967
|
+
* 票据不存在 / 已过期 / 已消费 / 用途不匹配时返回错误。
|
|
1968
|
+
*
|
|
1969
|
+
* @param ticket - 票据值
|
|
1970
|
+
* @param options - 消费选项(可校验用途)
|
|
1971
|
+
* @returns 主体 + 用途 + 授权信息
|
|
1972
|
+
*/
|
|
1973
|
+
consume: (ticket: string, options?: ConsumeTicketOptions) => Promise<HaiResult<ConsumedTicket>>;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
/**
|
|
1977
|
+
* @h-ai/iam — 认证类型定义
|
|
1978
|
+
*
|
|
1979
|
+
* 包含: - 凭证类型(Credentials) - 认证策略接口(AuthStrategy) - 认证操作接口(AuthnOperations)
|
|
1980
|
+
* @module iam-authn-types
|
|
1981
|
+
*/
|
|
1982
|
+
|
|
1983
|
+
/**
|
|
1984
|
+
* 交互鉴别用途;同一挑战不能用于另一入口,API Key 机器鉴别不使用它。
|
|
1985
|
+
* @example
|
|
1986
|
+
* ```ts
|
|
1987
|
+
* import type { CaptchaOperation } from '@h-ai/iam'
|
|
1988
|
+
* const operation: CaptchaOperation = 'login'
|
|
1989
|
+
* ```
|
|
1990
|
+
*/
|
|
1991
|
+
type CaptchaOperation = 'login' | 'register' | 'sendOtp' | 'passwordReset' | 'renewPassword';
|
|
1992
|
+
/**
|
|
1993
|
+
* 可公开的 ALTCHA 挑战;签名秘密仅存共享缓存,不包含在返回值中。
|
|
1994
|
+
* @example
|
|
1995
|
+
* ```ts
|
|
1996
|
+
* import { iam, type IamCaptchaChallenge } from '@h-ai/iam'
|
|
1997
|
+
* const result = await iam.auth.createCaptcha({ operation: 'login' })
|
|
1998
|
+
* if (result.success) { const challenge: IamCaptchaChallenge = result.data; void challenge.id }
|
|
1999
|
+
* ```
|
|
2000
|
+
*/
|
|
2001
|
+
interface IamCaptchaChallenge {
|
|
2002
|
+
id: string;
|
|
2003
|
+
challenge: Challenge;
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* 由可信 HTTP 宿主提供的登录元数据,独立于客户端凭据输入。
|
|
2007
|
+
* @example
|
|
2008
|
+
* ```ts
|
|
2009
|
+
* import type { AuthRequestContext } from '@h-ai/iam'
|
|
2010
|
+
* const context: AuthRequestContext = { ip: '192.0.2.10', requestId: 'request-1' }
|
|
2011
|
+
* ```
|
|
2012
|
+
*/
|
|
2013
|
+
interface AuthRequestContext {
|
|
2014
|
+
/** 宿主识别的接入终端分组;不是物理设备证明,省略时普通登录归为 web。 */
|
|
2015
|
+
terminalType?: 'web' | 'app' | 'desktop' | 'api';
|
|
2016
|
+
/** socket/平台或受信代理解析后的地址;不能直接复制 X-Forwarded-For。 */
|
|
2017
|
+
ip?: string;
|
|
2018
|
+
userAgent?: string;
|
|
2019
|
+
requestId?: string;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* 用户凭证(用于密码登录)
|
|
2023
|
+
* @example
|
|
2024
|
+
* ```ts
|
|
2025
|
+
* import type { PasswordCredentials } from '@h-ai/iam'
|
|
2026
|
+
* const value: PasswordCredentials = { identifier: 'alice', password: 'ExampleOnly123!' }
|
|
2027
|
+
* ```
|
|
2028
|
+
*/
|
|
2029
|
+
interface PasswordCredentials {
|
|
2030
|
+
/** 原地恢复会话时限定原主体;鉴别成功但不是此用户时也不签发新会话。 */
|
|
2031
|
+
expectedUserId?: string;
|
|
2032
|
+
captcha?: string;
|
|
2033
|
+
/** 用户名/邮箱/手机号 */
|
|
2034
|
+
identifier: string;
|
|
2035
|
+
/** 密码 */
|
|
2036
|
+
password: string;
|
|
2037
|
+
}
|
|
2038
|
+
/**
|
|
2039
|
+
* OTP 凭证(用于验证码登录)
|
|
2040
|
+
* @example
|
|
2041
|
+
* ```ts
|
|
2042
|
+
* import type { OtpCredentials } from '@h-ai/iam'
|
|
2043
|
+
* const value: OtpCredentials = { identifier: 'alice@example.com', code: '123456' }
|
|
2044
|
+
* ```
|
|
2045
|
+
*/
|
|
2046
|
+
interface OtpCredentials {
|
|
2047
|
+
/** 会话恢复限定原主体,不能以其他账号的有效验证码恢复原业务动作。 */
|
|
2048
|
+
expectedUserId?: string;
|
|
2049
|
+
captcha?: string;
|
|
2050
|
+
/** 邮箱/手机号 */
|
|
2051
|
+
identifier: string;
|
|
2052
|
+
/** 验证码 */
|
|
2053
|
+
code: string;
|
|
2054
|
+
}
|
|
2055
|
+
/**
|
|
2056
|
+
* LDAP 凭证
|
|
2057
|
+
* @example
|
|
2058
|
+
* ```ts
|
|
2059
|
+
* import type { LdapCredentials } from '@h-ai/iam'
|
|
2060
|
+
* const value: LdapCredentials = { username: 'alice', password: 'ExampleOnly123!' }
|
|
2061
|
+
* ```
|
|
2062
|
+
*/
|
|
2063
|
+
interface LdapCredentials {
|
|
2064
|
+
/** 会话恢复限定原主体;其他账号鉴别成功也不签发恢复会话。 */
|
|
2065
|
+
expectedUserId?: string;
|
|
2066
|
+
captcha?: string;
|
|
2067
|
+
/** 用户名 */
|
|
2068
|
+
username: string;
|
|
2069
|
+
/** 密码 */
|
|
2070
|
+
password: string;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* API Key 凭证(用于 API Key 认证)
|
|
2074
|
+
* @example
|
|
2075
|
+
* ```ts
|
|
2076
|
+
* import { iam, type ApiKeyCredentials } from '@h-ai/iam'
|
|
2077
|
+
* const created = await iam.apiKey.createApiKey('user-1', { name: 'report-reader', scopes: ['report:read'] })
|
|
2078
|
+
* if (created.success) {
|
|
2079
|
+
* const credentials: ApiKeyCredentials = { key: created.data.rawKey }
|
|
2080
|
+
* const authenticated = await iam.auth.loginWithApiKey(credentials)
|
|
2081
|
+
* if (!authenticated.success)
|
|
2082
|
+
* authenticated.error.code
|
|
2083
|
+
* }
|
|
2084
|
+
* ```
|
|
2085
|
+
*/
|
|
2086
|
+
interface ApiKeyCredentials {
|
|
2087
|
+
/** 明文 API Key */
|
|
2088
|
+
key: string;
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* 统一凭证类型
|
|
2092
|
+
* @example
|
|
2093
|
+
* ```ts
|
|
2094
|
+
* import type { Credentials } from '@h-ai/iam'
|
|
2095
|
+
* const value: Credentials = { type: 'otp', identifier: 'alice@example.com', code: '123456' }
|
|
2096
|
+
* ```
|
|
2097
|
+
*/
|
|
2098
|
+
type Credentials = {
|
|
2099
|
+
type: 'password';
|
|
2100
|
+
} & PasswordCredentials | {
|
|
2101
|
+
type: 'otp';
|
|
2102
|
+
} & OtpCredentials | {
|
|
2103
|
+
type: 'ldap';
|
|
2104
|
+
} & LdapCredentials | {
|
|
2105
|
+
type: 'apikey';
|
|
2106
|
+
} & ApiKeyCredentials;
|
|
2107
|
+
/**
|
|
2108
|
+
* 策略内部可信认证快照;凭据代来自实际验证的用户行,不能用会话创建时的新快照替换。
|
|
2109
|
+
* @internal
|
|
2110
|
+
* @example 模块内部上下文
|
|
2111
|
+
* ```ts
|
|
2112
|
+
* function credentialSnapshot(user: AuthenticatedUser) {
|
|
2113
|
+
* return { userId: user.id, expectedCredentialVersion: user.credentialVersion }
|
|
2114
|
+
* }
|
|
2115
|
+
* ```
|
|
2116
|
+
*/
|
|
2117
|
+
interface AuthenticatedUser extends User {
|
|
2118
|
+
/** 密码最终提交时更换;仅在服务端策略和会话构造间传递。 */
|
|
2119
|
+
credentialVersion: string;
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* 认证策略接口
|
|
2123
|
+
*
|
|
2124
|
+
* 所有认证方式(密码/OTP/LDAP)都实现此接口
|
|
2125
|
+
* @example
|
|
2126
|
+
* ```ts
|
|
2127
|
+
* import type { AuthStrategy, Credentials } from '@h-ai/iam'
|
|
2128
|
+
* async function authenticate(strategy: AuthStrategy, credentials: Credentials) {
|
|
2129
|
+
* const result = await strategy.authenticate(credentials)
|
|
2130
|
+
* if (!result.success)
|
|
2131
|
+
* return result
|
|
2132
|
+
* return { userId: result.data.id, credentialVersion: result.data.credentialVersion }
|
|
2133
|
+
* }
|
|
2134
|
+
* ```
|
|
2135
|
+
*/
|
|
2136
|
+
interface AuthStrategy {
|
|
2137
|
+
/** 策略类型 */
|
|
2138
|
+
readonly type: AuthStrategyType;
|
|
2139
|
+
/** 策略名称 */
|
|
2140
|
+
readonly name: string;
|
|
2141
|
+
/**
|
|
2142
|
+
* 执行认证
|
|
2143
|
+
*
|
|
2144
|
+
* @param credentials - 统一凭证(包含 type 字段标识认证方式)
|
|
2145
|
+
* @returns 认证成功返回用户信息;失败返回对应错误码(INVALID_CREDENTIALS / USER_LOCKED 等)
|
|
2146
|
+
*/
|
|
2147
|
+
authenticate: (credentials: Credentials) => Promise<HaiResult<AuthenticatedUser>>;
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* 认证子功能接口
|
|
2151
|
+
* @example
|
|
2152
|
+
* ```ts
|
|
2153
|
+
* import { iam, type AuthnOperations } from '@h-ai/iam'
|
|
2154
|
+
* // 应用已完成 RelDB、Cache、Crypto 和 IAM 初始化。
|
|
2155
|
+
* const operations: AuthnOperations = iam.auth
|
|
2156
|
+
* const result = await operations.verifyToken('token-from-authorization-header')
|
|
2157
|
+
* if (!result.success) {
|
|
2158
|
+
* result.error.code // 调用方将失败交给统一错误边界。
|
|
2159
|
+
* }
|
|
2160
|
+
* else {
|
|
2161
|
+
* result.data
|
|
2162
|
+
* }
|
|
2163
|
+
* ```
|
|
2164
|
+
*/
|
|
2165
|
+
interface AuthnOperations {
|
|
2166
|
+
/** 生成短期单用、自托管挑战;启用后各交互入口在服务端校验解答。 */
|
|
2167
|
+
createCaptcha: (input: {
|
|
2168
|
+
operation: CaptchaOperation;
|
|
2169
|
+
}) => Promise<HaiResult<IamCaptchaChallenge>>;
|
|
2170
|
+
/**
|
|
2171
|
+
* 验证当前会话和原密码,为已配置的敏感操作签发短期单次证明。
|
|
2172
|
+
* 不创建新会话,不替代操作本身的权限/资源检查。
|
|
2173
|
+
* @param accessToken - 当前会话令牌。
|
|
2174
|
+
* @param password - 原密码;失败计数与登录共用。
|
|
2175
|
+
* @param operation - 配置中选定的操作 ID。
|
|
2176
|
+
* @returns 绑定用户、会话与操作的单次票据,或鉴别失败。
|
|
2177
|
+
*/
|
|
2178
|
+
reauthenticate: (accessToken: string, password: string, operation: string) => Promise<HaiResult<IssuedTicket>>;
|
|
2179
|
+
/**
|
|
2180
|
+
* 操作入口完成权限检查后调用;关闭策略/未选操作仅验证当前会话。
|
|
2181
|
+
* 选定操作原子消费证明;不能跨会话、跨操作或重复使用。
|
|
2182
|
+
* @param accessToken - 当前会话令牌。
|
|
2183
|
+
* @param operation - 服务端确定的操作 ID,不能由客户端选择要执行的守卫。
|
|
2184
|
+
* @param ticket - reauthenticate 签发的短期证明。
|
|
2185
|
+
* @returns 允许继续时成功;拒绝或基础设施失败时返回错误。
|
|
2186
|
+
*/
|
|
2187
|
+
checkSensitiveOperation: (accessToken: string, operation: string, ticket?: string) => Promise<HaiResult<void>>;
|
|
2188
|
+
/**
|
|
2189
|
+
* 登录(使用密码)
|
|
2190
|
+
*
|
|
2191
|
+
* 检查登录方式启用 → 密码策略认证 → 创建会话 → 返回令牌。
|
|
2192
|
+
*
|
|
2193
|
+
* @param credentials - 密码凭证(identifier + password)
|
|
2194
|
+
* @param context - 宿主提供的可信登录元数据,不从凭据输入复制 IP。
|
|
2195
|
+
* @returns 成功返回 AuthResult(用户信息、令牌、协议展示);失败返回错误
|
|
2196
|
+
*/
|
|
2197
|
+
login: (credentials: PasswordCredentials, context?: AuthRequestContext) => Promise<HaiResult<AuthResult>>;
|
|
2198
|
+
/**
|
|
2199
|
+
* 使用验证码登录
|
|
2200
|
+
*
|
|
2201
|
+
* @param credentials - OTP 凭证(identifier + code)
|
|
2202
|
+
* @param context - 宿主提供的可信登录元数据,不从凭据输入复制 IP。
|
|
2203
|
+
* @returns 成功返回 AuthResult;验证码无效/过期返回 OTP_INVALID
|
|
2204
|
+
*/
|
|
2205
|
+
loginWithOtp: (credentials: OtpCredentials, context?: AuthRequestContext) => Promise<HaiResult<AuthResult>>;
|
|
2206
|
+
/**
|
|
2207
|
+
* 使用 LDAP 登录
|
|
2208
|
+
*
|
|
2209
|
+
* @param credentials - LDAP 凭证(username + password)
|
|
2210
|
+
* @param context - 宿主提供的可信登录元数据,不从凭据输入复制 IP。
|
|
2211
|
+
* @returns 成功返回 AuthResult(自动同步本地用户);失败返回错误
|
|
2212
|
+
*/
|
|
2213
|
+
loginWithLdap: (credentials: LdapCredentials, context?: AuthRequestContext) => Promise<HaiResult<AuthResult>>;
|
|
2214
|
+
/**
|
|
2215
|
+
* 使用 API Key 登录
|
|
2216
|
+
*
|
|
2217
|
+
* 验证 API Key 有效性 → 查找关联用户 → 按 scopes 收敛授权 → 创建绑定 key 的会话。
|
|
2218
|
+
* 空 scopes 无操作权限;显式 * 也不超出主体权限。受限 key 通过 permission 授权,不保留角色入口。
|
|
2219
|
+
*
|
|
2220
|
+
* @param credentials - API Key 凭证
|
|
2221
|
+
* @param context - 宿主提供的可信登录元数据,不从凭据输入复制 IP。
|
|
2222
|
+
* @returns 成功返回 AuthResult;API Key 无效/过期/禁用返回对应错误
|
|
2223
|
+
*/
|
|
2224
|
+
loginWithApiKey: (credentials: ApiKeyCredentials, context?: AuthRequestContext) => Promise<HaiResult<AuthResult>>;
|
|
2225
|
+
/**
|
|
2226
|
+
* 登出
|
|
2227
|
+
*
|
|
2228
|
+
* 按完整访问令牌原子删除会话及关联 refresh,access 已到期也撤销待刷新记录。
|
|
2229
|
+
* 无效或不存在的令牌幂等成功;缓存读取/提交失败必须返回错误,不能宣称服务端已退出。
|
|
2230
|
+
*
|
|
2231
|
+
* @param accessToken - 访问令牌
|
|
2232
|
+
* @returns 撤销完成或令牌不存在返回 ok;共享存储失败返回错误,恢复后可用同一令牌重试
|
|
2233
|
+
*/
|
|
2234
|
+
logout: (accessToken: string) => Promise<HaiResult<void>>;
|
|
2235
|
+
/**
|
|
2236
|
+
* 验证令牌
|
|
2237
|
+
*
|
|
2238
|
+
* 验证会话并读取当前用户状态、角色与权限;重新应用 API Key scopes,依赖失败即拒绝。
|
|
2239
|
+
* 会话同步仅是缓存快照,受保护请求使用此入口而非低层 session.verifyToken。
|
|
2240
|
+
*
|
|
2241
|
+
* @param accessToken - 访问令牌
|
|
2242
|
+
* @returns 成功返回会话信息;令牌无效/过期返回 SESSION_INVALID
|
|
2243
|
+
*/
|
|
2244
|
+
verifyToken: (accessToken: string) => Promise<HaiResult<Session>>;
|
|
2245
|
+
/**
|
|
2246
|
+
* 发送验证码
|
|
2247
|
+
*
|
|
2248
|
+
* @param identifier - 用户标识(邮箱/手机号)
|
|
2249
|
+
* @returns 成功返回验证码过期时间;频率限制返回 OTP_RESEND_TOO_FAST
|
|
2250
|
+
*/
|
|
2251
|
+
sendOtp: (identifier: string, options?: {
|
|
2252
|
+
captcha?: string;
|
|
2253
|
+
}) => Promise<HaiResult<{
|
|
2254
|
+
expiresAt: Date;
|
|
2255
|
+
}>>;
|
|
2256
|
+
/**
|
|
2257
|
+
* 注册并登录(一站式)
|
|
2258
|
+
*
|
|
2259
|
+
* 先注册用户(含默认角色分配),成功后立即登录并返回令牌。
|
|
2260
|
+
* 注册失败则直接返回注册错误;注册成功但登录失败则返回登录错误。
|
|
2261
|
+
*
|
|
2262
|
+
* @param options - 注册选项(用户名、密码、邮箱等)
|
|
2263
|
+
* @returns 成功返回 AuthResult(用户信息、令牌、协议展示);失败返回对应错误码
|
|
2264
|
+
*/
|
|
2265
|
+
registerAndLogin: (options: RegisterOptions, context?: AuthRequestContext) => Promise<HaiResult<AuthResult>>;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
/**
|
|
2269
|
+
* @h-ai/iam — LDAP 认证策略
|
|
2270
|
+
*
|
|
2271
|
+
* LDAP 目录认证方式
|
|
2272
|
+
* @module iam-authn-ldap-strategy
|
|
2273
|
+
*/
|
|
2274
|
+
|
|
2275
|
+
/**
|
|
2276
|
+
* LDAP 客户端接口
|
|
2277
|
+
* @internal
|
|
2278
|
+
* @example 模块内部上下文
|
|
2279
|
+
* ```ts
|
|
2280
|
+
* async function lookup(client: LdapClient) {
|
|
2281
|
+
* const found = await client.search('ou=users,dc=example,dc=com', '(uid=alice)', ['uid', 'mail'])
|
|
2282
|
+
* if (!found.success)
|
|
2283
|
+
* return found
|
|
2284
|
+
* return found.data.map(entry => entry.dn)
|
|
2285
|
+
* }
|
|
2286
|
+
* ```
|
|
2287
|
+
*/
|
|
2288
|
+
interface LdapClient {
|
|
2289
|
+
/**
|
|
2290
|
+
* 绑定(认证)
|
|
2291
|
+
*/
|
|
2292
|
+
bind: (dn: string, password: string) => Promise<HaiResult<void>>;
|
|
2293
|
+
/**
|
|
2294
|
+
* 搜索用户
|
|
2295
|
+
*/
|
|
2296
|
+
search: (base: string, filter: string, attributes: string[]) => Promise<HaiResult<LdapSearchEntry[]>>;
|
|
2297
|
+
/**
|
|
2298
|
+
* 解除绑定
|
|
2299
|
+
*/
|
|
2300
|
+
unbind: () => Promise<HaiResult<void>>;
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* LDAP 搜索结果条目
|
|
2304
|
+
* @internal
|
|
2305
|
+
* @example 模块内部上下文
|
|
2306
|
+
* ```ts
|
|
2307
|
+
* const entry: LdapSearchEntry = { dn: 'uid=alice,ou=users,dc=example,dc=com', attributes: { uid: 'alice', mail: ['alice@example.com'] } }
|
|
2308
|
+
* const username = entry.attributes.uid
|
|
2309
|
+
* ```
|
|
2310
|
+
*/
|
|
2311
|
+
interface LdapSearchEntry {
|
|
2312
|
+
/** DN */
|
|
2313
|
+
dn: string;
|
|
2314
|
+
/** 属性 */
|
|
2315
|
+
attributes: Record<string, string | string[]>;
|
|
2316
|
+
}
|
|
2317
|
+
/**
|
|
2318
|
+
* LDAP 客户端工厂
|
|
2319
|
+
* @example
|
|
2320
|
+
* ```ts
|
|
2321
|
+
* import type { LdapClientFactory, LdapConfig } from '@h-ai/iam'
|
|
2322
|
+
* async function check(factory: LdapClientFactory, config: LdapConfig) {
|
|
2323
|
+
* const opened = await factory(config)
|
|
2324
|
+
* if (!opened.success)
|
|
2325
|
+
* return opened
|
|
2326
|
+
* const result = await opened.data.bind(config.bindDn, config.bindPassword)
|
|
2327
|
+
* const closed = await opened.data.unbind()
|
|
2328
|
+
* if (!closed.success)
|
|
2329
|
+
* return closed
|
|
2330
|
+
* return result
|
|
2331
|
+
* }
|
|
2332
|
+
* ```
|
|
2333
|
+
*/
|
|
2334
|
+
type LdapClientFactory = (config: LdapConfig) => Promise<HaiResult<LdapClient>>;
|
|
2335
|
+
|
|
2336
|
+
/** 提供给认证页面的白名单配置;不含连接信息、密钥及安全状态。 */
|
|
2337
|
+
|
|
2338
|
+
/**
|
|
2339
|
+
* 认证页面可读取的白名单配置;能力开关同时考虑实际运行时依赖,不能用于服务端授权。
|
|
2340
|
+
* @example
|
|
2341
|
+
* ```ts
|
|
2342
|
+
* import { iam, type IamPublicConfig } from '@h-ai/iam'
|
|
2343
|
+
* const config: IamPublicConfig | null = iam.publicConfig
|
|
2344
|
+
* if (config) {
|
|
2345
|
+
* const canOfferEmailOtp = config.login.email
|
|
2346
|
+
* const passwordMinLength = config.password.minLength
|
|
2347
|
+
* }
|
|
2348
|
+
* ```
|
|
2349
|
+
*/
|
|
2350
|
+
interface IamPublicConfig {
|
|
2351
|
+
/** 只公开是否需要交互挑战,不公开秘密或缓存状态。 */
|
|
2352
|
+
captcha: {
|
|
2353
|
+
enabled: boolean;
|
|
2354
|
+
};
|
|
2355
|
+
/** 页面显示密码策略,不包含密码哈希或账户信息。 */
|
|
2356
|
+
password: ReturnType<typeof PasswordConfigSchema.parse>;
|
|
2357
|
+
/** 自助注册开关;管理端创建账户仍需要独立授权。 */
|
|
2358
|
+
register: {
|
|
2359
|
+
enabled: boolean;
|
|
2360
|
+
};
|
|
2361
|
+
/** 方式开关同时考虑配置和发送回调就绪状态,避免展示无法使用的入口。 */
|
|
2362
|
+
login: {
|
|
2363
|
+
password: boolean;
|
|
2364
|
+
otp: boolean;
|
|
2365
|
+
ldap: boolean;
|
|
2366
|
+
email: boolean;
|
|
2367
|
+
sms: boolean;
|
|
2368
|
+
};
|
|
2369
|
+
/** 重置能力同时要求配置启用和通知发送回调就绪。 */
|
|
2370
|
+
passwordReset: {
|
|
2371
|
+
enabled: boolean;
|
|
2372
|
+
mode: 'email' | 'phone';
|
|
2373
|
+
};
|
|
2374
|
+
/** 单位:验证码位数、重发间隔秒;安全计数与期限只由服务端执行。 */
|
|
2375
|
+
otp: {
|
|
2376
|
+
length: number;
|
|
2377
|
+
resendInterval: number;
|
|
2378
|
+
};
|
|
2379
|
+
/** 用户协议及隐私政策展示配置。 */
|
|
2380
|
+
agreements: IamConfig['agreements'];
|
|
2381
|
+
/** 已经由外观 Schema 校验的标题、图像 URL 和布局。 */
|
|
2382
|
+
ui: ReturnType<typeof AuthUiConfigSchema.parse>;
|
|
2383
|
+
/** 默认 access 有效时长,单位秒;真实认证期限由服务端会话状态决定。 */
|
|
2384
|
+
session: {
|
|
2385
|
+
maxAge: number;
|
|
2386
|
+
endOnPageHide: boolean;
|
|
2387
|
+
};
|
|
2388
|
+
/** 仅公开操作清单,便于界面在选定操作前请求再鉴别。 */
|
|
2389
|
+
reauthentication: {
|
|
2390
|
+
enabled: boolean;
|
|
2391
|
+
operations: string[];
|
|
2392
|
+
};
|
|
2393
|
+
/** 邀请入口同时要求开关、密码重置与通知通道就绪。 */
|
|
2394
|
+
initialCredentials: {
|
|
2395
|
+
enabled: boolean;
|
|
2396
|
+
};
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
/**
|
|
2400
|
+
* IAM 错误码及 HTTP 状态定义;调用方按 code 分支,底层原因只用于脱敏诊断。
|
|
2401
|
+
* @example
|
|
2402
|
+
* ```ts
|
|
2403
|
+
* import { HaiIamError, iam } from '@h-ai/iam'
|
|
2404
|
+
* const result = await iam.auth.verifyToken('token-from-authorization-header')
|
|
2405
|
+
* if (!result.success && result.error.code === HaiIamError.SESSION_INVALID.code) {
|
|
2406
|
+
* // 由应用的认证边界转为重新登录响应,不泄露底层 cause。
|
|
2407
|
+
* }
|
|
2408
|
+
* ```
|
|
2409
|
+
*/
|
|
2410
|
+
declare const HaiIamError: {
|
|
2411
|
+
readonly AUTH_FAILED: _h_ai_core.HaiErrorDef;
|
|
2412
|
+
readonly INVALID_CREDENTIALS: _h_ai_core.HaiErrorDef;
|
|
2413
|
+
readonly USER_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
2414
|
+
readonly ACCOUNT_UNAVAILABLE: _h_ai_core.HaiErrorDef;
|
|
2415
|
+
readonly USER_LOCKED: _h_ai_core.HaiErrorDef;
|
|
2416
|
+
readonly USER_ALREADY_EXISTS: _h_ai_core.HaiErrorDef;
|
|
2417
|
+
readonly PASSWORD_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2418
|
+
readonly PASSWORD_POLICY_VIOLATION: _h_ai_core.HaiErrorDef;
|
|
2419
|
+
readonly OTP_INVALID: _h_ai_core.HaiErrorDef;
|
|
2420
|
+
readonly NOT_INITIALIZED: _h_ai_core.HaiErrorDef;
|
|
2421
|
+
readonly OTP_RESEND_TOO_FAST: _h_ai_core.HaiErrorDef;
|
|
2422
|
+
readonly LOGIN_DISABLED: _h_ai_core.HaiErrorDef;
|
|
2423
|
+
readonly REGISTER_DISABLED: _h_ai_core.HaiErrorDef;
|
|
2424
|
+
readonly STRATEGY_NOT_SUPPORTED: _h_ai_core.HaiErrorDef;
|
|
2425
|
+
readonly APIKEY_INVALID: _h_ai_core.HaiErrorDef;
|
|
2426
|
+
readonly APIKEY_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2427
|
+
readonly APIKEY_DISABLED: _h_ai_core.HaiErrorDef;
|
|
2428
|
+
readonly APIKEY_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
2429
|
+
readonly RESET_TOKEN_INVALID: _h_ai_core.HaiErrorDef;
|
|
2430
|
+
readonly RESET_TOKEN_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2431
|
+
readonly RESET_TOKEN_MAX_ATTEMPTS: _h_ai_core.HaiErrorDef;
|
|
2432
|
+
readonly OTP_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2433
|
+
readonly SECURITY_BUSY: _h_ai_core.HaiErrorDef;
|
|
2434
|
+
readonly PASSWORD_RESET_DISABLED: _h_ai_core.HaiErrorDef;
|
|
2435
|
+
readonly PASSWORD_CHANGE_REQUIRED: _h_ai_core.HaiErrorDef;
|
|
2436
|
+
readonly LOGIN_ACCESS_DENIED: _h_ai_core.HaiErrorDef;
|
|
2437
|
+
readonly REAUTHENTICATION_REQUIRED: _h_ai_core.HaiErrorDef;
|
|
2438
|
+
readonly INVITATION_DELIVERY_UNKNOWN: _h_ai_core.HaiErrorDef;
|
|
2439
|
+
readonly INVITATION_DISABLED: _h_ai_core.HaiErrorDef;
|
|
2440
|
+
readonly CAPTCHA_REQUIRED: _h_ai_core.HaiErrorDef;
|
|
2441
|
+
readonly CAPTCHA_INVALID: _h_ai_core.HaiErrorDef;
|
|
2442
|
+
readonly CAPTCHA_DISABLED: _h_ai_core.HaiErrorDef;
|
|
2443
|
+
readonly CAPTCHA_UNAVAILABLE: _h_ai_core.HaiErrorDef;
|
|
2444
|
+
readonly PASSWORD_REUSE_NOT_ALLOWED: _h_ai_core.HaiErrorDef;
|
|
2445
|
+
readonly SESSION_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
2446
|
+
readonly SESSION_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2447
|
+
readonly SESSION_INVALID: _h_ai_core.HaiErrorDef;
|
|
2448
|
+
readonly SESSION_CREATE_FAILED: _h_ai_core.HaiErrorDef;
|
|
2449
|
+
readonly TOKEN_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2450
|
+
readonly TOKEN_INVALID: _h_ai_core.HaiErrorDef;
|
|
2451
|
+
readonly TOKEN_REFRESH_FAILED: _h_ai_core.HaiErrorDef;
|
|
2452
|
+
readonly SESSION_LIMIT_EXCEEDED: _h_ai_core.HaiErrorDef;
|
|
2453
|
+
readonly PERMISSION_DENIED: _h_ai_core.HaiErrorDef;
|
|
2454
|
+
readonly ROLE_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
2455
|
+
readonly PERMISSION_NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
2456
|
+
readonly ROLE_ALREADY_EXISTS: _h_ai_core.HaiErrorDef;
|
|
2457
|
+
readonly PERMISSION_ALREADY_EXISTS: _h_ai_core.HaiErrorDef;
|
|
2458
|
+
readonly LDAP_CONNECTION_FAILED: _h_ai_core.HaiErrorDef;
|
|
2459
|
+
readonly LDAP_BIND_FAILED: _h_ai_core.HaiErrorDef;
|
|
2460
|
+
readonly LDAP_SEARCH_FAILED: _h_ai_core.HaiErrorDef;
|
|
2461
|
+
readonly TICKET_INVALID: _h_ai_core.HaiErrorDef;
|
|
2462
|
+
readonly TICKET_EXPIRED: _h_ai_core.HaiErrorDef;
|
|
2463
|
+
readonly TICKET_ISSUE_FAILED: _h_ai_core.HaiErrorDef;
|
|
2464
|
+
readonly REPOSITORY_ERROR: _h_ai_core.HaiErrorDef;
|
|
2465
|
+
readonly NOT_FOUND: _h_ai_core.HaiErrorDef;
|
|
2466
|
+
readonly CONFLICT: _h_ai_core.HaiErrorDef;
|
|
2467
|
+
readonly FORBIDDEN: _h_ai_core.HaiErrorDef;
|
|
2468
|
+
readonly INVALID_ARGUMENT: _h_ai_core.HaiErrorDef;
|
|
2469
|
+
readonly CONFIG_ERROR: _h_ai_core.HaiErrorDef;
|
|
2470
|
+
readonly INTERNAL_ERROR: _h_ai_core.HaiErrorDef;
|
|
2471
|
+
};
|
|
2472
|
+
/**
|
|
2473
|
+
* 已成功发布的认证通知上下文;发送服务必须按幂等键持久化去重。
|
|
2474
|
+
* 回调抛错代表送达结果未知;IAM 不自动重发,也不回滚已经发布的凭据。
|
|
2475
|
+
* @example
|
|
2476
|
+
* ```ts
|
|
2477
|
+
* import type { IamNotificationContext } from '@h-ai/iam'
|
|
2478
|
+
* async function deliver(context: IamNotificationContext, enqueue: (key: string, expiresAt: Date) => Promise<void>) {
|
|
2479
|
+
* // 接收端持久去重;回调失败代表送达未知,不能擅自生成新的幂等键重发。
|
|
2480
|
+
* await enqueue(context.idempotencyKey, context.expiresAt)
|
|
2481
|
+
* }
|
|
2482
|
+
* ```
|
|
2483
|
+
*/
|
|
2484
|
+
interface IamNotificationContext {
|
|
2485
|
+
/** 同一次凭据发布的稳定 ID,不包含验证码或明文令牌。 */
|
|
2486
|
+
readonly idempotencyKey: string;
|
|
2487
|
+
/** 凭据过期时间;迟到通知不得延长其有效期。 */
|
|
2488
|
+
readonly expiresAt: Date;
|
|
2489
|
+
}
|
|
2490
|
+
/**
|
|
2491
|
+
* IAM 初始化配置
|
|
2492
|
+
*
|
|
2493
|
+
* 包含 Zod 校验的设置字段和运行时依赖。
|
|
2494
|
+
* 传入 `iam.init()` 的唯一参数。
|
|
2495
|
+
* @example
|
|
2496
|
+
* ```ts
|
|
2497
|
+
* import type { IamConfigInput } from '@h-ai/iam'
|
|
2498
|
+
* const value: IamConfigInput = { login: { password: true, otp: false }, register: { enabled: false }, seedDefaultData: false }
|
|
2499
|
+
* ```
|
|
2500
|
+
*/
|
|
2501
|
+
interface IamConfigInput extends IamConfigSettingsInput {
|
|
2502
|
+
/**
|
|
2503
|
+
* 可选、已初始化的触达模块;由应用安装并注入,IAM 不导入或关闭该模块。
|
|
2504
|
+
* 自动使用 otp_email、otp_sms、password_reset、password_reset_sms 模板;显式发送回调优先。
|
|
2505
|
+
*/
|
|
2506
|
+
reach?: ReachFunctions;
|
|
2507
|
+
/** LDAP 客户端工厂(可选);启用 LDAP 且未注入时才加载应用安装的 ldapts。 */
|
|
2508
|
+
ldapClientFactory?: LdapClientFactory;
|
|
2509
|
+
/** LDAP 用户同步开关(默认 true) */
|
|
2510
|
+
ldapSyncUser?: boolean;
|
|
2511
|
+
/**
|
|
2512
|
+
* 密码重置令牌回调(可选)
|
|
2513
|
+
*
|
|
2514
|
+
* 当用户请求密码重置时,框架生成令牌后通过此回调通知业务层,
|
|
2515
|
+
* 业务层负责将令牌通过邮件/短信等渠道发送给用户。
|
|
2516
|
+
* 也可传入已初始化的 reach 自动发送;两者均未配置时重置功能不可用。
|
|
2517
|
+
*
|
|
2518
|
+
* @param user - 请求重置的用户信息
|
|
2519
|
+
* @param token - 重置令牌(明文,需发送给用户)
|
|
2520
|
+
* @param context - 令牌有效期及接收端必须复用的幂等键;发送失败/超时视为结果未知。
|
|
2521
|
+
*/
|
|
2522
|
+
onPasswordResetRequest?: (user: User, token: string, context: IamNotificationContext) => Promise<void>;
|
|
2523
|
+
/**
|
|
2524
|
+
* OTP 邮件验证码发送回调(可选)
|
|
2525
|
+
*
|
|
2526
|
+
* 当用户请求 OTP 验证码时,框架生成验证码后通过此回调通知业务层,
|
|
2527
|
+
* 业务层负责将验证码通过邮件发送给用户。
|
|
2528
|
+
* 显式回调覆盖 Reach 的 otp_email 模板;两者均不可用时该登录入口关闭。
|
|
2529
|
+
* 投递失败须拒绝 Promise,异常由 IAM 转为业务结果,不要在回调中吞掉失败。
|
|
2530
|
+
*
|
|
2531
|
+
* @param email - 目标邮箱
|
|
2532
|
+
* @param code - 验证码
|
|
2533
|
+
*/
|
|
2534
|
+
onOtpSendEmail?: (email: string, code: string, context: IamNotificationContext) => Promise<void>;
|
|
2535
|
+
/**
|
|
2536
|
+
* OTP 短信验证码发送回调(可选)
|
|
2537
|
+
*
|
|
2538
|
+
* 当用户请求 OTP 验证码时,框架生成验证码后通过此回调通知业务层,
|
|
2539
|
+
* 业务层负责将验证码通过短信发送给用户。
|
|
2540
|
+
* 显式回调覆盖 Reach 的 otp_sms 模板;两者均不可用时该登录入口关闭。
|
|
2541
|
+
* 投递失败须拒绝 Promise,异常由 IAM 转为业务结果,不要在回调中吞掉失败。
|
|
2542
|
+
*
|
|
2543
|
+
* @param phone - 目标手机号
|
|
2544
|
+
* @param code - 验证码
|
|
2545
|
+
*/
|
|
2546
|
+
onOtpSendSms?: (phone: string, code: string, context: IamNotificationContext) => Promise<void>;
|
|
2547
|
+
}
|
|
2548
|
+
/**
|
|
2549
|
+
* IAM 函数接口
|
|
2550
|
+
*
|
|
2551
|
+
* 统一聚合所有 IAM 功能。
|
|
2552
|
+
* @example
|
|
2553
|
+
* ```ts
|
|
2554
|
+
* import { iam, type IamFunctions } from '@h-ai/iam'
|
|
2555
|
+
* const service: IamFunctions = iam
|
|
2556
|
+
* // 应用先初始化 Cache、RelDB、Crypto;关闭时先停止接收请求。
|
|
2557
|
+
* const initialized = await service.init({ seedDefaultData: false })
|
|
2558
|
+
* if (initialized.success) {
|
|
2559
|
+
* const users = await service.user.listUsers({ page: 1, pageSize: 20 })
|
|
2560
|
+
* if (!users.success)
|
|
2561
|
+
* users.error.code
|
|
2562
|
+
* await service.close() // IAM 不关闭应用注入的依赖。
|
|
2563
|
+
* }
|
|
2564
|
+
* ```
|
|
2565
|
+
*/
|
|
2566
|
+
interface IamFunctions {
|
|
2567
|
+
/**
|
|
2568
|
+
* 初始化 IAM 服务
|
|
2569
|
+
*
|
|
2570
|
+
* 已初始化时先关闭旧内部状态再重新初始化;并发初始化返回配置错误。
|
|
2571
|
+
* 内部按依赖顺序创建 session → authz → authn → user 子功能,
|
|
2572
|
+
* 并可选执行种子数据初始化。
|
|
2573
|
+
*
|
|
2574
|
+
* @param config - IAM 初始化配置(可选 session/password/LDAP 等策略配置)
|
|
2575
|
+
* @returns 成功返回 `ok(undefined)`;失败返回含错误码的 `err`
|
|
2576
|
+
*
|
|
2577
|
+
* @example
|
|
2578
|
+
* ```ts
|
|
2579
|
+
* import { iam } from '@h-ai/iam'
|
|
2580
|
+
* // 共享 RelDB、Cache 已由应用初始化;IAM 会确保 Crypto 可用。
|
|
2581
|
+
* const result = await iam.init({ session: { maxAge: 86400 } })
|
|
2582
|
+
* if (!result.success)
|
|
2583
|
+
* result.error.code // 交给应用统一错误边界,不能继续接收认证请求。
|
|
2584
|
+
* ```
|
|
2585
|
+
*/
|
|
2586
|
+
init: (config: IamConfigInput) => Promise<HaiResult<void>>;
|
|
2587
|
+
/**
|
|
2588
|
+
* 关闭 IAM 服务
|
|
2589
|
+
*
|
|
2590
|
+
* 释放所有内部子功能引用,将模块恢复到未初始化状态。
|
|
2591
|
+
* 关闭后访问 `iam.auth` 等属性将返回未初始化错误。
|
|
2592
|
+
*/
|
|
2593
|
+
close: () => Promise<void>;
|
|
2594
|
+
/** 当前脱敏后的配置快照(未初始化时为 null) */
|
|
2595
|
+
readonly config: IamConfig | null;
|
|
2596
|
+
/** 安全的文件配置快照;含数据库敏感规则的有效配置使用 getPublicConfig()。 */
|
|
2597
|
+
readonly publicConfig: IamPublicConfig | null;
|
|
2598
|
+
/**
|
|
2599
|
+
* 读取包含文件与当前RBAC数据库敏感操作并集的公开配置;数据库读取失败返回失败。
|
|
2600
|
+
* @returns 独立公开快照;未初始化或启用策略时数据库故障返回 HaiResult 失败。
|
|
2601
|
+
* @example
|
|
2602
|
+
* ```ts
|
|
2603
|
+
* import { iam } from '@h-ai/iam'
|
|
2604
|
+
* const config = await iam.getPublicConfig()
|
|
2605
|
+
* if (config.success) config.data.reauthentication.operations
|
|
2606
|
+
* ```
|
|
2607
|
+
*/
|
|
2608
|
+
getPublicConfig: () => Promise<HaiResult<IamPublicConfig>>;
|
|
2609
|
+
/** 是否已初始化 */
|
|
2610
|
+
readonly isInitialized: boolean;
|
|
2611
|
+
/** 注册功能是否启用(未初始化或未配置时默认启用) */
|
|
2612
|
+
readonly isRegisterEnabled: boolean;
|
|
2613
|
+
/** 认证操作(登录、登出、令牌验证等) */
|
|
2614
|
+
readonly auth: AuthnOperations;
|
|
2615
|
+
/** 用户管理(注册、查询、更新、密码管理等) */
|
|
2616
|
+
readonly user: UserOperations;
|
|
2617
|
+
/** 授权管理(角色/权限 CRUD、用户角色分配、权限检查) */
|
|
2618
|
+
readonly authz: AuthzOperations;
|
|
2619
|
+
/** 会话管理(创建、查询、验证、删除会话) */
|
|
2620
|
+
readonly session: SessionOperations;
|
|
2621
|
+
/** API Key 管理(创建、列表、吊销、验证),未启用 apikey 登录时返回未初始化代理 */
|
|
2622
|
+
readonly apiKey: ApiKeyOperations;
|
|
2623
|
+
/** 一次性能力票据(签发 / 原子消费短期票据,用于 WebSocket 等无法携带 Bearer Token 的通道) */
|
|
2624
|
+
readonly ticket: TicketOperations;
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
export { AccountConfigSchema, type AgreementConfig, AgreementConfigSchema, type AgreementDisplay, type ApiKey, type ApiKeyConfig, ApiKeyConfigSchema, type ApiKeyCredentials, type ApiKeyOperations, type ApiKeySortDirection, type ApiKeySortField, type AuthRequestContext, type AuthResult, type AuthStrategy, type AuthStrategyType, AuthStrategyTypeSchema, type AuthUiConfig, AuthUiConfigSchema, type AuthnOperations, type AuthzOperations, CaptchaConfigSchema, type CaptchaOperation, type ConsumeTicketOptions, type ConsumedTicket, type CreateApiKeyOptions, type CreateApiKeyResult, type CreateSessionOptions, type Credentials, HaiIamError, type IamCaptchaChallenge, type IamConfig, type IamConfigInput, IamConfigSchema, type IamConfigSettingsInput, type IamFunctions, type IamNotificationContext, type IamPublicConfig, type IssueTicketOptions, type IssuedTicket, type LdapClientFactory, type LdapConfig, LdapConfigSchema, type LdapCredentials, type ListApiKeysOptions, type ListUsersOptions, LoginAccessConfigSchema, 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, type UserSortDirection, type UserSortField };
|