@longzai-intelligence-auth/elysia 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,32 +1,239 @@
1
1
  import { DomainError } from "@longzai-intelligence/error";
2
- import { JwtConfig, LoggerService, RateLimiterPort, ResourceAction, TenantValidateFn, TokenVerifierPort, UnifiedAuthContext, UnifiedAuthContext as UnifiedAuthContext$1 } from "@longzai-intelligence-auth/core";
2
+ import { JwtConfig, LoggerService, RateLimiterPort, ResourceAction, TokenVerifierPort, UnifiedAuthContext, UnifiedAuthContext as UnifiedAuthContext$1 } from "@longzai-intelligence-auth/core";
3
3
  import { Elysia } from "elysia";
4
4
  import { z } from "zod";
5
5
 
6
6
  //#region src/core/error-mapper.d.ts
7
- /** 根据 DomainError 类型映射 HTTP 状态码 */
7
+ /**
8
+ * 根据 DomainError 类型映射 HTTP 状态码
9
+ *
10
+ * @param error - 领域错误实例
11
+ * @returns 对应的 HTTP 状态码
12
+ */
8
13
  declare function mapDomainErrorToStatus(error: DomainError): number;
9
14
  //#endregion
15
+ //#region src/verifiers/static-token-verifier.d.ts
16
+ /**
17
+ * 静态令牌验证器配置
18
+ */
19
+ type StaticTokenVerifierOptions = {
20
+ /**
21
+ * 静态令牌(与 getToken 至少提供一个)
22
+ */
23
+ token?: string;
24
+ /**
25
+ * 动态获取令牌的回调,优先级高于 token
26
+ * 用于运行时从 secret manager / env 缓存等来源解析令牌
27
+ */
28
+ getToken?: () => string | null;
29
+ /**
30
+ * 验证通过后填充到 AccessTokenPayload.sub 中的主体标识
31
+ * 默认 "static"
32
+ */
33
+ subject?: string;
34
+ /**
35
+ * 验证通过后填充到 AccessTokenPayload.tenantId 中的租户标识
36
+ * 默认不设置
37
+ */
38
+ tenantId?: string;
39
+ };
40
+ /**
41
+ * 创建静态令牌验证器
42
+ *
43
+ * @param options - 静态令牌验证器配置
44
+ * @returns 实现 TokenVerifierPort 的静态令牌验证器
45
+ */
46
+ declare function createStaticTokenVerifier(options: StaticTokenVerifierOptions): TokenVerifierPort;
47
+ //#endregion
48
+ //#region src/extractors/types.d.ts
49
+ /**
50
+ * 凭证提取器类型定义
51
+ *
52
+ * 提取器负责从 Request 中提取凭证字符串,与"如何验证凭证"完全解耦。
53
+ * 同一种提取器(如 cookie)可以配合任意验证器(JWT、静态 token、远程校验等)。
54
+ */
55
+ /**
56
+ * 凭证提取器接口
57
+ *
58
+ * 一个提取器回答一个问题:这个请求里有没有携带凭证,如果有,凭证字符串是什么?
59
+ * 提取器不做任何验证,仅负责"取出"。
60
+ */
61
+ type CredentialExtractor = {
62
+ /**
63
+ * 从请求中提取凭证字符串
64
+ *
65
+ * @param request - Web 标准 Request 对象
66
+ * @returns 凭证字符串;请求未携带凭证时返回 null
67
+ */
68
+ extract(request: Request): string | null;
69
+ };
70
+ /**
71
+ * 请求级令牌验证器接口
72
+ *
73
+ * 与 TokenVerifierPort(输入字符串)解耦:RequestTokenVerifier 接受 Request,
74
+ * 内部组合"提取器 + 令牌验证器",对调用方屏蔽凭证载体细节。
75
+ *
76
+ * 用于在 plugin 层组合多种载体(cookie、bearer、api-key header)与多种算法(JWT、静态比较)。
77
+ */
78
+ type RequestTokenVerifier = {
79
+ /**
80
+ * 验证请求中的凭证
81
+ *
82
+ * @param request - Web 标准 Request 对象
83
+ * @returns 验证结果,含 AccessTokenPayload 或错误信息
84
+ */
85
+ verify(request: Request): Promise<import("@longzai-intelligence-auth/core").TokenVerifyResult<import("@longzai-intelligence-auth/core").AccessTokenPayload>>;
86
+ };
87
+ //#endregion
88
+ //#region src/extractors/cookie-extractor.d.ts
89
+ /**
90
+ * Cookie 提取器配置
91
+ */
92
+ type CookieExtractorOptions = {
93
+ /**
94
+ * Cookie 名称(必填)
95
+ */
96
+ cookieName: string;
97
+ };
98
+ /**
99
+ * 创建 Cookie 凭证提取器
100
+ *
101
+ * 兼容 Elysia/Bun 的 cookie 解析行为:仅做简单的"key=value"拆分,
102
+ * 不处理属性(Domain/Path/Expires/Secure/HttpOnly/SameSite 等)。
103
+ * 复杂场景下建议消费方自行解析后通过其他 extractor 注入。
104
+ *
105
+ * @param options - Cookie 提取器配置
106
+ * @returns 实现 CredentialExtractor 的 cookie 提取器
107
+ */
108
+ declare function createCookieExtractor(options: CookieExtractorOptions): CredentialExtractor;
109
+ //#endregion
110
+ //#region src/extractors/bearer-extractor.d.ts
111
+ /**
112
+ * Bearer 提取器配置
113
+ */
114
+ type BearerExtractorOptions = {
115
+ /**
116
+ * Authorization 头字段名(默认 "authorization")
117
+ * 大小写不敏感(HTTP header 字段名本身大小写不敏感)
118
+ */
119
+ headerName?: string;
120
+ };
121
+ /**
122
+ * 创建 Bearer 凭证提取器
123
+ *
124
+ * 兼容标准 "Authorization: Bearer <token>" 格式,
125
+ * 不做任何令牌格式校验(由 verifier 负责)。
126
+ *
127
+ * @param options - Bearer 提取器配置
128
+ * @returns 实现 CredentialExtractor 的 bearer 提取器
129
+ */
130
+ declare function createBearerExtractor(options?: BearerExtractorOptions): CredentialExtractor;
131
+ //#endregion
132
+ //#region src/verifiers/cookie-jwt-verifier.d.ts
133
+ /**
134
+ * Cookie JWT 验证器配置
135
+ */
136
+ type CookieJwtVerifierOptions = {
137
+ /**
138
+ * Cookie 名称(承载 JWT 的 cookie 字段名)
139
+ */
140
+ cookieName: string;
141
+ /**
142
+ * JWT 配置(与 verifier 二选一)
143
+ * 提供时内部创建 JwtTokenVerifier 实例
144
+ */
145
+ jwt?: JwtConfig;
146
+ /**
147
+ * 已构造的 TokenVerifierPort(与 jwt 二选一)
148
+ * 用于注入自定义 verifier(如多策略、远程校验等)
149
+ */
150
+ verifier?: TokenVerifierPort;
151
+ /**
152
+ * 自定义凭证提取器(高级用法)
153
+ * 默认使用 createCookieExtractor({ cookieName })
154
+ */
155
+ extractor?: CredentialExtractor;
156
+ };
157
+ /**
158
+ * 创建 Cookie JWT 验证器
159
+ *
160
+ * @param options - Cookie JWT 验证器配置
161
+ * @returns 实现 RequestTokenVerifier 的组合验证器
162
+ */
163
+ declare function createCookieJwtVerifier(options: CookieJwtVerifierOptions): RequestTokenVerifier;
164
+ //#endregion
10
165
  //#region src/plugins/jwt-verify.plugin.d.ts
