@esengine/server 1.1.4 → 1.2.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/dist/Room-BnKpl5Sj.d.ts +237 -0
- package/dist/auth/index.d.ts +711 -0
- package/dist/auth/index.js +707 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/testing/index.d.ts +153 -0
- package/dist/auth/testing/index.js +165 -0
- package/dist/auth/testing/index.js.map +1 -0
- package/dist/chunk-7C6JZO4O.js +744 -0
- package/dist/chunk-7C6JZO4O.js.map +1 -0
- package/dist/chunk-T626JPC7.js +8 -0
- package/dist/chunk-T626JPC7.js.map +1 -0
- package/dist/index-DgaJIm6-.d.ts +170 -0
- package/dist/index.d.ts +4 -403
- package/dist/index.js +3 -745
- package/dist/index.js.map +1 -1
- package/dist/testing/index.d.ts +386 -0
- package/dist/testing/index.js +629 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types-BmO5ykKp.d.ts +311 -0
- package/package.json +26 -3
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
import { __name, __publicField } from '../chunk-T626JPC7.js';
|
|
2
|
+
import * as jwt from 'jsonwebtoken';
|
|
3
|
+
import { randomBytes } from 'crypto';
|
|
4
|
+
|
|
5
|
+
// src/auth/context.ts
|
|
6
|
+
var defaultUserExtractor = {
|
|
7
|
+
getId(user) {
|
|
8
|
+
if (user && typeof user === "object") {
|
|
9
|
+
const u = user;
|
|
10
|
+
if (typeof u.id === "string") return u.id;
|
|
11
|
+
if (typeof u.id === "number") return String(u.id);
|
|
12
|
+
if (typeof u.userId === "string") return u.userId;
|
|
13
|
+
if (typeof u.userId === "number") return String(u.userId);
|
|
14
|
+
if (typeof u.sub === "string") return u.sub;
|
|
15
|
+
}
|
|
16
|
+
return "";
|
|
17
|
+
},
|
|
18
|
+
getRoles(user) {
|
|
19
|
+
if (user && typeof user === "object") {
|
|
20
|
+
const u = user;
|
|
21
|
+
if (Array.isArray(u.roles)) {
|
|
22
|
+
return u.roles.filter((r) => typeof r === "string");
|
|
23
|
+
}
|
|
24
|
+
if (typeof u.role === "string") {
|
|
25
|
+
return [
|
|
26
|
+
u.role
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var _AuthContext = class _AuthContext {
|
|
34
|
+
constructor(extractor) {
|
|
35
|
+
__publicField(this, "_isAuthenticated", false);
|
|
36
|
+
__publicField(this, "_user", null);
|
|
37
|
+
__publicField(this, "_userId", null);
|
|
38
|
+
__publicField(this, "_roles", []);
|
|
39
|
+
__publicField(this, "_authenticatedAt", null);
|
|
40
|
+
__publicField(this, "_expiresAt", null);
|
|
41
|
+
__publicField(this, "_extractor");
|
|
42
|
+
this._extractor = extractor ?? defaultUserExtractor;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @zh 是否已认证
|
|
46
|
+
* @en Whether authenticated
|
|
47
|
+
*/
|
|
48
|
+
get isAuthenticated() {
|
|
49
|
+
if (this._expiresAt && Date.now() > this._expiresAt) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return this._isAuthenticated;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* @zh 用户信息
|
|
56
|
+
* @en User information
|
|
57
|
+
*/
|
|
58
|
+
get user() {
|
|
59
|
+
return this._user;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @zh 用户 ID
|
|
63
|
+
* @en User ID
|
|
64
|
+
*/
|
|
65
|
+
get userId() {
|
|
66
|
+
return this._userId;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* @zh 用户角色
|
|
70
|
+
* @en User roles
|
|
71
|
+
*/
|
|
72
|
+
get roles() {
|
|
73
|
+
return this._roles;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @zh 认证时间
|
|
77
|
+
* @en Authentication timestamp
|
|
78
|
+
*/
|
|
79
|
+
get authenticatedAt() {
|
|
80
|
+
return this._authenticatedAt;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* @zh 令牌过期时间
|
|
84
|
+
* @en Token expiration time
|
|
85
|
+
*/
|
|
86
|
+
get expiresAt() {
|
|
87
|
+
return this._expiresAt;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* @zh 检查是否有指定角色
|
|
91
|
+
* @en Check if has specified role
|
|
92
|
+
*/
|
|
93
|
+
hasRole(role) {
|
|
94
|
+
return this._roles.includes(role);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* @zh 检查是否有任一指定角色
|
|
98
|
+
* @en Check if has any of specified roles
|
|
99
|
+
*/
|
|
100
|
+
hasAnyRole(roles) {
|
|
101
|
+
return roles.some((role) => this._roles.includes(role));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @zh 检查是否有所有指定角色
|
|
105
|
+
* @en Check if has all specified roles
|
|
106
|
+
*/
|
|
107
|
+
hasAllRoles(roles) {
|
|
108
|
+
return roles.every((role) => this._roles.includes(role));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* @zh 设置认证结果
|
|
112
|
+
* @en Set authentication result
|
|
113
|
+
*/
|
|
114
|
+
setAuthenticated(result) {
|
|
115
|
+
if (result.success && result.user) {
|
|
116
|
+
this._isAuthenticated = true;
|
|
117
|
+
this._user = result.user;
|
|
118
|
+
this._userId = this._extractor.getId(result.user);
|
|
119
|
+
this._roles = this._extractor.getRoles(result.user);
|
|
120
|
+
this._authenticatedAt = Date.now();
|
|
121
|
+
this._expiresAt = result.expiresAt ?? null;
|
|
122
|
+
} else {
|
|
123
|
+
this.clear();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* @zh 清除认证状态
|
|
128
|
+
* @en Clear authentication state
|
|
129
|
+
*/
|
|
130
|
+
clear() {
|
|
131
|
+
this._isAuthenticated = false;
|
|
132
|
+
this._user = null;
|
|
133
|
+
this._userId = null;
|
|
134
|
+
this._roles = [];
|
|
135
|
+
this._authenticatedAt = null;
|
|
136
|
+
this._expiresAt = null;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
__name(_AuthContext, "AuthContext");
|
|
140
|
+
var AuthContext = _AuthContext;
|
|
141
|
+
function createGuestContext() {
|
|
142
|
+
return new AuthContext();
|
|
143
|
+
}
|
|
144
|
+
__name(createGuestContext, "createGuestContext");
|
|
145
|
+
function createAuthContext(result, extractor) {
|
|
146
|
+
const context = new AuthContext(extractor);
|
|
147
|
+
context.setAuthenticated(result);
|
|
148
|
+
return context;
|
|
149
|
+
}
|
|
150
|
+
__name(createAuthContext, "createAuthContext");
|
|
151
|
+
var _JwtAuthProvider = class _JwtAuthProvider {
|
|
152
|
+
constructor(config) {
|
|
153
|
+
__publicField(this, "name", "jwt");
|
|
154
|
+
__publicField(this, "_config");
|
|
155
|
+
this._config = {
|
|
156
|
+
algorithm: "HS256",
|
|
157
|
+
expiresIn: 3600,
|
|
158
|
+
...config
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* @zh 验证令牌
|
|
163
|
+
* @en Verify token
|
|
164
|
+
*/
|
|
165
|
+
async verify(token) {
|
|
166
|
+
if (!token) {
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
error: "Token is required",
|
|
170
|
+
errorCode: "INVALID_TOKEN"
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const verifyOptions = {
|
|
175
|
+
algorithms: [
|
|
176
|
+
this._config.algorithm
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
if (this._config.issuer) {
|
|
180
|
+
verifyOptions.issuer = this._config.issuer;
|
|
181
|
+
}
|
|
182
|
+
if (this._config.audience) {
|
|
183
|
+
verifyOptions.audience = this._config.audience;
|
|
184
|
+
}
|
|
185
|
+
const payload = jwt.verify(token, this._config.secret, verifyOptions);
|
|
186
|
+
let user = null;
|
|
187
|
+
if (this._config.getUser) {
|
|
188
|
+
user = await this._config.getUser(payload);
|
|
189
|
+
if (!user) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: "User not found",
|
|
193
|
+
errorCode: "USER_NOT_FOUND"
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
user = payload;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
success: true,
|
|
201
|
+
user,
|
|
202
|
+
token,
|
|
203
|
+
expiresAt: payload.exp ? payload.exp * 1e3 : void 0
|
|
204
|
+
};
|
|
205
|
+
} catch (error) {
|
|
206
|
+
const err = error;
|
|
207
|
+
if (err.name === "TokenExpiredError") {
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: "Token has expired",
|
|
211
|
+
errorCode: "EXPIRED_TOKEN"
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
success: false,
|
|
216
|
+
error: err.message || "Invalid token",
|
|
217
|
+
errorCode: "INVALID_TOKEN"
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* @zh 刷新令牌
|
|
223
|
+
* @en Refresh token
|
|
224
|
+
*/
|
|
225
|
+
async refresh(token) {
|
|
226
|
+
const result = await this.verify(token);
|
|
227
|
+
if (!result.success || !result.user) {
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
const payload = jwt.decode(token);
|
|
231
|
+
const { iat, exp, nbf, ...restPayload } = payload;
|
|
232
|
+
const newToken = this.sign(restPayload);
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
user: result.user,
|
|
236
|
+
token: newToken,
|
|
237
|
+
expiresAt: Date.now() + this._config.expiresIn * 1e3
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* @zh 生成令牌
|
|
242
|
+
* @en Generate token
|
|
243
|
+
*/
|
|
244
|
+
sign(payload) {
|
|
245
|
+
const signOptions = {
|
|
246
|
+
algorithm: this._config.algorithm,
|
|
247
|
+
expiresIn: this._config.expiresIn
|
|
248
|
+
};
|
|
249
|
+
if (this._config.issuer) {
|
|
250
|
+
signOptions.issuer = this._config.issuer;
|
|
251
|
+
}
|
|
252
|
+
if (this._config.audience) {
|
|
253
|
+
signOptions.audience = this._config.audience;
|
|
254
|
+
}
|
|
255
|
+
return jwt.sign(payload, this._config.secret, signOptions);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* @zh 解码令牌(不验证)
|
|
259
|
+
* @en Decode token (without verification)
|
|
260
|
+
*/
|
|
261
|
+
decode(token) {
|
|
262
|
+
return jwt.decode(token);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
__name(_JwtAuthProvider, "JwtAuthProvider");
|
|
266
|
+
var JwtAuthProvider = _JwtAuthProvider;
|
|
267
|
+
function createJwtAuthProvider(config) {
|
|
268
|
+
return new JwtAuthProvider(config);
|
|
269
|
+
}
|
|
270
|
+
__name(createJwtAuthProvider, "createJwtAuthProvider");
|
|
271
|
+
var _SessionAuthProvider = class _SessionAuthProvider {
|
|
272
|
+
constructor(config) {
|
|
273
|
+
__publicField(this, "name", "session");
|
|
274
|
+
__publicField(this, "_config");
|
|
275
|
+
this._config = {
|
|
276
|
+
sessionTTL: 864e5,
|
|
277
|
+
prefix: "session:",
|
|
278
|
+
autoRenew: true,
|
|
279
|
+
...config
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* @zh 获取存储键
|
|
284
|
+
* @en Get storage key
|
|
285
|
+
*/
|
|
286
|
+
_getKey(sessionId) {
|
|
287
|
+
return `${this._config.prefix}${sessionId}`;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* @zh 验证 Session
|
|
291
|
+
* @en Verify session
|
|
292
|
+
*/
|
|
293
|
+
async verify(sessionId) {
|
|
294
|
+
if (!sessionId) {
|
|
295
|
+
return {
|
|
296
|
+
success: false,
|
|
297
|
+
error: "Session ID is required",
|
|
298
|
+
errorCode: "INVALID_TOKEN"
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
const key = this._getKey(sessionId);
|
|
302
|
+
const session = await this._config.storage.get(key);
|
|
303
|
+
if (!session) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
error: "Session not found or expired",
|
|
307
|
+
errorCode: "EXPIRED_TOKEN"
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
if (this._config.validateUser) {
|
|
311
|
+
const isValid = await this._config.validateUser(session.user);
|
|
312
|
+
if (!isValid) {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
error: "User validation failed",
|
|
316
|
+
errorCode: "ACCOUNT_DISABLED"
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (this._config.autoRenew) {
|
|
321
|
+
session.lastActiveAt = Date.now();
|
|
322
|
+
await this._config.storage.set(key, session, this._config.sessionTTL);
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
success: true,
|
|
326
|
+
user: session.user,
|
|
327
|
+
token: sessionId,
|
|
328
|
+
expiresAt: session.createdAt + this._config.sessionTTL
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* @zh 刷新 Session
|
|
333
|
+
* @en Refresh session
|
|
334
|
+
*/
|
|
335
|
+
async refresh(sessionId) {
|
|
336
|
+
const result = await this.verify(sessionId);
|
|
337
|
+
if (!result.success) {
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
const key = this._getKey(sessionId);
|
|
341
|
+
const session = await this._config.storage.get(key);
|
|
342
|
+
if (session) {
|
|
343
|
+
session.lastActiveAt = Date.now();
|
|
344
|
+
await this._config.storage.set(key, session, this._config.sessionTTL);
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
...result,
|
|
348
|
+
expiresAt: Date.now() + this._config.sessionTTL
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* @zh 撤销 Session
|
|
353
|
+
* @en Revoke session
|
|
354
|
+
*/
|
|
355
|
+
async revoke(sessionId) {
|
|
356
|
+
const key = this._getKey(sessionId);
|
|
357
|
+
return await this._config.storage.delete(key);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* @zh 创建 Session
|
|
361
|
+
* @en Create session
|
|
362
|
+
*/
|
|
363
|
+
async createSession(user, data) {
|
|
364
|
+
const sessionId = this._generateSessionId();
|
|
365
|
+
const key = this._getKey(sessionId);
|
|
366
|
+
const session = {
|
|
367
|
+
user,
|
|
368
|
+
createdAt: Date.now(),
|
|
369
|
+
lastActiveAt: Date.now(),
|
|
370
|
+
data
|
|
371
|
+
};
|
|
372
|
+
await this._config.storage.set(key, session, this._config.sessionTTL);
|
|
373
|
+
return sessionId;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* @zh 获取 Session 数据
|
|
377
|
+
* @en Get session data
|
|
378
|
+
*/
|
|
379
|
+
async getSession(sessionId) {
|
|
380
|
+
const key = this._getKey(sessionId);
|
|
381
|
+
return await this._config.storage.get(key);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* @zh 更新 Session 数据
|
|
385
|
+
* @en Update session data
|
|
386
|
+
*/
|
|
387
|
+
async updateSession(sessionId, data) {
|
|
388
|
+
const key = this._getKey(sessionId);
|
|
389
|
+
const session = await this._config.storage.get(key);
|
|
390
|
+
if (!session) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
session.data = {
|
|
394
|
+
...session.data,
|
|
395
|
+
...data
|
|
396
|
+
};
|
|
397
|
+
session.lastActiveAt = Date.now();
|
|
398
|
+
await this._config.storage.set(key, session, this._config.sessionTTL);
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* @zh 生成 Session ID(使用加密安全的随机数)
|
|
403
|
+
* @en Generate session ID (using cryptographically secure random)
|
|
404
|
+
*/
|
|
405
|
+
_generateSessionId() {
|
|
406
|
+
return randomBytes(32).toString("hex");
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
__name(_SessionAuthProvider, "SessionAuthProvider");
|
|
410
|
+
var SessionAuthProvider = _SessionAuthProvider;
|
|
411
|
+
function createSessionAuthProvider(config) {
|
|
412
|
+
return new SessionAuthProvider(config);
|
|
413
|
+
}
|
|
414
|
+
__name(createSessionAuthProvider, "createSessionAuthProvider");
|
|
415
|
+
|
|
416
|
+
// src/auth/mixin/withAuth.ts
|
|
417
|
+
var AUTH_CONTEXT_KEY = /* @__PURE__ */ Symbol("authContext");
|
|
418
|
+
function getAuthContext(conn) {
|
|
419
|
+
const data = conn.data;
|
|
420
|
+
return data[AUTH_CONTEXT_KEY] ?? null;
|
|
421
|
+
}
|
|
422
|
+
__name(getAuthContext, "getAuthContext");
|
|
423
|
+
function setAuthContext(conn, context) {
|
|
424
|
+
const data = conn.data;
|
|
425
|
+
data[AUTH_CONTEXT_KEY] = context;
|
|
426
|
+
}
|
|
427
|
+
__name(setAuthContext, "setAuthContext");
|
|
428
|
+
function withAuth(server, config) {
|
|
429
|
+
const { provider, extractCredentials, autoAuthOnConnect = true, disconnectOnAuthFailure = false, onAuthSuccess, onAuthFailure } = config;
|
|
430
|
+
const originalConnections = server.connections;
|
|
431
|
+
const connectionAuthMap = /* @__PURE__ */ new WeakMap();
|
|
432
|
+
const authServer = {
|
|
433
|
+
...server,
|
|
434
|
+
get authProvider() {
|
|
435
|
+
return provider;
|
|
436
|
+
},
|
|
437
|
+
async authenticate(conn, credentials) {
|
|
438
|
+
const result = await provider.verify(credentials);
|
|
439
|
+
let authContext = connectionAuthMap.get(conn);
|
|
440
|
+
if (!authContext) {
|
|
441
|
+
authContext = new AuthContext();
|
|
442
|
+
connectionAuthMap.set(conn, authContext);
|
|
443
|
+
setAuthContext(conn, authContext);
|
|
444
|
+
}
|
|
445
|
+
if (result.success) {
|
|
446
|
+
authContext.setAuthenticated(result);
|
|
447
|
+
await onAuthSuccess?.(conn, result.user);
|
|
448
|
+
} else {
|
|
449
|
+
authContext.clear();
|
|
450
|
+
await onAuthFailure?.(conn, result);
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
453
|
+
},
|
|
454
|
+
getAuthContext(conn) {
|
|
455
|
+
return connectionAuthMap.get(conn) ?? null;
|
|
456
|
+
},
|
|
457
|
+
get connections() {
|
|
458
|
+
return originalConnections;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
const originalOnConnect = server._onConnect;
|
|
462
|
+
server._onConnect = async (conn, req) => {
|
|
463
|
+
const authContext = new AuthContext();
|
|
464
|
+
connectionAuthMap.set(conn, authContext);
|
|
465
|
+
setAuthContext(conn, authContext);
|
|
466
|
+
if (autoAuthOnConnect && extractCredentials && req) {
|
|
467
|
+
try {
|
|
468
|
+
const connReq = req;
|
|
469
|
+
const credentials = await extractCredentials(connReq);
|
|
470
|
+
if (credentials) {
|
|
471
|
+
const result = await provider.verify(credentials);
|
|
472
|
+
if (result.success) {
|
|
473
|
+
authContext.setAuthenticated(result);
|
|
474
|
+
await onAuthSuccess?.(conn, result.user);
|
|
475
|
+
} else {
|
|
476
|
+
await onAuthFailure?.(conn, result);
|
|
477
|
+
if (disconnectOnAuthFailure) {
|
|
478
|
+
conn.close?.();
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error("[Auth] Error during auto-authentication:", error);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (originalOnConnect) {
|
|
488
|
+
await originalOnConnect(conn, req);
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
const originalOnDisconnect = server._onDisconnect;
|
|
492
|
+
server._onDisconnect = async (conn) => {
|
|
493
|
+
connectionAuthMap.delete(conn);
|
|
494
|
+
if (originalOnDisconnect) {
|
|
495
|
+
await originalOnDisconnect(conn);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
return authServer;
|
|
499
|
+
}
|
|
500
|
+
__name(withAuth, "withAuth");
|
|
501
|
+
function requireAuthentication(conn, options) {
|
|
502
|
+
const auth = getAuthContext(conn);
|
|
503
|
+
if (!auth || !auth.isAuthenticated) {
|
|
504
|
+
throw new Error(options?.errorMessage ?? "Authentication required");
|
|
505
|
+
}
|
|
506
|
+
return auth;
|
|
507
|
+
}
|
|
508
|
+
__name(requireAuthentication, "requireAuthentication");
|
|
509
|
+
function requireRole(conn, roles, options) {
|
|
510
|
+
const auth = requireAuthentication(conn);
|
|
511
|
+
const roleArray = Array.isArray(roles) ? roles : [
|
|
512
|
+
roles
|
|
513
|
+
];
|
|
514
|
+
const mode = options?.mode ?? "any";
|
|
515
|
+
const hasRole = mode === "any" ? auth.hasAnyRole(roleArray) : auth.hasAllRoles(roleArray);
|
|
516
|
+
if (!hasRole) {
|
|
517
|
+
throw new Error(options?.errorMessage ?? "Insufficient permissions");
|
|
518
|
+
}
|
|
519
|
+
return auth;
|
|
520
|
+
}
|
|
521
|
+
__name(requireRole, "requireRole");
|
|
522
|
+
|
|
523
|
+
// src/auth/mixin/withRoomAuth.ts
|
|
524
|
+
var playerAuthContexts = /* @__PURE__ */ new WeakMap();
|
|
525
|
+
function wrapPlayerWithAuth(player, authContext) {
|
|
526
|
+
playerAuthContexts.set(player, authContext);
|
|
527
|
+
Object.defineProperty(player, "auth", {
|
|
528
|
+
get: /* @__PURE__ */ __name(() => playerAuthContexts.get(player) ?? createGuestContext(), "get"),
|
|
529
|
+
enumerable: true,
|
|
530
|
+
configurable: false
|
|
531
|
+
});
|
|
532
|
+
Object.defineProperty(player, "user", {
|
|
533
|
+
get: /* @__PURE__ */ __name(() => playerAuthContexts.get(player)?.user ?? null, "get"),
|
|
534
|
+
enumerable: true,
|
|
535
|
+
configurable: false
|
|
536
|
+
});
|
|
537
|
+
return player;
|
|
538
|
+
}
|
|
539
|
+
__name(wrapPlayerWithAuth, "wrapPlayerWithAuth");
|
|
540
|
+
function withRoomAuth(Base, config = {}) {
|
|
541
|
+
var _a;
|
|
542
|
+
const { requireAuth: requireAuth2 = true, allowedRoles = [], roleCheckMode = "any" } = config;
|
|
543
|
+
let AuthRoom = (_a = class extends Base {
|
|
544
|
+
constructor(...args) {
|
|
545
|
+
super(...args);
|
|
546
|
+
__publicField(this, "_originalOnJoin");
|
|
547
|
+
this._originalOnJoin = this.onJoin?.bind(this);
|
|
548
|
+
this.onJoin = this._authOnJoin.bind(this);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* @zh 包装的 onJoin 方法
|
|
552
|
+
* @en Wrapped onJoin method
|
|
553
|
+
*/
|
|
554
|
+
async _authOnJoin(player) {
|
|
555
|
+
const conn = player.connection ?? player._conn;
|
|
556
|
+
const authContext = conn ? getAuthContext(conn) ?? createGuestContext() : createGuestContext();
|
|
557
|
+
if (requireAuth2 && !authContext.isAuthenticated) {
|
|
558
|
+
console.warn(`[AuthRoom] Rejected unauthenticated player: ${player.id}`);
|
|
559
|
+
this.kick(player, "Authentication required");
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (allowedRoles.length > 0) {
|
|
563
|
+
const hasRole = roleCheckMode === "any" ? authContext.hasAnyRole(allowedRoles) : authContext.hasAllRoles(allowedRoles);
|
|
564
|
+
if (!hasRole) {
|
|
565
|
+
console.warn(`[AuthRoom] Rejected player ${player.id}: insufficient roles`);
|
|
566
|
+
this.kick(player, "Insufficient permissions");
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
const authPlayer = wrapPlayerWithAuth(player, authContext);
|
|
571
|
+
if (typeof this.onAuth === "function") {
|
|
572
|
+
try {
|
|
573
|
+
const allowed = await this.onAuth(authPlayer);
|
|
574
|
+
if (!allowed) {
|
|
575
|
+
console.warn(`[AuthRoom] Rejected player ${player.id}: onAuth returned false`);
|
|
576
|
+
this.kick(player, "Authentication rejected");
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
} catch (error) {
|
|
580
|
+
console.error(`[AuthRoom] Error in onAuth for player ${player.id}:`, error);
|
|
581
|
+
this.kick(player, "Authentication error");
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
if (this._originalOnJoin) {
|
|
586
|
+
await this._originalOnJoin(authPlayer);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* @zh 获取带认证信息的玩家
|
|
591
|
+
* @en Get player with auth info
|
|
592
|
+
*/
|
|
593
|
+
getAuthPlayer(id) {
|
|
594
|
+
const player = this.getPlayer(id);
|
|
595
|
+
return player;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* @zh 获取所有带认证信息的玩家
|
|
599
|
+
* @en Get all players with auth info
|
|
600
|
+
*/
|
|
601
|
+
getAuthPlayers() {
|
|
602
|
+
return this.players;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* @zh 按角色获取玩家
|
|
606
|
+
* @en Get players by role
|
|
607
|
+
*/
|
|
608
|
+
getPlayersByRole(role) {
|
|
609
|
+
return this.getAuthPlayers().filter((p) => p.auth?.hasRole(role));
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* @zh 按用户 ID 获取玩家
|
|
613
|
+
* @en Get player by user ID
|
|
614
|
+
*/
|
|
615
|
+
getPlayerByUserId(userId) {
|
|
616
|
+
return this.getAuthPlayers().find((p) => p.auth?.userId === userId);
|
|
617
|
+
}
|
|
618
|
+
}, __name(_a, "AuthRoom"), _a);
|
|
619
|
+
return AuthRoom;
|
|
620
|
+
}
|
|
621
|
+
__name(withRoomAuth, "withRoomAuth");
|
|
622
|
+
var _AuthRoomBase = class _AuthRoomBase {
|
|
623
|
+
constructor() {
|
|
624
|
+
/**
|
|
625
|
+
* @zh 认证配置(子类可覆盖)
|
|
626
|
+
* @en Auth config (can be overridden by subclass)
|
|
627
|
+
*/
|
|
628
|
+
__publicField(this, "authConfig", {
|
|
629
|
+
requireAuth: true,
|
|
630
|
+
allowedRoles: [],
|
|
631
|
+
roleCheckMode: "any"
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
getAuthPlayer(id) {
|
|
635
|
+
return void 0;
|
|
636
|
+
}
|
|
637
|
+
getAuthPlayers() {
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
getPlayersByRole(role) {
|
|
641
|
+
return [];
|
|
642
|
+
}
|
|
643
|
+
getPlayerByUserId(userId) {
|
|
644
|
+
return void 0;
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
__name(_AuthRoomBase, "AuthRoomBase");
|
|
648
|
+
var AuthRoomBase = _AuthRoomBase;
|
|
649
|
+
|
|
650
|
+
// src/auth/decorators/requireAuth.ts
|
|
651
|
+
var AUTH_METADATA_KEY = /* @__PURE__ */ Symbol("authMetadata");
|
|
652
|
+
function getAuthMetadata(target, propertyKey) {
|
|
653
|
+
const metadata = target[AUTH_METADATA_KEY];
|
|
654
|
+
return metadata?.get(propertyKey);
|
|
655
|
+
}
|
|
656
|
+
__name(getAuthMetadata, "getAuthMetadata");
|
|
657
|
+
function setAuthMetadata(target, propertyKey, metadata) {
|
|
658
|
+
if (!target[AUTH_METADATA_KEY]) {
|
|
659
|
+
target[AUTH_METADATA_KEY] = /* @__PURE__ */ new Map();
|
|
660
|
+
}
|
|
661
|
+
target[AUTH_METADATA_KEY].set(propertyKey, metadata);
|
|
662
|
+
}
|
|
663
|
+
__name(setAuthMetadata, "setAuthMetadata");
|
|
664
|
+
function requireAuth(options) {
|
|
665
|
+
return function(target, propertyKey, descriptor) {
|
|
666
|
+
const key = String(propertyKey);
|
|
667
|
+
const existing = getAuthMetadata(target, key);
|
|
668
|
+
setAuthMetadata(target, key, {
|
|
669
|
+
...existing,
|
|
670
|
+
requireAuth: true,
|
|
671
|
+
options
|
|
672
|
+
});
|
|
673
|
+
return descriptor;
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
__name(requireAuth, "requireAuth");
|
|
677
|
+
|
|
678
|
+
// src/auth/decorators/requireRole.ts
|
|
679
|
+
function setAuthMetadata2(target, propertyKey, metadata) {
|
|
680
|
+
if (!target[AUTH_METADATA_KEY]) {
|
|
681
|
+
target[AUTH_METADATA_KEY] = /* @__PURE__ */ new Map();
|
|
682
|
+
}
|
|
683
|
+
target[AUTH_METADATA_KEY].set(propertyKey, metadata);
|
|
684
|
+
}
|
|
685
|
+
__name(setAuthMetadata2, "setAuthMetadata");
|
|
686
|
+
function requireRole2(roles, options) {
|
|
687
|
+
return function(target, propertyKey, descriptor) {
|
|
688
|
+
const key = String(propertyKey);
|
|
689
|
+
const existing = getAuthMetadata(target, key);
|
|
690
|
+
const roleArray = Array.isArray(roles) ? roles : [
|
|
691
|
+
roles
|
|
692
|
+
];
|
|
693
|
+
setAuthMetadata2(target, key, {
|
|
694
|
+
...existing,
|
|
695
|
+
requireAuth: true,
|
|
696
|
+
roles: roleArray,
|
|
697
|
+
roleMode: options?.mode ?? "any",
|
|
698
|
+
options
|
|
699
|
+
});
|
|
700
|
+
return descriptor;
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
__name(requireRole2, "requireRole");
|
|
704
|
+
|
|
705
|
+
export { AUTH_METADATA_KEY, AuthContext, AuthRoomBase, JwtAuthProvider, SessionAuthProvider, createAuthContext, createGuestContext, createJwtAuthProvider, createSessionAuthProvider, defaultUserExtractor, getAuthContext, getAuthMetadata, requireAuth, requireAuthentication, requireRole2 as requireRole, requireRole as requireRoleCheck, setAuthContext, withAuth, withRoomAuth };
|
|
706
|
+
//# sourceMappingURL=index.js.map
|
|
707
|
+
//# sourceMappingURL=index.js.map
|