@55387.ai/uniauth-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -0
- package/dist/index.cjs +378 -0
- package/dist/index.d.cts +260 -0
- package/dist/index.d.ts +260 -0
- package/dist/index.js +341 -0
- package/package.json +43 -0
- package/src/index.ts +581 -0
- package/src/server.test.ts +231 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# @55387.ai/uniauth-server
|
|
2
|
+
|
|
3
|
+
UniAuth 后端 SDK,用于在 Node.js 后端服务中验证用户令牌。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @55387.ai/uniauth-server
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @55387.ai/uniauth-server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 快速开始
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { UniAuthServer } from '@55387.ai/uniauth-server';
|
|
17
|
+
|
|
18
|
+
const auth = new UniAuthServer({
|
|
19
|
+
baseUrl: 'https://sso.55387.xyz',
|
|
20
|
+
clientId: 'your-client-id',
|
|
21
|
+
clientSecret: 'your-client-secret',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// 验证令牌
|
|
25
|
+
const payload = await auth.verifyToken(accessToken);
|
|
26
|
+
console.log('User ID:', payload.sub);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Express 中间件
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import express from 'express';
|
|
33
|
+
import { UniAuthServer } from '@uniauth/server-sdk';
|
|
34
|
+
|
|
35
|
+
const app = express();
|
|
36
|
+
const auth = new UniAuthServer({ ... });
|
|
37
|
+
|
|
38
|
+
// 保护 API 路由
|
|
39
|
+
app.use('/api/*', auth.middleware());
|
|
40
|
+
|
|
41
|
+
// 在路由中使用用户信息
|
|
42
|
+
app.get('/api/profile', (req, res) => {
|
|
43
|
+
res.json({ user: req.user, payload: req.authPayload });
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Hono 中间件
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { Hono } from 'hono';
|
|
51
|
+
import { UniAuthServer } from '@uniauth/server-sdk';
|
|
52
|
+
|
|
53
|
+
const app = new Hono();
|
|
54
|
+
const auth = new UniAuthServer({ ... });
|
|
55
|
+
|
|
56
|
+
// 保护 API 路由
|
|
57
|
+
app.use('/api/*', auth.honoMiddleware());
|
|
58
|
+
|
|
59
|
+
// 在路由中使用用户信息
|
|
60
|
+
app.get('/api/profile', (c) => {
|
|
61
|
+
const user = c.get('user');
|
|
62
|
+
return c.json({ user });
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## OAuth2 Token Introspection (RFC 7662)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// 内省令牌(资源服务器标准验证方式)
|
|
70
|
+
const result = await auth.introspectToken(accessToken);
|
|
71
|
+
|
|
72
|
+
if (result.active) {
|
|
73
|
+
console.log('Token 有效');
|
|
74
|
+
console.log('用户:', result.sub);
|
|
75
|
+
console.log('权限:', result.scope);
|
|
76
|
+
} else {
|
|
77
|
+
console.log('Token 无效或已过期');
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API 参考
|
|
82
|
+
|
|
83
|
+
### 初始化选项
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
interface UniAuthServerConfig {
|
|
87
|
+
baseUrl: string; // UniAuth 服务地址
|
|
88
|
+
clientId: string; // OAuth2 客户端 ID
|
|
89
|
+
clientSecret: string; // OAuth2 客户端密钥
|
|
90
|
+
jwtPublicKey?: string; // JWT 公钥(用于本地验证)
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 方法
|
|
95
|
+
|
|
96
|
+
| 方法 | 说明 |
|
|
97
|
+
|------|------|
|
|
98
|
+
| `verifyToken(token)` | 验证访问令牌 |
|
|
99
|
+
| `introspectToken(token)` | RFC 7662 令牌内省 |
|
|
100
|
+
| `isTokenActive(token)` | 检查令牌是否有效 |
|
|
101
|
+
| `getUser(userId)` | 获取用户信息 |
|
|
102
|
+
| `middleware()` | Express/Connect 中间件 |
|
|
103
|
+
| `honoMiddleware()` | Hono 中间件 |
|
|
104
|
+
| `clearCache()` | 清除令牌缓存 |
|
|
105
|
+
|
|
106
|
+
### 类型
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
interface TokenPayload {
|
|
110
|
+
sub: string; // 用户 ID
|
|
111
|
+
iss?: string; // 签发者
|
|
112
|
+
aud?: string | string[]; // 受众
|
|
113
|
+
exp: number; // 过期时间
|
|
114
|
+
iat: number; // 签发时间
|
|
115
|
+
scope?: string; // 权限范围
|
|
116
|
+
phone?: string; // 手机号
|
|
117
|
+
email?: string; // 邮箱
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface UserInfo {
|
|
121
|
+
id: string;
|
|
122
|
+
phone?: string;
|
|
123
|
+
email?: string;
|
|
124
|
+
nickname?: string;
|
|
125
|
+
avatar_url?: string;
|
|
126
|
+
phone_verified?: boolean;
|
|
127
|
+
email_verified?: boolean;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 错误处理
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { ServerAuthError, ServerErrorCode } from '@55387.ai/uniauth-server';
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
await auth.verifyToken(token);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof ServerAuthError) {
|
|
140
|
+
switch (error.code) {
|
|
141
|
+
case ServerErrorCode.INVALID_TOKEN:
|
|
142
|
+
// 令牌无效
|
|
143
|
+
break;
|
|
144
|
+
case ServerErrorCode.TOKEN_EXPIRED:
|
|
145
|
+
// 令牌已过期
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ServerAuthError: () => ServerAuthError,
|
|
34
|
+
ServerErrorCode: () => ServerErrorCode,
|
|
35
|
+
UniAuthServer: () => UniAuthServer,
|
|
36
|
+
default: () => index_default
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
var jose = __toESM(require("jose"), 1);
|
|
40
|
+
var ServerErrorCode = {
|
|
41
|
+
INVALID_TOKEN: "INVALID_TOKEN",
|
|
42
|
+
TOKEN_EXPIRED: "TOKEN_EXPIRED",
|
|
43
|
+
VERIFICATION_FAILED: "VERIFICATION_FAILED",
|
|
44
|
+
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
45
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
46
|
+
NO_PUBLIC_KEY: "NO_PUBLIC_KEY",
|
|
47
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
48
|
+
INTERNAL_ERROR: "INTERNAL_ERROR"
|
|
49
|
+
};
|
|
50
|
+
var ServerAuthError = class _ServerAuthError extends Error {
|
|
51
|
+
code;
|
|
52
|
+
statusCode;
|
|
53
|
+
constructor(code, message, statusCode = 401) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "ServerAuthError";
|
|
56
|
+
this.code = code;
|
|
57
|
+
this.statusCode = statusCode;
|
|
58
|
+
if (Error.captureStackTrace) {
|
|
59
|
+
Error.captureStackTrace(this, _ServerAuthError);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var UniAuthServer = class {
|
|
64
|
+
config;
|
|
65
|
+
tokenCache = /* @__PURE__ */ new Map();
|
|
66
|
+
constructor(config) {
|
|
67
|
+
this.config = {
|
|
68
|
+
...config,
|
|
69
|
+
clientId: config.clientId || config.appKey || "",
|
|
70
|
+
clientSecret: config.clientSecret || config.appSecret || ""
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// ============================================
|
|
74
|
+
// Token Verification
|
|
75
|
+
// ============================================
|
|
76
|
+
/**
|
|
77
|
+
* Verify access token
|
|
78
|
+
* 验证访问令牌
|
|
79
|
+
*
|
|
80
|
+
* @param token - JWT access token
|
|
81
|
+
* @returns Token payload if valid
|
|
82
|
+
* @throws ServerAuthError if token is invalid
|
|
83
|
+
*/
|
|
84
|
+
async verifyToken(token) {
|
|
85
|
+
const cached = this.tokenCache.get(token);
|
|
86
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
87
|
+
return cached.payload;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const response = await fetch(`${this.config.baseUrl}/api/v1/auth/verify`, {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers: {
|
|
93
|
+
"Content-Type": "application/json",
|
|
94
|
+
"X-App-Key": this.config.clientId,
|
|
95
|
+
"X-App-Secret": this.config.clientSecret
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({ token })
|
|
98
|
+
});
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
const errorResponse = await response.json();
|
|
101
|
+
throw new ServerAuthError(
|
|
102
|
+
errorResponse.error?.code || ServerErrorCode.INVALID_TOKEN,
|
|
103
|
+
errorResponse.error?.message || "Invalid token",
|
|
104
|
+
401
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const data = await response.json();
|
|
108
|
+
const payload = data.data;
|
|
109
|
+
const cacheExpiry = Math.min(payload.exp * 1e3, Date.now() + 60 * 1e3);
|
|
110
|
+
this.tokenCache.set(token, { payload, expiresAt: cacheExpiry });
|
|
111
|
+
return payload;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error instanceof ServerAuthError) {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
if (this.config.jwtPublicKey) {
|
|
117
|
+
return this.verifyTokenLocally(token);
|
|
118
|
+
}
|
|
119
|
+
throw new ServerAuthError(
|
|
120
|
+
ServerErrorCode.VERIFICATION_FAILED,
|
|
121
|
+
error instanceof Error ? error.message : "Token verification failed",
|
|
122
|
+
401
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Verify token locally using JWT public key
|
|
128
|
+
* 使用 JWT 公钥本地验证令牌
|
|
129
|
+
*/
|
|
130
|
+
async verifyTokenLocally(token) {
|
|
131
|
+
if (!this.config.jwtPublicKey) {
|
|
132
|
+
throw new ServerAuthError(ServerErrorCode.NO_PUBLIC_KEY, "JWT public key not configured", 500);
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const publicKey = await jose.importSPKI(this.config.jwtPublicKey, "RS256");
|
|
136
|
+
const { payload } = await jose.jwtVerify(token, publicKey);
|
|
137
|
+
return {
|
|
138
|
+
sub: payload.sub,
|
|
139
|
+
iss: payload.iss,
|
|
140
|
+
aud: payload.aud,
|
|
141
|
+
iat: payload.iat,
|
|
142
|
+
exp: payload.exp,
|
|
143
|
+
scope: payload.scope,
|
|
144
|
+
azp: payload.azp,
|
|
145
|
+
phone: payload.phone,
|
|
146
|
+
email: payload.email
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw new ServerAuthError(
|
|
150
|
+
ServerErrorCode.INVALID_TOKEN,
|
|
151
|
+
error instanceof Error ? error.message : "Invalid token",
|
|
152
|
+
401
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ============================================
|
|
157
|
+
// OAuth2 Token Introspection (RFC 7662)
|
|
158
|
+
// ============================================
|
|
159
|
+
/**
|
|
160
|
+
* Introspect a token (RFC 7662)
|
|
161
|
+
* 内省令牌(RFC 7662 标准)
|
|
162
|
+
*
|
|
163
|
+
* This is the standard way for resource servers to validate tokens.
|
|
164
|
+
*
|
|
165
|
+
* @param token - The token to introspect
|
|
166
|
+
* @param tokenTypeHint - Optional hint about the token type ('access_token' or 'refresh_token')
|
|
167
|
+
* @returns Introspection result
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* const result = await auth.introspectToken(accessToken);
|
|
172
|
+
* if (result.active) {
|
|
173
|
+
* console.log('Token is valid, user:', result.sub);
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
async introspectToken(token, tokenTypeHint) {
|
|
178
|
+
try {
|
|
179
|
+
const credentials = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString("base64");
|
|
180
|
+
const body = { token };
|
|
181
|
+
if (tokenTypeHint) {
|
|
182
|
+
body.token_type_hint = tokenTypeHint;
|
|
183
|
+
}
|
|
184
|
+
const response = await fetch(`${this.config.baseUrl}/api/v1/oauth2/introspect`, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/json",
|
|
188
|
+
"Authorization": `Basic ${credentials}`
|
|
189
|
+
},
|
|
190
|
+
body: JSON.stringify(body)
|
|
191
|
+
});
|
|
192
|
+
const result = await response.json();
|
|
193
|
+
return result;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
return { active: false };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if a token is active
|
|
200
|
+
* 检查令牌是否有效
|
|
201
|
+
*
|
|
202
|
+
* @param token - The token to check
|
|
203
|
+
* @returns true if token is active
|
|
204
|
+
*/
|
|
205
|
+
async isTokenActive(token) {
|
|
206
|
+
const result = await this.introspectToken(token);
|
|
207
|
+
return result.active;
|
|
208
|
+
}
|
|
209
|
+
// ============================================
|
|
210
|
+
// User Management
|
|
211
|
+
// ============================================
|
|
212
|
+
/**
|
|
213
|
+
* Get user info by ID
|
|
214
|
+
* 根据 ID 获取用户信息
|
|
215
|
+
*/
|
|
216
|
+
async getUser(userId) {
|
|
217
|
+
const response = await fetch(`${this.config.baseUrl}/api/v1/admin/users/${userId}`, {
|
|
218
|
+
method: "GET",
|
|
219
|
+
headers: {
|
|
220
|
+
"Content-Type": "application/json",
|
|
221
|
+
"X-App-Key": this.config.clientId,
|
|
222
|
+
"X-App-Secret": this.config.clientSecret
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
if (!response.ok) {
|
|
226
|
+
const errorResponse = await response.json();
|
|
227
|
+
throw new ServerAuthError(
|
|
228
|
+
errorResponse.error?.code || ServerErrorCode.USER_NOT_FOUND,
|
|
229
|
+
errorResponse.error?.message || "User not found",
|
|
230
|
+
response.status
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const data = await response.json();
|
|
234
|
+
return data.data;
|
|
235
|
+
}
|
|
236
|
+
// ============================================
|
|
237
|
+
// Express/Connect Middleware
|
|
238
|
+
// ============================================
|
|
239
|
+
/**
|
|
240
|
+
* Express/Connect middleware for authentication
|
|
241
|
+
* Express/Connect 认证中间件
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* import express from 'express';
|
|
246
|
+
*
|
|
247
|
+
* const app = express();
|
|
248
|
+
* app.use('/api/*', auth.middleware());
|
|
249
|
+
*
|
|
250
|
+
* app.get('/api/profile', (req, res) => {
|
|
251
|
+
* res.json({ user: req.user });
|
|
252
|
+
* });
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
middleware() {
|
|
256
|
+
return async (req, res, next) => {
|
|
257
|
+
const authHeader = req.headers["authorization"];
|
|
258
|
+
if (!authHeader || typeof authHeader !== "string" || !authHeader.startsWith("Bearer ")) {
|
|
259
|
+
res.status(401).json({
|
|
260
|
+
success: false,
|
|
261
|
+
error: {
|
|
262
|
+
code: ServerErrorCode.UNAUTHORIZED,
|
|
263
|
+
message: "Authorization header is required / \u9700\u8981\u6388\u6743\u5934"
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const token = authHeader.substring(7);
|
|
269
|
+
try {
|
|
270
|
+
const payload = await this.verifyToken(token);
|
|
271
|
+
req.authPayload = payload;
|
|
272
|
+
try {
|
|
273
|
+
req.user = await this.getUser(payload.sub);
|
|
274
|
+
} catch {
|
|
275
|
+
}
|
|
276
|
+
next();
|
|
277
|
+
} catch (error) {
|
|
278
|
+
const authError = error instanceof ServerAuthError ? error : new ServerAuthError(
|
|
279
|
+
ServerErrorCode.UNAUTHORIZED,
|
|
280
|
+
"Authentication failed",
|
|
281
|
+
401
|
|
282
|
+
);
|
|
283
|
+
res.status(authError.statusCode).json({
|
|
284
|
+
success: false,
|
|
285
|
+
error: {
|
|
286
|
+
code: authError.code,
|
|
287
|
+
message: authError.message
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
// ============================================
|
|
294
|
+
// Hono Middleware
|
|
295
|
+
// ============================================
|
|
296
|
+
/**
|
|
297
|
+
* Hono middleware for authentication
|
|
298
|
+
* Hono 认证中间件
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```typescript
|
|
302
|
+
* import { Hono } from 'hono';
|
|
303
|
+
*
|
|
304
|
+
* const app = new Hono();
|
|
305
|
+
* app.use('/api/*', auth.honoMiddleware());
|
|
306
|
+
*
|
|
307
|
+
* app.get('/api/profile', (c) => {
|
|
308
|
+
* const user = c.get('user');
|
|
309
|
+
* return c.json({ user });
|
|
310
|
+
* });
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
honoMiddleware() {
|
|
314
|
+
return async (c, next) => {
|
|
315
|
+
const authHeader = c.req.header("authorization") || c.req.header("Authorization");
|
|
316
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
317
|
+
return c.json({
|
|
318
|
+
success: false,
|
|
319
|
+
error: {
|
|
320
|
+
code: ServerErrorCode.UNAUTHORIZED,
|
|
321
|
+
message: "Authorization header is required / \u9700\u8981\u6388\u6743\u5934"
|
|
322
|
+
}
|
|
323
|
+
}, 401);
|
|
324
|
+
}
|
|
325
|
+
const token = authHeader.substring(7);
|
|
326
|
+
try {
|
|
327
|
+
const payload = await this.verifyToken(token);
|
|
328
|
+
c.set("authPayload", payload);
|
|
329
|
+
try {
|
|
330
|
+
const user = await this.getUser(payload.sub);
|
|
331
|
+
c.set("user", user);
|
|
332
|
+
} catch {
|
|
333
|
+
}
|
|
334
|
+
await next();
|
|
335
|
+
} catch (error) {
|
|
336
|
+
const authError = error instanceof ServerAuthError ? error : new ServerAuthError(
|
|
337
|
+
ServerErrorCode.UNAUTHORIZED,
|
|
338
|
+
"Authentication failed",
|
|
339
|
+
401
|
|
340
|
+
);
|
|
341
|
+
return c.json({
|
|
342
|
+
success: false,
|
|
343
|
+
error: {
|
|
344
|
+
code: authError.code,
|
|
345
|
+
message: authError.message
|
|
346
|
+
}
|
|
347
|
+
}, authError.statusCode);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
// ============================================
|
|
352
|
+
// Utility Methods
|
|
353
|
+
// ============================================
|
|
354
|
+
/**
|
|
355
|
+
* Clear token cache
|
|
356
|
+
* 清除令牌缓存
|
|
357
|
+
*/
|
|
358
|
+
clearCache() {
|
|
359
|
+
this.tokenCache.clear();
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Get cache statistics
|
|
363
|
+
* 获取缓存统计
|
|
364
|
+
*/
|
|
365
|
+
getCacheStats() {
|
|
366
|
+
return {
|
|
367
|
+
size: this.tokenCache.size,
|
|
368
|
+
entries: this.tokenCache.size
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var index_default = UniAuthServer;
|
|
373
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
374
|
+
0 && (module.exports = {
|
|
375
|
+
ServerAuthError,
|
|
376
|
+
ServerErrorCode,
|
|
377
|
+
UniAuthServer
|
|
378
|
+
});
|