11
166
  /** JWT 验证插件解析后的认证上下文 */
12
167
  type VerifiedAuthContext = {
13
- /** 用户 ID(来自 payload.sub) */userId: string; /** 租户 ID(来自 payload.tenantId,默认使用 defaultTenantId) */
14
- tenantId: string; /** 角色列表 */
15
- roles: string[]; /** 权限列表 */
16
- permissions: string[]; /** JWT ID(用于 token 黑名单检查) */
168
+ /**
169
+ * 用户 ID(来自 payload.sub)
170
+ */
171
+ userId: string;
172
+ /**
173
+ * 租户 ID(来自 payload.tenantId,默认使用 defaultTenantId)
174
+ */
175
+ tenantId: string;
176
+ /**
177
+ * 角色列表
178
+ */
179
+ roles: string[];
180
+ /**
181
+ * 权限列表
182
+ */
183
+ permissions: string[];
184
+ /**
185
+ * JWT ID(用于 token 黑名单检查)
186
+ */
17
187
  jti?: string;
18
188
  };
189
+ /**
190
+ * JWT 验证插件配置选项
191
+ *
192
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
193
+ */
19
194
  type JwtVerifyPluginOptions<T extends z.ZodTypeAny = z.ZodUndefined> = {
20
- /** 令牌验证器端口(用户注入,与 jwt 二选一) */verifier?: TokenVerifierPort; /** JWT 配置(与 verifier 二选一,未提供时使用默认配置) */
21
- jwt?: JwtConfig; /** 默认租户 ID */
22
- defaultTenantId?: string; /** 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*") */
23
- publicPaths?: string[]; /** 扩展 schema,用于从 payload 中解析自定义业务字段 */
195
+ /**
196
+ * 令牌验证器端口(用户注入,与 jwt 二选一)
197
+ */
198
+ verifier?: TokenVerifierPort;
199
+ /**
200
+ * JWT 配置(与 verifier 二选一,未提供时使用默认配置)
201
+ */
202
+ jwt?: JwtConfig;
203
+ /**
204
+ * 默认租户 ID
205
+ */
206
+ defaultTenantId?: string;
207
+ /**
208
+ * 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*")
209
+ */
210
+ publicPaths?: string[];
211
+ /**
212
+ * 扩展 schema,用于从 payload 中解析自定义业务字段
213
+ */
24
214
  extendSchema?: T;
25
215
  };
26
- /** 从 extendSchema 提取额外字段类型 */
216
+ /**
217
+ * 从 extendSchema 提取额外字段类型
218
+ *
219
+ * @typeParam T - 扩展 schema 类型,用于推断额外的业务字段类型
220
+ */
27
221
  type ExtractExtra<T extends z.ZodTypeAny> = T extends z.ZodUndefined ? Record<string, never> : z.infer<T>;
28
- /** 完整的认证上下文类型(基础字段 + 扩展字段) */
222
+ /**
223
+ * 完整的认证上下文类型(基础字段 + 扩展字段)
224
+ *
225
+ * @typeParam T - 扩展 schema 类型,用于推断额外的业务字段类型
226
+ */
29
227
  type AuthContextWithExtra<T extends z.ZodTypeAny = z.ZodUndefined> = VerifiedAuthContext & ExtractExtra<T>;
