@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 +491 -114
- package/dist/index.js +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,239 @@
|
|
|
1
1
|
import { DomainError } from "@longzai-intelligence/error";
|
|
2
|
-
import { JwtConfig, LoggerService, RateLimiterPort, ResourceAction,
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
347
|
+
/**
|
|
348
|
+
* 查询用户在指定租户下的所有权限
|
|
349
|
+
*/
|
|
350
|
+
findPermissionsByUserId(userId: string, tenantId: string): Promise<ResourceAction[]>;
|
|
111
351
|
};
|
|
352
|
+
/**
|
|
353
|
+
* RBAC 插件配置选项
|
|
354
|
+
*/
|
|
112
355
|
type RbacPluginOptions = {
|
|
113
|
-
/**
|
|
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
|
-
/**
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
/**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
/**
|
|
329
|
-
|
|
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
|
-
/**
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
/**
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
/**
|
|
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
|
|
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.
|
|
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": "
|
|
36
|
+
"clean": "rimraf dist out .cache"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@longzai-intelligence-auth/core": "0.0.
|
|
40
|
-
"@longzai-intelligence-auth/jwt": "0.0.
|
|
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"
|