228
+ /**
229
+ * 创建 JWT 验证插件
230
+ *
231
+ * 该插件从请求头中提取 Bearer token 并验证,验证失败时抛出异常。
232
+ *
233
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
234
+ * @param options - 插件配置选项
235
+ * @returns Elysia 插件实例
236
+ */
30
237
  declare function createJwtVerifyPlugin<T extends z.ZodTypeAny = z.ZodUndefined>(options: JwtVerifyPluginOptions<T>): Elysia<"", {
31
238
  decorator: {};
32
239
  store: {};
@@ -61,16 +268,46 @@ declare function createJwtVerifyPlugin<T extends z.ZodTypeAny = z.ZodUndefined>(
61
268
  }>;
62
269
  //#endregion
63
270
  //#region src/plugins/optional-jwt.plugin.d.ts
271
+ /**
272
+ * 可选 JWT 认证插件配置选项
273
+ *
274
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
275
+ */
64
276
  type OptionalJwtPluginOptions<T extends z.ZodTypeAny = z.ZodUndefined> = {
65
- /** 令牌验证器端口(用户注入,与 jwt 二选一) */verifier?: TokenVerifierPort; /** JWT 配置(与 verifier 二选一,未提供时使用默认配置) */
66
- jwt?: JwtConfig; /** 默认租户 ID */
67
- defaultTenantId?: string; /** 扩展 schema,用于从 payload 中解析自定义业务字段 */
277
+ /**
278
+ * 令牌验证器端口(用户注入,与 jwt 二选一)
279
+ */
280
+ verifier?: TokenVerifierPort;
281
+ /**
282
+ * JWT 配置(与 verifier 二选一,未提供时使用默认配置)
283
+ */
284
+ jwt?: JwtConfig;
285
+ /**
286
+ * 默认租户 ID
287
+ */
288
+ defaultTenantId?: string;
289
+ /**
290
+ * 扩展 schema,用于从 payload 中解析自定义业务字段
291
+ */
68
292
  extendSchema?: T;
69
293
  };
70
- /** 可选认证上下文,auth 为 null 表示未认证 */
294
+ /**
295
+ * 可选认证上下文,auth 为 null 表示未认证
296
+ *
297
+ * @typeParam T - 扩展 schema 类型,用于推断额外的业务字段类型
298
+ */
71
299
  type OptionalAuthContext<T extends z.ZodTypeAny = z.ZodUndefined> = {
72
300
  auth: (UnifiedAuthContext$1 & (T extends z.ZodUndefined ? Record<string, never> : z.infer<T>)) | null;
73
301
  };
302
+ /**
303
+ * 创建可选 JWT 认证插件
304
+ *
305
+ * 该插件从请求头中提取 Bearer token 并验证,如果验证失败或无 token 则返回 null 认证上下文。
306
+ *
307
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
308
+ * @param options - 插件配置选项
309
+ * @returns Elysia 插件实例
310
+ */
74
311
  declare function createOptionalJwtPlugin<T extends z.ZodTypeAny = z.ZodUndefined>(options: OptionalJwtPluginOptions<T>): Elysia<"", {
75
312
  decorator: {};
76
313
  store: {};
@@ -107,11 +344,26 @@ declare function createOptionalJwtPlugin<T extends z.ZodTypeAny = z.ZodUndefined
107
344
  //#region src/plugins/rbac.plugin.d.ts
108
345
  /** 用户角色策略接口,用于数据库查询权限 */
109
346
  type UserRoleStrategy = {
110
- /** 查询用户在指定租户下的所有权限 */findPermissionsByUserId(userId: string, tenantId: string): Promise<ResourceAction[]>;
347
+ /**
348
+ * 查询用户在指定租户下的所有权限
349
+ */
350
+ findPermissionsByUserId(userId: string, tenantId: string): Promise<ResourceAction[]>;
111
351
  };
352
+ /**
353
+ * RBAC 插件配置选项
354
+ */
112
355
  type RbacPluginOptions = {
113
- /** 用户角色策略(用于数据库查询权限,优先于上下文权限) */userRole?: UserRoleStrategy;
356
+ /**
357
+ * 用户角色策略(用于数据库查询权限,优先于上下文权限)
358
+ */
359
+ userRole?: UserRoleStrategy;
114
360
  };
361
+ /**
362
+ * 创建 RBAC 插件
363
+ *
364
+ * @param options - 插件配置选项
365
+ * @returns Elysia 插件实例
366
+ */
115
367
  declare function createRbacPlugin(options?: RbacPluginOptions): Elysia<"", {
116
368
  decorator: {};
117
369
  store: {};
@@ -148,15 +400,42 @@ declare function createRbacPlugin(options?: RbacPluginOptions): Elysia<"", {
148
400
  //#region src/plugins/api-key.plugin.d.ts
149
401
  /** API Key 认证的主体信息 */
150
402
  type ApiKeyPrincipal = {
151
- /** 主体标识 */principalId: string; /** 租户 ID */
152
- tenantId?: string; /** 角色列表 */
153
- roles: string[]; /** 权限列表 */
403
+ /**
404
+ * 主体标识
405
+ */
406
+ principalId: string;
407
+ /**
408
+ * 租户 ID
409
+ */
410
+ tenantId?: string;
411
+ /**
412
+ * 角色列表
413
+ */
414
+ roles: string[];
415
+ /**
416
+ * 权限列表
417
+ */
154
418
  permissions: string[];
155
419
  };
420
+ /**
421
+ * API Key 认证插件配置选项
422
+ */
156
423
  type ApiKeyPluginOptions = {
157
- /** API Key 所在的请求头名称(默认 "x-api-key") */header?: string; /** API Key 验证器,返回 null 表示无效 */
424
+ /**
425
+ * API Key 所在的请求头名称(默认 "x-api-key")
426
+ */
427
+ header?: string;
428
+ /**
429
+ * API Key 验证器,返回 null 表示无效
430
+ */
158
431
  validator: (key: string) => Promise<ApiKeyPrincipal | null>;
159
432
  };
433
+ /**
434
+ * 创建 API Key 认证插件
435
+ *
436
+ * @param options - 插件配置选项
437
+ * @returns Elysia 插件实例
438
+ */
160
439
  declare function createApiKeyPlugin(options: ApiKeyPluginOptions): Elysia<"", {
161
440
  decorator: {};
162
441
  store: {};
@@ -193,9 +472,21 @@ declare function createApiKeyPlugin(options: ApiKeyPluginOptions): Elysia<"", {
193
472
  //#region src/plugins/in-memory-api-key-validator.d.ts
194
473
  /** 内存 API Key 映射条目 */
195
474
  type InMemoryApiKeyEntry = {
196
- /** 主体标识 */principalId: string; /** 租户ID(可选) */
197
- tenantId?: string; /** 角色列表 */
198
- roles: string[]; /** 权限列表 */
475
+ /**
476
+ * 主体标识
477
+ */
478
+ principalId: string;
479
+ /**
480
+ * 租户ID(可选)
481
+ */
482
+ tenantId?: string;
483
+ /**
484
+ * 角色列表
485
+ */
486
+ roles: string[];
487
+ /**
488
+ * 权限列表
489
+ */
199
490
  permissions: string[];
200
491
  };
201
492
  /** 内存 API Key 映射表 */
@@ -203,22 +494,64 @@ type InMemoryApiKeyMap = Record<string, InMemoryApiKeyEntry>;
203
494
  /**
204
495
  * 创建内存 API Key 验证器
205
496
  * 接受静态 API Key 映射,返回符合 createApiKeyPlugin 的 validator 函数
497
+ *
206
498
  * @param keyMap - API Key 到主体信息的映射
207
499
  * @returns validator 函数
208
500
  */
209
501
  declare function createInMemoryApiKeyValidator(keyMap: InMemoryApiKeyMap): (key: string) => Promise<ApiKeyPrincipal | null>;
210
502
  //#endregion
211
503
  //#region src/plugins/composite-auth.plugin.d.ts
504
+ /**
505
+ * 组合认证插件配置选项
506
+ *
507
+ * 支持多种认证方式(JWT 和 API Key),优先尝试 JWT 认证。
508
+ */
212
509
  type CompositeAuthPluginOptions = {
213
- /** 令牌验证器端口(用户注入,与 jwt 二选一) */verifier?: TokenVerifierPort; /** JWT 配置(与 verifier 二选一,未提供时使用默认配置) */
214
- jwt?: JwtConfig; /** 默认租户 ID */
215
- defaultTenantId?: string; /** API Key 验证器 */
216
- apiKeyValidator?: (key: string) => Promise<ApiKeyPrincipal | null>; /** API Key 所在的请求头名称(默认 "x-api-key") */
217
- apiKeyHeader?: string; /** 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*") */
510
+ /**
511
+ * 令牌验证器端口(用户注入,与 jwt 二选一)
512
+ *
513
+ * 单个验证器场景的简写形式;与 verifiers 同时提供时会合并到列表头部。
514
+ */
515
+ verifier?: TokenVerifierPort;
516
+ /**
517
+ * 令牌验证器端口列表(多验证器场景)
518
+ *
519
+ * 按数组顺序依次尝试,任一成功即返回;
520
+ * 用于多模式共存(如同时支持 cookie JWT 与静态令牌、新旧密钥平滑迁移等)。
521
+ */
522
+ verifiers?: TokenVerifierPort[];
523
+ /**
524
+ * JWT 配置(与 verifier/verifiers 二选一,未提供时使用默认配置)
525
+ */
526
+ jwt?: JwtConfig;
527
+ /**
528
+ * 默认租户 ID
529
+ */
530
+ defaultTenantId?: string;
531
+ /**
532
+ * API Key 验证器
533
+ */
534
+ apiKeyValidator?: (key: string) => Promise<ApiKeyPrincipal | null>;
535
+ /**
536
+ * API Key 所在的请求头名称(默认 "x-api-key")
537
+ */
538
+ apiKeyHeader?: string;
539
+ /**
540
+ * 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*")
541
+ */
218
542
  publicPaths?: string[];
219
543
  };
220
544
  /** 组合认证上下文(向后兼容,内部使用 UnifiedAuthContext) */
221
545
  type CompositeAuthContext = UnifiedAuthContext$1;
546
+ /**
547
+ * 创建组合认证插件
548
+ *
549
+ * 该插件支持多种认证方式(JWT 和 API Key),优先尝试 JWT 认证,
550
+ * 如果 JWT 认证失败则尝试 API Key 认证,未认证时返回 null。
551
+ *
552
+ * @param options - 插件配置选项
553
+ * @returns Elysia 插件实例
554
+ */
222
555
  declare function createCompositeAuthPlugin(options: CompositeAuthPluginOptions): Elysia<"", {
223
556
  decorator: {};
224
557
  store: {};
@@ -251,7 +584,12 @@ declare function createCompositeAuthPlugin(options: CompositeAuthPluginOptions):
251
584
  standaloneSchema: {};
252
585
  response: {};
253
586
  }>;
254
- /** 创建要求认证的组合认证插件(未认证时抛出异常) */
587
+ /**
588
+ * 创建要求认证的组合认证插件(未认证时抛出异常)
589
+ *
590
+ * @param options - 插件配置选项
591
+ * @returns Elysia 插件实例
592
+ */
255
593
  declare function createRequiredCompositeAuthPlugin(options: CompositeAuthPluginOptions): Elysia<"", {
256
594
  decorator: {};
257
595
  store: {};
@@ -285,50 +623,30 @@ declare function createRequiredCompositeAuthPlugin(options: CompositeAuthPluginO
285
623
  response: {};
286
624
  }>;
287
625
  //#endregion
288
- //#region src/plugins/tenant.plugin.d.ts
289
- type TenantPluginOptions = {
290
- /** 默认租户 ID */defaultTenantId?: string; /** 租户验证函数 */
291
- validateFn?: TenantValidateFn;
292
- };
293
- declare function createTenantPlugin(options?: TenantPluginOptions): Elysia<"", {
294
- decorator: {};
295
- store: {};
296
- derive: {
297
- tenantId: string;
298
- };
299
- resolve: {};
300
- }, {
301
- typebox: {};
302
- error: {};
303
- }, {
304
- schema: {};
305
- standaloneSchema: {};
306
- macro: {};
307
- macroFn: {};
308
- parser: {};
309
- response: import("elysia").ExtractErrorFromHandle<{
310
- tenantId: string;
311
- }> & {};
312
- }, {}, {
313
- derive: {};
314
- resolve: {};
315
- schema: {};
316
- standaloneSchema: {};
317
- response: {};
318
- }, {
319
- derive: {};
320
- resolve: {};
321
- schema: {};
322
- standaloneSchema: {};
323
- response: {};
324
- }>;
325
- //#endregion
326
626
  //#region src/plugins/rate-limit.plugin.d.ts
627
+ /**
628
+ * 速率限制插件配置选项
629
+ */
327
630
  type RateLimitPluginOptions = {
328
- /** 速率限制器端口(用户注入) */limiter: RateLimiterPort; /** 日志服务 */
329
- logger?: LoggerService; /** 键生成器(用于从请求中提取限制键) */
631
+ /**
632
+ * 速率限制器端口(用户注入)
633
+ */
634
+ limiter: RateLimiterPort;
635
+ /**
636
+ * 日志服务
637
+ */
638
+ logger?: LoggerService;
639
+ /**
640
+ * 键生成器(用于从请求中提取限制键)
641
+ */
330
642
  keyGenerator?: (request: Request) => string;
331
643
  };
644
+ /**
645
+ * 创建速率限制插件
646
+ *
647
+ * @param options - 插件配置选项
648
+ * @returns Elysia 插件实例
649
+ */
332
650
  declare function createRateLimitPlugin(options: RateLimitPluginOptions): Elysia<"", {
333
651
  decorator: {};
334
652
  store: {};
@@ -444,13 +762,42 @@ declare function createLoggerPlugin(options: LoggerPluginOptions): Elysia<"", {
444
762
  }>;
445
763
  //#endregion
446
764
  //#region src/presets/basic-preset.d.ts
765
+ /**
766
+ * 基础预设配置选项
767
+ *
768
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
769
+ */
447
770
  type BasicPresetOptions<T extends z.ZodTypeAny = z.ZodUndefined> = {
448
- /** 令牌验证器端口(用户注入) */tokenVerifier: TokenVerifierPort; /** 默认租户 ID */
449
- defaultTenantId?: string; /** 日志服务 */
450
- logger?: LoggerService; /** 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*") */
451
- publicPaths?: string[]; /** 扩展 schema,用于从 payload 中解析自定义业务字段 */
771
+ /**
772
+ * 令牌验证器端口(用户注入)
773
+ */
774
+ tokenVerifier: TokenVerifierPort;
775
+ /**
776
+ * 默认租户 ID
777
+ */
778
+ defaultTenantId?: string;
779
+ /**
780
+ * 日志服务
781
+ */
782
+ logger?: LoggerService;
783
+ /**
784
+ * 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*")
785
+ */
786
+ publicPaths?: string[];
787
+ /**
788
+ * 扩展 schema,用于从 payload 中解析自定义业务字段
789
+ */
452
790
  extendSchema?: T;
453
791
  };
792
+ /**
793
+ * 创建基础预设插件
794
+ *
795
+ * 该预设包含 JWT 验证插件和错误处理插件,提供完整的认证功能。
796
+ *
797
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
798
+ * @param options - 预设配置选项
799
+ * @returns Elysia 插件实例
800
+ */
454
801
  declare function createBasicPreset<T extends z.ZodTypeAny = z.ZodUndefined>(options: BasicPresetOptions<T>): Elysia<"", {
455
802
  decorator: {};
456
803
  store: {};
@@ -517,24 +864,59 @@ declare function createBasicPreset<T extends z.ZodTypeAny = z.ZodUndefined>(opti
517
864
  }>;
518
865
  //#endregion
519
866
  //#region src/presets/standard-preset.d.ts
867
+ /**
868
+ * 标准预设配置选项
869
+ *
870
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
871
+ */
520
872
  type StandardPresetOptions<T extends z.ZodTypeAny = z.ZodUndefined> = {
521
- /** 令牌验证器端口(用户注入) */tokenVerifier: TokenVerifierPort; /** 速率限制器端口(可选,用户注入) */
522
- rateLimiter?: RateLimiterPort; /** 默认租户 ID */
523
- defaultTenantId?: string; /** 租户验证函数 */
524
- validateFn?: TenantValidateFn; /** 用户角色策略 */
525
- userRole?: UserRoleStrategy; /** 日志服务 */
526
- logger?: LoggerService; /** 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*") */
527
- publicPaths?: string[]; /** 扩展 schema,用于从 payload 中解析自定义业务字段(仅在未使用 apiKeyValidator 时生效) */
528
- extendSchema?: T; /** API Key 验证器(提供后使用组合认证插件替代 JWT 验证插件) */
873
+ /**
874
+ * 令牌验证器端口(用户注入)
875
+ */
876
+ tokenVerifier: TokenVerifierPort;
877
+ /**
878
+ * 速率限制器端口(可选,用户注入)
879
+ */
880
+ rateLimiter?: RateLimiterPort;
881
+ /**
882
+ * 默认租户 ID
883
+ */
884
+ defaultTenantId?: string;
885
+ /**
886
+ * 用户角色策略
887
+ */
888
+ userRole?: UserRoleStrategy;
889
+ /**
890
+ * 日志服务
891
+ */
892
+ logger?: LoggerService;
893
+ /**
894
+ * 跳过认证的路径列表(精确匹配或前缀匹配,如 "/api/public/*")
895
+ */
896
+ publicPaths?: string[];
897
+ /**
898
+ * 扩展 schema,用于从 payload 中解析自定义业务字段(仅在未使用 apiKeyValidator 时生效)
899
+ */
900
+ extendSchema?: T;
901
+ /**
902
+ * API Key 验证器(提供后使用组合认证插件替代 JWT 验证插件)
903
+ */
529
904
  apiKeyValidator?: (key: string) => Promise<ApiKeyPrincipal | null>;
530
905
  };
906
+ /**
907
+ * 创建标准预设插件
908
+ *
909
+ * 该预设包含认证插件、RBAC 插件、可选的限流插件和错误处理插件。
910
+ *
911
+ * @typeParam T - 扩展 schema 类型,用于从 payload 中解析自定义业务字段
912
+ * @param options - 预设配置选项
913
+ * @returns Elysia 插件实例
914
+ */
531
915
  declare function createStandardPreset<T extends z.ZodTypeAny = z.ZodUndefined>(options: StandardPresetOptions<T>): Elysia<"", {
532
916
  decorator: {};
533
917
  store: {};
534
918
  derive: {
535
919
  readonly requirePermission: (resource: string, action: string) => Promise<void>;
536
- } & {
537
- tenantId: string;
538
920
  };
539
921
  resolve: {};
540
922
  }, {
@@ -556,15 +938,6 @@ declare function createStandardPreset<T extends z.ZodTypeAny = z.ZodUndefined>(o
556
938
  response: import("elysia").ExtractErrorFromHandle<{
557
939
  readonly requirePermission: (resource: string, action: string) => Promise<void>;
558
940
  }>;
559
- } & {
560
- schema: {};
561
- standaloneSchema: {};
562
- macro: {};
563
- macroFn: {};
564
- parser: {};
565
- response: import("elysia").ExtractErrorFromHandle<{
566
- tenantId: string;
567
- }> & {};
568
941
  } & {
569
942
  schema: {};
570
943
  standaloneSchema: {};
@@ -591,13 +964,13 @@ declare function createStandardPreset<T extends z.ZodTypeAny = z.ZodUndefined>(o
591
964
  schema: {};
592
965
  standaloneSchema: {};
593
966
  response: {};
594
- }, ({
967
+ }, (({
595
968
  derive: {};
596
969
  resolve: {};
597
970
  schema: {};
598
971
  standaloneSchema: {};
599
972
  response: {};
600
- } & {
973
+ } & ({
601
974
  derive: {
602
975
  auth: import("@longzai-intelligence-auth/core").UnifiedAuthContext;
603
976
  };
@@ -607,19 +980,7 @@ declare function createStandardPreset<T extends z.ZodTypeAny = z.ZodUndefined>(o
607
980
  response: import("elysia").ExtractErrorFromHandle<{
608
981
  auth: import("@longzai-intelligence-auth/core").UnifiedAuthContext;
609
982
  }>;
610
- } & {
611
- derive: {};
612
- resolve: {};
613
- schema: {};
614
- standaloneSchema: {};
615
- response: {};
616
- }) | ({
617
- derive: {};
618
- resolve: {};
619
- schema: {};
620
- standaloneSchema: {};
621
- response: {};
622
- } & {
983
+ } | {
623
984
  derive: {
624
985
  readonly auth: any;
625
986
  };
@@ -629,20 +990,36 @@ declare function createStandardPreset<T extends z.ZodTypeAny = z.ZodUndefined>(o
629
990
  response: import("elysia").ExtractErrorFromHandle<{
630
991
  readonly auth: any;
631
992
  }>;
632
- } & {
993
+ })) & {
994
+ derive: {};
995
+ resolve: {};
996
+ schema: {};
997
+ standaloneSchema: {};
998
+ response: {};
999
+ }) & {
633
1000
  derive: {};
634
1001
  resolve: {};
635
1002
  schema: {};
636
1003
  standaloneSchema: {};
637
1004
  response: {};
638
- })>;
1005
+ }>;
639
1006
  //#endregion
640
1007
  //#region src/utils/extract-client-ip.d.ts
641
- /** 从请求中提取客户端 IP 地址 */
1008
+ /**
1009
+ * 从请求中提取客户端 IP 地址
1010
+ *
1011
+ * @param request - HTTP 请求对象
1012
+ * @returns 客户端 IP 地址,如果无法提取则返回 undefined
1013
+ */
642
1014
  declare function extractClientIp(request: Request): string | undefined;
643
1015
  //#endregion
644
1016
  //#region src/utils/extract-user-agent.d.ts
645
- /** 从请求中提取用户代理 */
1017
+ /**
1018
+ * 从请求中提取用户代理
1019
+ *
1020
+ * @param request - HTTP 请求对象
1021
+ * @returns 用户代理字符串,如果无法提取则返回 undefined
1022
+ */
646
1023
  declare function extractUserAgent(request: Request): string | undefined;
647
1024
  //#endregion
648
- export { type ApiKeyPluginOptions, type ApiKeyPrincipal, type AuthContextWithExtra, type BasicPresetOptions, type CompositeAuthContext, type CompositeAuthPluginOptions, type ErrorHandlerPluginOptions, type ExtractExtra, type InMemoryApiKeyEntry, type InMemoryApiKeyMap, type JwtVerifyPluginOptions, type LoggerPluginOptions, type OptionalAuthContext, type OptionalJwtPluginOptions, type RateLimitPluginOptions, type RbacPluginOptions, type StandardPresetOptions, type TenantPluginOptions, type UnifiedAuthContext, type UserRoleStrategy, type VerifiedAuthContext, createApiKeyPlugin, createBasicPreset, createCompositeAuthPlugin, createErrorHandlerPlugin, createInMemoryApiKeyValidator, createJwtVerifyPlugin, createLoggerPlugin, createOptionalJwtPlugin, createRateLimitPlugin, createRbacPlugin, createRequiredCompositeAuthPlugin, createStandardPreset, createTenantPlugin, extractClientIp, extractUserAgent, mapDomainErrorToStatus };
1025
+ export { type ApiKeyPluginOptions, type ApiKeyPrincipal, type AuthContextWithExtra, type BasicPresetOptions, type BearerExtractorOptions, type CompositeAuthContext, type CompositeAuthPluginOptions, type CookieExtractorOptions, type CookieJwtVerifierOptions, type CredentialExtractor, type ErrorHandlerPluginOptions, type ExtractExtra, type InMemoryApiKeyEntry, type InMemoryApiKeyMap, type JwtVerifyPluginOptions, type LoggerPluginOptions, type OptionalAuthContext, type OptionalJwtPluginOptions, type RateLimitPluginOptions, type RbacPluginOptions, type RequestTokenVerifier, type StandardPresetOptions, type StaticTokenVerifierOptions, type UnifiedAuthContext, type UserRoleStrategy, type VerifiedAuthContext, createApiKeyPlugin, createBasicPreset, createBearerExtractor, createCompositeAuthPlugin, createCookieExtractor, createCookieJwtVerifier, createErrorHandlerPlugin, createInMemoryApiKeyValidator, createJwtVerifyPlugin, createLoggerPlugin, createOptionalJwtPlugin, createRateLimitPlugin, createRbacPlugin, createRequiredCompositeAuthPlugin, createStandardPreset, createStaticTokenVerifier, extractClientIp, extractUserAgent, mapDomainErrorToStatus };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{AuthenticationError as e,DomainError as t,DuplicateEntityError as n,isBusinessRuleError as r,isConcurrencyError as i,isEntityNotFoundError as a,isPermissionDeniedError as o,isValidationError as s}from"@longzai-intelligence/error";import{AccountDisabledError as c,InvalidCredentialsError as l,MfaRequiredError as u,RateLimitExceededError as d,TokenExpiredError as f,TokenInvalidError as p,TokenMissingError as m,UserAlreadyExistsError as h,accessTokenPayloadSchema as g,createDefaultJwtConfig as _}from"@longzai-intelligence-auth/core";import{Elysia as v}from"elysia";import{JwtTokenVerifier as y}from"@longzai-intelligence-auth/jwt";function b(t){return s(t)?400:t instanceof f||t instanceof p||t instanceof m||t instanceof u||t instanceof l||t instanceof c||t instanceof e?401:o(t)?403:a(t)?404:t instanceof n||t instanceof h?409:t instanceof d?429:i(t)?409:r(t)||t.code===`PASSWORD_POLICY_VIOLATION`?422:500}function x(e,t){return{userId:e.sub,tenantId:e.tenantId??t,roles:e.roles??[],permissions:e.permissions??[],authMethod:`jwt`}}function S(e){return{userId:``,tenantId:e,roles:[],permissions:[],authMethod:`jwt`}}function C(e,t){return t.some(t=>t.endsWith(`/*`)?e.startsWith(t.slice(0,-2)):e===t)}function w(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function T(e,t){return e||new y(w(t??_()))}function E(e){let{verifier:t,jwt:n,defaultTenantId:r=`default`,publicPaths:i=[],extendSchema:a}=e,o=T(t,n);return new v({name:`@longzai-intelligence-auth/jwt-verify`}).derive(async({request:e})=>{if(C(new URL(e.url).pathname,i)){let e=a?a.parse({}):{};return{auth:{...S(r),...e}}}let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(!n)throw new m;let s=await o.verifyAccessToken(n);if(!s.success||!s.payload)throw new p(s.error);g.parse(s.payload);let c=x(s.payload,r),l=a?a.parse(s.payload):{};return{auth:{...c,...l}}}).as(`scoped`)}function D(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function O(e,t){return e||new y(D(t??_()))}function k(e){let{verifier:t,jwt:n,defaultTenantId:r=`default`,extendSchema:i}=e,a=O(t,n);return new v({name:`@longzai-intelligence-auth/optional-jwt`}).derive(async({request:e})=>{let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(!n)return{auth:null};try{let e=await a.verifyAccessToken(n);if(!e.success||!e.payload)return{auth:null};g.parse(e.payload);let t=e.payload,o={userId:t.sub,tenantId:t.tenantId??r,roles:t.roles??[],permissions:t.permissions??[],authMethod:`jwt`},s=i?i.parse(t):{};return{auth:{...o,...s}}}catch{return{auth:null}}}).as(`scoped`)}function A(e,t,n){return e.some(e=>e.resource===`*`&&e.action===`*`||e.resource===t&&e.action===`*`||e.resource===`*`&&e.action===n||e.resource===t&&e.action===n)}function j(e,t,n){let r=`${t}:${n}`;return e.includes(r)||e.includes(`${t}:*`)||e.includes(`*:${n}`)||e.includes(`*:*`)}function M(e={}){let{userRole:t}=e;return new v({name:`@longzai-intelligence-auth/rbac`}).derive({as:`scoped`},async e=>{let n=e.auth;if(!n)throw new m;let r=n.userId,i=n.tenantId;return{requirePermission:async(e,a)=>{if(t){if(A(await t.findPermissionsByUserId(r,i),e,a))return;let{PermissionDeniedError:n}=await import(`@longzai-intelligence/error`);throw new n(e,a)}if(j(n.permissions??[],e,a))return;let{PermissionDeniedError:o}=await import(`@longzai-intelligence/error`);throw new o(e,a)}}}).as(`global`)}function N(e){let{header:t=`x-api-key`,validator:n}=e;return new v({name:`@longzai-intelligence-auth/api-key`}).derive(async({request:e})=>{let r=e.headers.get(t)??e.headers.get(t.toLowerCase());if(!r)throw new m(`缺少 API Key`);let i=await n(r);if(!i)throw new p(`无效的 API Key`);return{auth:{userId:i.principalId,tenantId:i.tenantId??`default`,roles:i.roles,permissions:i.permissions,authMethod:`api-key`}}}).as(`scoped`)}function P(e){return async t=>{let n=e[t];return n?{principalId:n.principalId,tenantId:n.tenantId,roles:n.roles,permissions:n.permissions}:null}}function F(e){return{authMethod:`jwt`,userId:e.userId,tenantId:e.tenantId,roles:e.roles,permissions:e.permissions}}function I(e,t){return{authMethod:`api-key`,userId:e.principalId,tenantId:e.tenantId??t,roles:e.roles,permissions:e.permissions}}function L(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function R(e,t){return e||new y(L(t??_()))}function z(e,t){return t.some(t=>t.endsWith(`/*`)?e.startsWith(t.slice(0,-2)):e===t)}function B(e){let{verifier:t,jwt:n,defaultTenantId:r=`default`,apiKeyValidator:i,apiKeyHeader:a=`x-api-key`,publicPaths:o=[]}=e,s=R(t,n);return new v({name:`@longzai-intelligence-auth/composite-auth`}).derive(async({request:e})=>{if(z(new URL(e.url).pathname,o))return{auth:null};let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(n)try{let e=await s.verifyAccessToken(n);if(e.success&&e.payload)return g.parse(e.payload),{auth:F({userId:e.payload.sub,tenantId:e.payload.tenantId??r,roles:e.payload.roles??[],permissions:e.payload.permissions??[]})}}catch{}if(i){let t=e.headers.get(a)??e.headers.get(a.toLowerCase());if(t){let e=await i(t);if(e)return{auth:I(e,r)}}}return{auth:null}}).as(`scoped`)}function V(e){let{verifier:t,jwt:n,defaultTenantId:r=`default`,apiKeyValidator:i,apiKeyHeader:a=`x-api-key`,publicPaths:o=[]}=e,s=R(t,n);return new v({name:`@longzai-intelligence-auth/required-composite-auth`}).derive(async({request:e})=>{if(z(new URL(e.url).pathname,o))return{auth:{authMethod:`jwt`,userId:``,tenantId:r,roles:[],permissions:[]}};let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(n)try{let e=await s.verifyAccessToken(n);if(e.success&&e.payload)return g.parse(e.payload),{auth:F({userId:e.payload.sub,tenantId:e.payload.tenantId??r,roles:e.payload.roles??[],permissions:e.payload.permissions??[]})}}catch{}if(i){let t=e.headers.get(a)??e.headers.get(a.toLowerCase());if(t){let e=await i(t);if(e)return{auth:I(e,r)}}}throw Error(`认证失败:需要提供有效的 JWT Token 或 API Key`)}).as(`scoped`)}function H(e={}){let{defaultTenantId:t=`default`,validateFn:n}=e;return new v({name:`@longzai-intelligence-auth/tenant`}).derive(({request:e})=>({tenantId:e.headers.get(`x-tenant-id`)??t})).as(`scoped`).onBeforeHandle(async({tenantId:e})=>{if(n&&e!==t&&!(await n(e)).valid){let{PermissionDeniedError:e}=await import(`@longzai-intelligence/error`);throw new e(`tenant`,`access`)}}).as(`global`)}function U(e){return e.headers.get(`x-forwarded-for`)?.split(`,`)[0]?.trim()??e.headers.get(`x-real-ip`)??`unknown`}function W(e){let{limiter:t,logger:n}=e,r=e.keyGenerator??U;return new v({name:`@longzai-intelligence-auth/rate-limit`}).derive(({request:e})=>({clientIp:r(e)})).derive(async({clientIp:e,request:r})=>{let i=new URL(r.url).pathname,a=`${e}:${i}`,o=await t.checkLimit(a);if(!o.allowed)throw n?.warn(`限流触发`,{clientIp:e,path:i}),new d;return{rateLimitRemaining:o.remaining}}).as(`global`)}function G(e={}){let{logger:n}=e;return new v({name:`@longzai-intelligence-auth/error-handler`}).onError(({code:e,error:r,set:i})=>{if(r instanceof t)return i.status=b(r),{code:r.code,message:r.message,...r.context&&Object.keys(r.context).length>0?{details:r.context}:{}};switch(e){case`VALIDATION`:return i.status=400,{code:`VALIDATION_ERROR`,message:`请求参数验证失败`,details:r.all.map(e=>({path:e.path,message:e.summary}))};case`NOT_FOUND`:return i.status=404,{code:`NOT_FOUND`,message:`未找到请求的资源`};case`PARSE`:return i.status=400,{code:`PARSE_ERROR`,message:`请求数据解析失败`};default:return n?.error(`未预期的错误`,{error:r instanceof Error?r.message:String(r),stack:r instanceof Error?r.stack:void 0}),i.status=500,{code:`INTERNAL_ERROR`,message:`服务器内部错误`}}}).as(`global`)}function K(e){let{logger:t}=e;return new v({name:`@longzai-intelligence-auth/logger`}).onRequest(({request:e})=>{t.info(`${e.method} ${e.url}`)}).onAfterResponse(({request:e,set:n})=>{t.info(`${e.method} ${e.url} ${n.status}`)}).onError(({request:e,error:n})=>{t.error(`${e.method} ${e.url} 请求错误`,{error:n instanceof Error?n.message:String(n)})}).as(`global`)}function q(e){return new v({name:`@longzai-intelligence-auth/basic-preset`}).use(E({verifier:e.tokenVerifier,defaultTenantId:e.defaultTenantId,publicPaths:e.publicPaths,extendSchema:e.extendSchema})).use(G({logger:e.logger}))}function J(e){let t=e.apiKeyValidator?V({verifier:e.tokenVerifier,defaultTenantId:e.defaultTenantId,publicPaths:e.publicPaths,apiKeyValidator:e.apiKeyValidator}):E({verifier:e.tokenVerifier,defaultTenantId:e.defaultTenantId,publicPaths:e.publicPaths,extendSchema:e.extendSchema}),n=new v({name:`@longzai-intelligence-auth/standard-preset`}).use(t).use(M({userRole:e.userRole})).use(H({defaultTenantId:e.defaultTenantId,validateFn:e.validateFn}));return e.rateLimiter&&n.use(W({limiter:e.rateLimiter,logger:e.logger})),n.use(G({logger:e.logger}))}function Y(e){return e.headers.get(`x-forwarded-for`)?.split(`,`)[0]?.trim()??e.headers.get(`x-real-ip`)??void 0}function X(e){return e.headers.get(`user-agent`)??void 0}export{N as createApiKeyPlugin,q as createBasicPreset,B as createCompositeAuthPlugin,G as createErrorHandlerPlugin,P as createInMemoryApiKeyValidator,E as createJwtVerifyPlugin,K as createLoggerPlugin,k as createOptionalJwtPlugin,W as createRateLimitPlugin,M as createRbacPlugin,V as createRequiredCompositeAuthPlugin,J as createStandardPreset,H as createTenantPlugin,Y as extractClientIp,X as extractUserAgent,b as mapDomainErrorToStatus};
1
+ import{AuthenticationError as e,DomainError as t,DuplicateEntityError as n,isBusinessRuleError as r,isConcurrencyError as i,isEntityNotFoundError as a,isPermissionDeniedError as o,isValidationError as s}from"@longzai-intelligence/error";import{AccountDisabledError as c,InvalidCredentialsError as l,MfaRequiredError as u,RateLimitExceededError as d,TokenExpiredError as f,TokenInvalidError as p,TokenMissingError as m,UserAlreadyExistsError as h,accessTokenPayloadSchema as g,createDefaultJwtConfig as _}from"@longzai-intelligence-auth/core";import v from"node:crypto";import{JwtTokenVerifier as y}from"@longzai-intelligence-auth/jwt";import{Elysia as b}from"elysia";function x(t){return s(t)?400:t instanceof f||t instanceof p||t instanceof m||t instanceof u||t instanceof l||t instanceof c||t instanceof e?401:o(t)?403:a(t)?404:t instanceof n||t instanceof h?409:t instanceof d?429:i(t)?409:r(t)||t.code===`PASSWORD_POLICY_VIOLATION`?422:500}function S(e,t){let n=Buffer.from(e,`utf-8`),r=Buffer.from(t,`utf-8`);if(n.length!==r.length){let e=Buffer.alloc(n.length);return v.timingSafeEqual(n,e),!1}return v.timingSafeEqual(n,r)}function C(e){let{token:t,getToken:n,subject:r=`static`,tenantId:i}=e;if(!t&&!n)throw Error(`createStaticTokenVerifier 需要至少提供 token 或 getToken 之一`);let a=()=>{if(n){let e=n();if(e)return e}return t??null};return{async verifyAccessToken(e){let t=a();return t?S(e,t)?{success:!0,payload:{sub:r,type:`access`,...i!==void 0&&{tenantId:i}}}:{success:!1,error:`静态令牌不匹配`}:{success:!1,error:`静态令牌未配置`}},async verifyRefreshToken(){return{success:!1,error:`静态令牌模式不支持 refresh token`}}}}function w(e){let{cookieName:t}=e;return{extract(e){let n=e.headers.get(`cookie`);if(!n)return null;let r=n.split(`;`);for(let e of r){let n=e.indexOf(`=`);if(n!==-1&&e.slice(0,n).trim()===t)return e.slice(n+1).trim()||null}return null}}}function T(e={}){let{headerName:t=`authorization`}=e;return{extract(e){let n=e.headers.get(t);if(!n)return null;let r=n.split(` `);return r.length!==2||r[0]!==`Bearer`||!r[1]?null:r[1]}}}function E(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function D(e,t){if(e)return e;if(!t)throw Error(`createCookieJwtVerifier 需要至少提供 jwt 或 verifier 之一`);return new y(E(t))}function O(e){let{cookieName:t,jwt:n,verifier:r,extractor:i}=e,a=D(r,n),o=i??w({cookieName:t});return{async verify(e){let t=o.extract(e);return t?a.verifyAccessToken(t):{success:!1,error:`请求未携带 cookie 凭证`}}}}function k(e,t){return{userId:e.sub,tenantId:e.tenantId??t,roles:e.roles??[],permissions:e.permissions??[],authMethod:`jwt`}}function A(e){return{userId:``,tenantId:e,roles:[],permissions:[],authMethod:`jwt`}}function j(e,t){return t.some(t=>t.endsWith(`/*`)?e.startsWith(t.slice(0,-2)):e===t)}function M(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function N(e,t){return e||new y(M(t??_()))}function P(e){let{verifier:t,jwt:n,defaultTenantId:r=`default`,publicPaths:i=[],extendSchema:a}=e,o=N(t,n);return new b({name:`@longzai-intelligence-auth/jwt-verify`}).derive(async({request:e})=>{if(j(new URL(e.url).pathname,i)){let e=a?a.parse({}):{};return{auth:{...A(r),...e}}}let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(!n)throw new m;let s=await o.verifyAccessToken(n);if(!s.success||!s.payload)throw new p(s.error);g.parse(s.payload);let c=k(s.payload,r),l=a?a.parse(s.payload):{};return{auth:{...c,...l}}}).as(`scoped`)}function F(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function I(e,t){return e||new y(F(t??_()))}function L(e){let{verifier:t,jwt:n,defaultTenantId:r=`default`,extendSchema:i}=e,a=I(t,n);return new b({name:`@longzai-intelligence-auth/optional-jwt`}).derive(async({request:e})=>{let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(!n)return{auth:null};try{let e=await a.verifyAccessToken(n);if(!e.success||!e.payload)return{auth:null};g.parse(e.payload);let t=e.payload,o={userId:t.sub,tenantId:t.tenantId??r,roles:t.roles??[],permissions:t.permissions??[],authMethod:`jwt`},s=i?i.parse(t):{};return{auth:{...o,...s}}}catch{return{auth:null}}}).as(`scoped`)}function R(e,t,n){return e.some(e=>e.resource===`*`&&e.action===`*`||e.resource===t&&e.action===`*`||e.resource===`*`&&e.action===n||e.resource===t&&e.action===n)}function z(e,t,n){let r=`${t}:${n}`;return e.includes(r)||e.includes(`${t}:*`)||e.includes(`*:${n}`)||e.includes(`*:*`)}function B(e={}){let{userRole:t}=e;return new b({name:`@longzai-intelligence-auth/rbac`}).derive({as:`scoped`},async e=>{let n=e.auth;if(!n)throw new m;let r=n.userId,i=n.tenantId;return{requirePermission:async(e,a)=>{if(t){if(R(await t.findPermissionsByUserId(r,i),e,a))return;let{PermissionDeniedError:n}=await import(`@longzai-intelligence/error`);throw new n(e,a)}if(z(n.permissions??[],e,a))return;let{PermissionDeniedError:o}=await import(`@longzai-intelligence/error`);throw new o(e,a)}}}).as(`global`)}function V(e){let{header:t=`x-api-key`,validator:n}=e;return new b({name:`@longzai-intelligence-auth/api-key`}).derive(async({request:e})=>{let r=e.headers.get(t)??e.headers.get(t.toLowerCase());if(!r)throw new m(`缺少 API Key`);let i=await n(r);if(!i)throw new p(`无效的 API Key`);return{auth:{userId:i.principalId,tenantId:i.tenantId??`default`,roles:i.roles,permissions:i.permissions,authMethod:`api-key`}}}).as(`scoped`)}function H(e){return async t=>{let n=e[t];return n?{principalId:n.principalId,tenantId:n.tenantId,roles:n.roles,permissions:n.permissions}:null}}function U(e){return{authMethod:`jwt`,userId:e.userId,tenantId:e.tenantId,roles:e.roles,permissions:e.permissions}}function W(e,t){return{authMethod:`api-key`,userId:e.principalId,tenantId:e.tenantId??t,roles:e.roles,permissions:e.permissions}}function G(e){return{algorithm:`HS256`,secret:e.secret,accessExpiresIn:e.accessExpiresIn,refreshExpiresIn:e.refreshExpiresIn}}function K(e,t,n){let r=[];return e&&r.push(e),t&&t.length>0&&r.push(...t),r.length>0?r:[new y(G(n??_()))]}function q(e,t){return t.some(t=>t.endsWith(`/*`)?e.startsWith(t.slice(0,-2)):e===t)}async function J(e,t){for(let n of e)try{let e=await n.verifyAccessToken(t);if(e.success&&e.payload)return{success:!0,payload:e.payload}}catch{}return null}function Y(e){let{verifier:t,verifiers:n,jwt:r,defaultTenantId:i=`default`,apiKeyValidator:a,apiKeyHeader:o=`x-api-key`,publicPaths:s=[]}=e,c=K(t,n,r);return new b({name:`@longzai-intelligence-auth/composite-auth`}).derive(async({request:e})=>{if(q(new URL(e.url).pathname,s))return{auth:null};let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(n){let e=await J(c,n);if(e)return g.parse(e.payload),{auth:U({userId:e.payload.sub,tenantId:e.payload.tenantId??i,roles:e.payload.roles??[],permissions:e.payload.permissions??[]})}}if(a){let t=e.headers.get(o)??e.headers.get(o.toLowerCase());if(t){let e=await a(t);if(e)return{auth:W(e,i)}}}return{auth:null}}).as(`scoped`)}function X(e){let{verifier:t,verifiers:n,jwt:r,defaultTenantId:i=`default`,apiKeyValidator:a,apiKeyHeader:o=`x-api-key`,publicPaths:s=[]}=e,c=K(t,n,r);return new b({name:`@longzai-intelligence-auth/required-composite-auth`}).derive(async({request:e})=>{if(q(new URL(e.url).pathname,s))return{auth:{authMethod:`jwt`,userId:``,tenantId:i,roles:[],permissions:[]}};let t=e.headers.get(`Authorization`),n=t?.startsWith(`Bearer `)?t.slice(7):null;if(n){let e=await J(c,n);if(e)return g.parse(e.payload),{auth:U({userId:e.payload.sub,tenantId:e.payload.tenantId??i,roles:e.payload.roles??[],permissions:e.payload.permissions??[]})}}if(a){let t=e.headers.get(o)??e.headers.get(o.toLowerCase());if(t){let e=await a(t);if(e)return{auth:W(e,i)}}}throw Error(`认证失败:需要提供有效的 JWT Token 或 API Key`)}).as(`scoped`)}function Z(e){return e.headers.get(`x-forwarded-for`)?.split(`,`)[0]?.trim()??e.headers.get(`x-real-ip`)??`unknown`}function Q(e){let{limiter:t,logger:n}=e,r=e.keyGenerator??Z;return new b({name:`@longzai-intelligence-auth/rate-limit`}).derive(({request:e})=>({clientIp:r(e)})).derive(async({clientIp:e,request:r})=>{let i=new URL(r.url).pathname,a=`${e}:${i}`,o=await t.checkLimit(a);if(!o.allowed)throw n?.warn(`限流触发`,{clientIp:e,path:i}),new d;return{rateLimitRemaining:o.remaining}}).as(`global`)}function $(e={}){let{logger:n}=e;return new b({name:`@longzai-intelligence-auth/error-handler`}).onError(({code:e,error:r,set:i})=>{if(r instanceof t)return i.status=x(r),{code:r.code,message:r.message,...r.context&&Object.keys(r.context).length>0?{details:r.context}:{}};switch(e){case`VALIDATION`:return i.status=400,{code:`VALIDATION_ERROR`,message:`请求参数验证失败`,details:r.all.map(e=>({path:e.path,message:e.summary}))};case`NOT_FOUND`:return i.status=404,{code:`NOT_FOUND`,message:`未找到请求的资源`};case`PARSE`:return i.status=400,{code:`PARSE_ERROR`,message:`请求数据解析失败`};default:return n?.error(`未预期的错误`,{error:r instanceof Error?r.message:String(r),stack:r instanceof Error?r.stack:void 0}),i.status=500,{code:`INTERNAL_ERROR`,message:`服务器内部错误`}}}).as(`global`)}function ee(e){let{logger:t}=e;return new b({name:`@longzai-intelligence-auth/logger`}).onRequest(({request:e})=>{t.info(`${e.method} ${e.url}`)}).onAfterResponse(({request:e,set:n})=>{t.info(`${e.method} ${e.url} ${n.status}`)}).onError(({request:e,error:n})=>{t.error(`${e.method} ${e.url} 请求错误`,{error:n instanceof Error?n.message:String(n)})}).as(`global`)}function te(e){return new b({name:`@longzai-intelligence-auth/basic-preset`}).use(P({verifier:e.tokenVerifier,defaultTenantId:e.defaultTenantId,publicPaths:e.publicPaths,extendSchema:e.extendSchema})).use($({logger:e.logger}))}function ne(e){let t=e.apiKeyValidator?X({verifier:e.tokenVerifier,defaultTenantId:e.defaultTenantId,publicPaths:e.publicPaths,apiKeyValidator:e.apiKeyValidator}):P({verifier:e.tokenVerifier,defaultTenantId:e.defaultTenantId,publicPaths:e.publicPaths,extendSchema:e.extendSchema}),n=new b({name:`@longzai-intelligence-auth/standard-preset`}).use(t).use(B({userRole:e.userRole}));return e.rateLimiter&&n.use(Q({limiter:e.rateLimiter,logger:e.logger})),n.use($({logger:e.logger}))}function re(e){return e.headers.get(`x-forwarded-for`)?.split(`,`)[0]?.trim()??e.headers.get(`x-real-ip`)??void 0}function ie(e){return e.headers.get(`user-agent`)??void 0}export{V as createApiKeyPlugin,te as createBasicPreset,T as createBearerExtractor,Y as createCompositeAuthPlugin,w as createCookieExtractor,O as createCookieJwtVerifier,$ as createErrorHandlerPlugin,H as createInMemoryApiKeyValidator,P as createJwtVerifyPlugin,ee as createLoggerPlugin,L as createOptionalJwtPlugin,Q as createRateLimitPlugin,B as createRbacPlugin,X as createRequiredCompositeAuthPlugin,ne as createStandardPreset,C as createStaticTokenVerifier,re as extractClientIp,ie as extractUserAgent,x as mapDomainErrorToStatus};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longzai-intelligence-auth/elysia",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -33,11 +33,11 @@
33
33
  "test:coverage": "bun test --coverage",
34
34
  "test:unit": "bun test src/__tests__/unit/",
35
35
  "test:integration": "bun test src/__tests__/integration/",
36
- "clean": "rm -rf dist out .cache"
36
+ "clean": "rimraf dist out .cache"
37
37
  },
38
38
  "dependencies": {
39
- "@longzai-intelligence-auth/core": "0.0.5",
40
- "@longzai-intelligence-auth/jwt": "0.0.5",
39
+ "@longzai-intelligence-auth/core": "0.0.7",
40
+ "@longzai-intelligence-auth/jwt": "0.0.7",
41
41
  "@longzai-intelligence/error": "^0.0.5",
42
42
  "@elysiajs/bearer": "^1.3",
43
43
  "zod": "^3.24"