@technomoron/api-server-base 2.0.0-beta.19 → 2.0.0-beta.20
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/cjs/api-server-base.cjs +48 -21
- package/dist/cjs/auth-api/auth-module.js +16 -30
- package/dist/cjs/auth-api/mem-auth-store.js +5 -4
- package/dist/cjs/auth-api/sql-auth-store.js +6 -4
- package/dist/cjs/auth-api/user-id.d.ts +1 -0
- package/dist/cjs/auth-api/user-id.js +7 -0
- package/dist/cjs/auth-cookie-options.js +10 -1
- package/dist/cjs/oauth/memory.d.ts +6 -0
- package/dist/cjs/oauth/memory.js +43 -4
- package/dist/cjs/oauth/models.d.ts +1 -0
- package/dist/cjs/oauth/models.js +7 -18
- package/dist/cjs/oauth/sequelize.d.ts +5 -52
- package/dist/cjs/oauth/sequelize.js +34 -93
- package/dist/cjs/oauth/types.d.ts +1 -0
- package/dist/cjs/passkey/base.d.ts +1 -0
- package/dist/cjs/passkey/memory.d.ts +7 -0
- package/dist/cjs/passkey/memory.js +47 -5
- package/dist/cjs/passkey/models.js +2 -5
- package/dist/cjs/passkey/sequelize.d.ts +5 -29
- package/dist/cjs/passkey/sequelize.js +48 -191
- package/dist/cjs/passkey/service.d.ts +1 -0
- package/dist/cjs/passkey/service.js +52 -15
- package/dist/cjs/passkey/types.d.ts +1 -0
- package/dist/cjs/sequelize-utils.d.ts +5 -0
- package/dist/cjs/sequelize-utils.js +40 -0
- package/dist/cjs/token/base.js +3 -1
- package/dist/cjs/token/memory.d.ts +6 -0
- package/dist/cjs/token/memory.js +32 -7
- package/dist/cjs/token/sequelize.d.ts +0 -3
- package/dist/cjs/token/sequelize.js +50 -81
- package/dist/cjs/token/types.d.ts +1 -1
- package/dist/cjs/user/base.d.ts +1 -0
- package/dist/cjs/user/base.js +11 -4
- package/dist/cjs/user/memory.d.ts +2 -0
- package/dist/cjs/user/memory.js +8 -2
- package/dist/cjs/user/sequelize.js +12 -22
- package/dist/esm/api-server-base.js +48 -21
- package/dist/esm/auth-api/auth-module.js +16 -30
- package/dist/esm/auth-api/mem-auth-store.js +5 -4
- package/dist/esm/auth-api/sql-auth-store.js +6 -4
- package/dist/esm/auth-api/user-id.d.ts +1 -0
- package/dist/esm/auth-api/user-id.js +6 -0
- package/dist/esm/auth-cookie-options.js +10 -1
- package/dist/esm/oauth/memory.d.ts +6 -0
- package/dist/esm/oauth/memory.js +44 -5
- package/dist/esm/oauth/models.d.ts +1 -0
- package/dist/esm/oauth/models.js +2 -15
- package/dist/esm/oauth/sequelize.d.ts +5 -52
- package/dist/esm/oauth/sequelize.js +21 -80
- package/dist/esm/oauth/types.d.ts +1 -0
- package/dist/esm/passkey/base.d.ts +1 -0
- package/dist/esm/passkey/memory.d.ts +7 -0
- package/dist/esm/passkey/memory.js +47 -5
- package/dist/esm/passkey/models.js +1 -4
- package/dist/esm/passkey/sequelize.d.ts +5 -29
- package/dist/esm/passkey/sequelize.js +47 -190
- package/dist/esm/passkey/service.d.ts +1 -0
- package/dist/esm/passkey/service.js +52 -15
- package/dist/esm/passkey/types.d.ts +1 -0
- package/dist/esm/sequelize-utils.d.ts +5 -0
- package/dist/esm/sequelize-utils.js +36 -0
- package/dist/esm/token/base.js +3 -1
- package/dist/esm/token/memory.d.ts +6 -0
- package/dist/esm/token/memory.js +32 -7
- package/dist/esm/token/sequelize.d.ts +0 -3
- package/dist/esm/token/sequelize.js +51 -82
- package/dist/esm/token/types.d.ts +1 -1
- package/dist/esm/user/base.d.ts +1 -0
- package/dist/esm/user/base.js +11 -4
- package/dist/esm/user/memory.d.ts +2 -0
- package/dist/esm/user/memory.js +8 -2
- package/dist/esm/user/sequelize.js +13 -23
- package/package.json +5 -5
|
@@ -11,7 +11,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.ApiServer = exports.ApiError = exports.ApiModule = void 0;
|
|
13
13
|
const node_crypto_1 = require("node:crypto");
|
|
14
|
-
const
|
|
14
|
+
const promises_1 = require("node:fs/promises");
|
|
15
15
|
const node_module_1 = require("node:module");
|
|
16
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
17
|
const cookie_parser_1 = __importDefault(require("cookie-parser"));
|
|
@@ -20,6 +20,7 @@ const express_1 = __importDefault(require("express"));
|
|
|
20
20
|
const multer_1 = __importDefault(require("multer"));
|
|
21
21
|
const module_js_1 = require("./auth-api/module.js");
|
|
22
22
|
const storage_js_1 = require("./auth-api/storage.js");
|
|
23
|
+
const user_id_js_1 = require("./auth-api/user-id.js");
|
|
23
24
|
const auth_cookie_options_js_1 = require("./auth-cookie-options.js");
|
|
24
25
|
const base_js_1 = require("./token/base.js");
|
|
25
26
|
class JwtHelperStore extends base_js_1.TokenStore {
|
|
@@ -274,7 +275,9 @@ function collectClientIpChain(req) {
|
|
|
274
275
|
}
|
|
275
276
|
const realIp = req.headers['x-real-ip'];
|
|
276
277
|
if (Array.isArray(realIp)) {
|
|
277
|
-
|
|
278
|
+
for (const value of realIp) {
|
|
279
|
+
pushNormalized(normalizeIpAddress(value));
|
|
280
|
+
}
|
|
278
281
|
}
|
|
279
282
|
else if (typeof realIp === 'string') {
|
|
280
283
|
pushNormalized(normalizeIpAddress(realIp));
|
|
@@ -429,6 +432,10 @@ class ApiServer {
|
|
|
429
432
|
this.canImpersonateAdapter = canImpersonate ?? null;
|
|
430
433
|
this.storageAdapter = this.getServerAuthAdapter();
|
|
431
434
|
}
|
|
435
|
+
if ((this.config.authApi || this.config.authStores) &&
|
|
436
|
+
(!this.config.accessSecret || !this.config.refreshSecret)) {
|
|
437
|
+
console.warn('[api-server-base] Auth is enabled but accessSecret and/or refreshSecret are empty. JWT signing will fail at runtime.');
|
|
438
|
+
}
|
|
432
439
|
this.app = (0, express_1.default)();
|
|
433
440
|
// Mount API modules and any custom endpoints under `apiBasePath` on this router so we can keep
|
|
434
441
|
// the API 404 handler ordered last without relying on Express internals.
|
|
@@ -616,8 +623,8 @@ class ApiServer {
|
|
|
616
623
|
async getToken(query, opts) {
|
|
617
624
|
const normalized = {
|
|
618
625
|
...query,
|
|
619
|
-
userId:
|
|
620
|
-
ruid:
|
|
626
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
627
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
621
628
|
};
|
|
622
629
|
if (this.tokenStoreAdapter) {
|
|
623
630
|
return this.tokenStoreAdapter.get(normalized, opts);
|
|
@@ -631,8 +638,8 @@ class ApiServer {
|
|
|
631
638
|
async deleteToken(query) {
|
|
632
639
|
const normalized = {
|
|
633
640
|
...query,
|
|
634
|
-
userId:
|
|
635
|
-
ruid:
|
|
641
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
642
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
636
643
|
};
|
|
637
644
|
if (this.tokenStoreAdapter) {
|
|
638
645
|
return this.tokenStoreAdapter.delete(normalized);
|
|
@@ -795,7 +802,7 @@ class ApiServer {
|
|
|
795
802
|
res.status(200).json({ success: true, code: 200, message: 'Success', data: payload, errors: {} });
|
|
796
803
|
});
|
|
797
804
|
}
|
|
798
|
-
loadSwaggerSpec() {
|
|
805
|
+
async loadSwaggerSpec() {
|
|
799
806
|
const candidates = [node_path_1.default.resolve(process.cwd(), 'docs/swagger/openapi.json')];
|
|
800
807
|
if (typeof __dirname === 'string') {
|
|
801
808
|
candidates.push(node_path_1.default.resolve(__dirname, '../../docs/swagger/openapi.json'));
|
|
@@ -810,11 +817,14 @@ class ApiServer {
|
|
|
810
817
|
// Ignore resolution failures; fall back to any existing candidate.
|
|
811
818
|
}
|
|
812
819
|
for (const candidate of candidates) {
|
|
813
|
-
|
|
820
|
+
try {
|
|
821
|
+
await (0, promises_1.access)(candidate);
|
|
822
|
+
}
|
|
823
|
+
catch {
|
|
814
824
|
continue;
|
|
815
825
|
}
|
|
816
826
|
try {
|
|
817
|
-
const raw =
|
|
827
|
+
const raw = await (0, promises_1.readFile)(candidate, 'utf8');
|
|
818
828
|
return JSON.parse(raw);
|
|
819
829
|
}
|
|
820
830
|
catch {
|
|
@@ -832,12 +842,12 @@ class ApiServer {
|
|
|
832
842
|
const base = this.apiBasePath === '/' ? '' : this.apiBasePath;
|
|
833
843
|
const resolved = rawPath.length > 0 ? rawPath : `${base}/swagger`;
|
|
834
844
|
const path = resolved.startsWith('/') ? resolved : `/${resolved}`;
|
|
835
|
-
let
|
|
836
|
-
this.app.get(path, (_req, res) => {
|
|
837
|
-
if (
|
|
838
|
-
|
|
845
|
+
let specPromise;
|
|
846
|
+
this.app.get(path, async (_req, res) => {
|
|
847
|
+
if (!specPromise) {
|
|
848
|
+
specPromise = this.loadSwaggerSpec();
|
|
839
849
|
}
|
|
840
|
-
const spec =
|
|
850
|
+
const spec = await specPromise;
|
|
841
851
|
if (!spec) {
|
|
842
852
|
res.status(500).json({
|
|
843
853
|
success: false,
|
|
@@ -1050,7 +1060,7 @@ class ApiServer {
|
|
|
1050
1060
|
let tokenData;
|
|
1051
1061
|
let error;
|
|
1052
1062
|
let expired = false;
|
|
1053
|
-
if (!token
|
|
1063
|
+
if (!token) {
|
|
1054
1064
|
if (authType === 'maybe') {
|
|
1055
1065
|
if (!this.config.refreshMaybe) {
|
|
1056
1066
|
return null;
|
|
@@ -1203,9 +1213,6 @@ class ApiServer {
|
|
|
1203
1213
|
if (rawReal === null) {
|
|
1204
1214
|
return effectiveUserId;
|
|
1205
1215
|
}
|
|
1206
|
-
if (typeof rawReal === 'number' && rawReal === 0) {
|
|
1207
|
-
return effectiveUserId;
|
|
1208
|
-
}
|
|
1209
1216
|
return rawReal;
|
|
1210
1217
|
}
|
|
1211
1218
|
useExpress(pathOrHandler, ...handlers) {
|
|
@@ -1429,9 +1436,29 @@ class ApiServer {
|
|
|
1429
1436
|
console.log('URL:', url);
|
|
1430
1437
|
console.log('Method:', req.method);
|
|
1431
1438
|
console.log('Query Params:', req.query || {});
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1439
|
+
const sensitiveBodyKeys = ['password', 'client_secret', 'clientSecret', 'secret'];
|
|
1440
|
+
const body = req.body && typeof req.body === 'object' ? { ...req.body } : req.body;
|
|
1441
|
+
if (body && typeof body === 'object') {
|
|
1442
|
+
for (const key of sensitiveBodyKeys) {
|
|
1443
|
+
if (key in body) {
|
|
1444
|
+
body[key] = '[REDACTED]';
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
console.log('Body Params:', body || {});
|
|
1449
|
+
const cookies = req.cookies ? { ...req.cookies } : {};
|
|
1450
|
+
const sensitiveCookieKeys = [this.config.accessCookie, this.config.refreshCookie];
|
|
1451
|
+
for (const key of sensitiveCookieKeys) {
|
|
1452
|
+
if (key in cookies) {
|
|
1453
|
+
cookies[key] = '[REDACTED]';
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
console.log('Cookies:', cookies);
|
|
1457
|
+
const headers = { ...req.headers };
|
|
1458
|
+
if (headers.authorization) {
|
|
1459
|
+
headers.authorization = '[REDACTED]';
|
|
1460
|
+
}
|
|
1461
|
+
console.log('Headers:', headers);
|
|
1435
1462
|
console.log('------------------------');
|
|
1436
1463
|
}
|
|
1437
1464
|
formatDebugValue(value, maxLength = 50, seen = new WeakSet()) {
|
|
@@ -157,6 +157,9 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
157
157
|
return candidate ? {} : { sessionCookie: true, refreshTtlSeconds: this.sessionRefreshTtlSeconds() };
|
|
158
158
|
}
|
|
159
159
|
if (typeof candidate === 'number') {
|
|
160
|
+
if (candidate === 0) {
|
|
161
|
+
return { sessionCookie: true, refreshTtlSeconds: this.sessionRefreshTtlSeconds() };
|
|
162
|
+
}
|
|
160
163
|
const ttl = this.normalizeRefreshTtlSeconds(candidate);
|
|
161
164
|
return ttl ? { sessionCookie: false, refreshTtlSeconds: ttl } : {};
|
|
162
165
|
}
|
|
@@ -449,20 +452,12 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
449
452
|
this.assertAuthReady();
|
|
450
453
|
const { login, password, ...metadata } = this.parseLoginBody(apiReq);
|
|
451
454
|
const user = await this.storage.getUser(login);
|
|
452
|
-
|
|
455
|
+
const hash = user ? this.storage.getUserPasswordHash(user) : '';
|
|
456
|
+
const verified = user ? await this.storage.verifyPassword(password, hash) : false;
|
|
457
|
+
if (!user || !verified) {
|
|
453
458
|
throw new api_server_base_js_1.ApiError({
|
|
454
459
|
code: 400,
|
|
455
|
-
message: 'Invalid credentials'
|
|
456
|
-
errors: { login: 'Unknown user' }
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
const hash = this.storage.getUserPasswordHash(user);
|
|
460
|
-
const verified = await this.storage.verifyPassword(password, hash);
|
|
461
|
-
if (!verified) {
|
|
462
|
-
throw new api_server_base_js_1.ApiError({
|
|
463
|
-
code: 400,
|
|
464
|
-
message: 'Invalid credentials',
|
|
465
|
-
errors: { password: 'Wrong password' }
|
|
460
|
+
message: 'Invalid credentials'
|
|
466
461
|
});
|
|
467
462
|
}
|
|
468
463
|
const pair = await this.issueTokens(apiReq, user, metadata);
|
|
@@ -504,6 +499,7 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
504
499
|
refreshTtlSeconds: sessionPrefs.refreshTtlSeconds ?? stored.refreshTtlSeconds,
|
|
505
500
|
sessionCookie: sessionPrefs.sessionCookie ?? stored.sessionCookie
|
|
506
501
|
};
|
|
502
|
+
await this.storage.deleteToken({ refreshToken: providedToken });
|
|
507
503
|
const pair = await this.issueTokens(apiReq, user, metadata);
|
|
508
504
|
const publicUser = this.storage.filterUser(user);
|
|
509
505
|
return [200, { ...pair, user: publicUser }];
|
|
@@ -913,7 +909,7 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
913
909
|
}
|
|
914
910
|
}
|
|
915
911
|
}
|
|
916
|
-
else if (!clientSecretProvided && client.clientSecret) {
|
|
912
|
+
else if (!clientSecretProvided && (client.hasSecret ?? Boolean(client.clientSecret))) {
|
|
917
913
|
throw new api_server_base_js_1.ApiError({ code: 400, message: 'Client authentication required when no PKCE challenge present' });
|
|
918
914
|
}
|
|
919
915
|
const user = await this.getUserOrThrow(record.userId, 'User not found');
|
|
@@ -948,6 +944,7 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
948
944
|
throw new api_server_base_js_1.ApiError({ code: 400, message: 'Refresh token issued to another client' });
|
|
949
945
|
}
|
|
950
946
|
const user = await this.getUserOrThrow(stored.userId ?? verify.data.uid, 'User not found');
|
|
947
|
+
await this.storage.deleteToken({ refreshToken });
|
|
951
948
|
const tokens = await this.issueTokens(apiReq, user, {
|
|
952
949
|
clientId: client.clientId,
|
|
953
950
|
scope: stored.scope,
|
|
@@ -956,11 +953,7 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
956
953
|
loginType: stored.loginType ?? 'oauth'
|
|
957
954
|
});
|
|
958
955
|
this.clearOAuthCookies(apiReq);
|
|
959
|
-
const scope = Array.isArray(stored.scope)
|
|
960
|
-
? stored.scope
|
|
961
|
-
: typeof stored.scope === 'string'
|
|
962
|
-
? stored.scope.split(/\s+/).filter((entry) => entry.length > 0)
|
|
963
|
-
: [];
|
|
956
|
+
const scope = Array.isArray(stored.scope) ? stored.scope : [];
|
|
964
957
|
return [200, this.buildTokenResponse(tokens, client, scope)];
|
|
965
958
|
}
|
|
966
959
|
clearOAuthCookies(apiReq) {
|
|
@@ -1023,7 +1016,7 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
1023
1016
|
if (!client) {
|
|
1024
1017
|
throw new api_server_base_js_1.ApiError({ code: 400, message: 'Unknown client_id' });
|
|
1025
1018
|
}
|
|
1026
|
-
const requiresSecret =
|
|
1019
|
+
const requiresSecret = client.hasSecret ?? Boolean(client.clientSecret);
|
|
1027
1020
|
if (requiresSecret) {
|
|
1028
1021
|
if (!secretProvided) {
|
|
1029
1022
|
throw new api_server_base_js_1.ApiError({ code: 400, message: 'Client authentication is required' });
|
|
@@ -1059,17 +1052,10 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
1059
1052
|
const password = toStringOrNull(body.password);
|
|
1060
1053
|
if (login && password) {
|
|
1061
1054
|
const user = await this.storage.getUser(login);
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const verified = await this.storage.verifyPassword(password, hash);
|
|
1067
|
-
if (!verified) {
|
|
1068
|
-
throw new api_server_base_js_1.ApiError({
|
|
1069
|
-
code: 400,
|
|
1070
|
-
message: 'Invalid credentials',
|
|
1071
|
-
errors: { password: 'Wrong password' }
|
|
1072
|
-
});
|
|
1055
|
+
const hash = user ? this.storage.getUserPasswordHash(user) : '';
|
|
1056
|
+
const verified = user ? await this.storage.verifyPassword(password, hash) : false;
|
|
1057
|
+
if (!user || !verified) {
|
|
1058
|
+
throw new api_server_base_js_1.ApiError({ code: 400, message: 'Invalid credentials' });
|
|
1073
1059
|
}
|
|
1074
1060
|
return user;
|
|
1075
1061
|
}
|
|
@@ -7,6 +7,7 @@ const memory_js_2 = require("../passkey/memory.js");
|
|
|
7
7
|
const memory_js_3 = require("../token/memory.js");
|
|
8
8
|
const memory_js_4 = require("../user/memory.js");
|
|
9
9
|
const compat_auth_storage_js_1 = require("./compat-auth-storage.js");
|
|
10
|
+
const user_id_js_1 = require("./user-id.js");
|
|
10
11
|
class MemAuthStore {
|
|
11
12
|
constructor(params = {}) {
|
|
12
13
|
this.userStore = new memory_js_4.MemoryUserStore({
|
|
@@ -73,16 +74,16 @@ class MemAuthStore {
|
|
|
73
74
|
async getToken(query, opts) {
|
|
74
75
|
const normalized = {
|
|
75
76
|
...query,
|
|
76
|
-
userId:
|
|
77
|
-
ruid:
|
|
77
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
78
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
78
79
|
};
|
|
79
80
|
return this.adapter.getToken(normalized, opts);
|
|
80
81
|
}
|
|
81
82
|
async deleteToken(query) {
|
|
82
83
|
const normalized = {
|
|
83
84
|
...query,
|
|
84
|
-
userId:
|
|
85
|
-
ruid:
|
|
85
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
86
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
86
87
|
};
|
|
87
88
|
return this.adapter.deleteToken(normalized);
|
|
88
89
|
}
|
|
@@ -8,6 +8,7 @@ const sequelize_utils_js_1 = require("../sequelize-utils.js");
|
|
|
8
8
|
const sequelize_js_3 = require("../token/sequelize.js");
|
|
9
9
|
const sequelize_js_4 = require("../user/sequelize.js");
|
|
10
10
|
const compat_auth_storage_js_1 = require("./compat-auth-storage.js");
|
|
11
|
+
const user_id_js_1 = require("./user-id.js");
|
|
11
12
|
function resolveTablePrefix(...prefixes) {
|
|
12
13
|
for (const prefix of prefixes) {
|
|
13
14
|
const normalized = (0, sequelize_utils_js_1.normalizeTablePrefix)(prefix);
|
|
@@ -106,6 +107,7 @@ class SqlAuthStore {
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
finally {
|
|
110
|
+
// Prevent double-close errors when the same Sequelize instance is shared with other code.
|
|
109
111
|
this.sequelize.close = async () => { };
|
|
110
112
|
}
|
|
111
113
|
}
|
|
@@ -130,16 +132,16 @@ class SqlAuthStore {
|
|
|
130
132
|
async getToken(query, opts) {
|
|
131
133
|
const normalized = {
|
|
132
134
|
...query,
|
|
133
|
-
userId:
|
|
134
|
-
ruid:
|
|
135
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
136
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
135
137
|
};
|
|
136
138
|
return this.adapter.getToken(normalized, opts);
|
|
137
139
|
}
|
|
138
140
|
async deleteToken(query) {
|
|
139
141
|
const normalized = {
|
|
140
142
|
...query,
|
|
141
|
-
userId:
|
|
142
|
-
ruid:
|
|
143
|
+
userId: (0, user_id_js_1.toOptionalStringId)(query.userId),
|
|
144
|
+
ruid: (0, user_id_js_1.toOptionalStringId)(query.ruid)
|
|
143
145
|
};
|
|
144
146
|
return this.adapter.deleteToken(normalized);
|
|
145
147
|
}
|
|
@@ -2,3 +2,4 @@ import type { AuthIdentifier } from './types.js';
|
|
|
2
2
|
export declare function normalizeComparableUserId(identifier: AuthIdentifier): string;
|
|
3
3
|
export declare function normalizeNumericUserId(identifier: AuthIdentifier): number;
|
|
4
4
|
export declare function normalizeStringUserId(identifier: AuthIdentifier): string;
|
|
5
|
+
export declare function toOptionalStringId(value: AuthIdentifier | undefined | null): string | undefined;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.normalizeComparableUserId = normalizeComparableUserId;
|
|
4
4
|
exports.normalizeNumericUserId = normalizeNumericUserId;
|
|
5
5
|
exports.normalizeStringUserId = normalizeStringUserId;
|
|
6
|
+
exports.toOptionalStringId = toOptionalStringId;
|
|
6
7
|
function normalizeComparableUserId(identifier) {
|
|
7
8
|
if (typeof identifier === 'number' && Number.isFinite(identifier)) {
|
|
8
9
|
return String(identifier);
|
|
@@ -29,3 +30,9 @@ function normalizeNumericUserId(identifier) {
|
|
|
29
30
|
function normalizeStringUserId(identifier) {
|
|
30
31
|
return normalizeComparableUserId(identifier);
|
|
31
32
|
}
|
|
33
|
+
function toOptionalStringId(value) {
|
|
34
|
+
if (value === undefined || value === null) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return String(value);
|
|
38
|
+
}
|
|
@@ -31,7 +31,16 @@ function buildAuthCookieOptions(config, req) {
|
|
|
31
31
|
const forwardedProto = firstHeaderValue(req.headers['x-forwarded-proto']).split(',')[0].trim().toLowerCase();
|
|
32
32
|
const isHttps = forwardedProto === 'https' || req.protocol === 'https';
|
|
33
33
|
const origin = firstHeaderValue(req.headers.origin ?? req.headers.referer);
|
|
34
|
-
|
|
34
|
+
let secure;
|
|
35
|
+
if (config.cookieSecure === true) {
|
|
36
|
+
secure = true;
|
|
37
|
+
}
|
|
38
|
+
else if (config.cookieSecure === false) {
|
|
39
|
+
secure = false;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
secure = isHttps;
|
|
43
|
+
}
|
|
35
44
|
let sameSite = config.cookieSameSite ?? 'lax';
|
|
36
45
|
if (sameSite !== 'lax' && sameSite !== 'strict' && sameSite !== 'none') {
|
|
37
46
|
sameSite = 'lax';
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { OAuthStore, type AuthCode, type OAuthClient } from './base.js';
|
|
2
2
|
export interface MemoryOAuthStoreOptions {
|
|
3
3
|
bcryptRounds?: number;
|
|
4
|
+
maxClients?: number;
|
|
5
|
+
maxAuthCodes?: number;
|
|
4
6
|
}
|
|
5
7
|
export declare class MemoryOAuthStore extends OAuthStore {
|
|
6
8
|
private readonly clients;
|
|
7
9
|
private readonly codes;
|
|
8
10
|
private readonly bcryptRounds;
|
|
11
|
+
private readonly maxClients?;
|
|
12
|
+
private readonly maxAuthCodes?;
|
|
9
13
|
constructor(options?: MemoryOAuthStoreOptions);
|
|
10
14
|
getClient(clientId: string): Promise<OAuthClient | null>;
|
|
11
15
|
createClient(input: OAuthClient): Promise<OAuthClient>;
|
|
@@ -13,4 +17,6 @@ export declare class MemoryOAuthStore extends OAuthStore {
|
|
|
13
17
|
createAuthCode(code: AuthCode): Promise<void>;
|
|
14
18
|
consumeAuthCode(code: string): Promise<AuthCode | null>;
|
|
15
19
|
close(): Promise<void>;
|
|
20
|
+
private enforceClientCapacity;
|
|
21
|
+
private enforceCodeCapacity;
|
|
16
22
|
}
|
package/dist/cjs/oauth/memory.js
CHANGED
|
@@ -13,8 +13,7 @@ function cloneClient(client) {
|
|
|
13
13
|
}
|
|
14
14
|
return {
|
|
15
15
|
clientId: client.clientId,
|
|
16
|
-
|
|
17
|
-
clientSecret: client.clientSecret ? '__stored__' : undefined,
|
|
16
|
+
hasSecret: Boolean(client.clientSecret),
|
|
18
17
|
name: client.name,
|
|
19
18
|
redirectUris: [...client.redirectUris],
|
|
20
19
|
scope: client.scope ? [...client.scope] : undefined,
|
|
@@ -25,18 +24,28 @@ function cloneClient(client) {
|
|
|
25
24
|
function cloneCode(code) {
|
|
26
25
|
return {
|
|
27
26
|
...code,
|
|
27
|
+
userId: (0, user_id_js_1.normalizeComparableUserId)(code.userId),
|
|
28
28
|
scope: code.scope ? [...code.scope] : undefined,
|
|
29
29
|
expiresAt: new Date(code.expiresAt),
|
|
30
30
|
metadata: code.metadata ? { ...code.metadata } : undefined
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
-
const normalizeUserId = user_id_js_1.normalizeNumericUserId;
|
|
34
33
|
class MemoryOAuthStore extends base_js_1.OAuthStore {
|
|
35
34
|
constructor(options = {}) {
|
|
36
35
|
super();
|
|
37
36
|
this.clients = new Map();
|
|
38
37
|
this.codes = new Map();
|
|
39
38
|
this.bcryptRounds = options.bcryptRounds ?? 12;
|
|
39
|
+
this.maxClients =
|
|
40
|
+
typeof options.maxClients === 'number' && Number.isFinite(options.maxClients) && options.maxClients > 0
|
|
41
|
+
? Math.floor(options.maxClients)
|
|
42
|
+
: undefined;
|
|
43
|
+
this.maxAuthCodes =
|
|
44
|
+
typeof options.maxAuthCodes === 'number' &&
|
|
45
|
+
Number.isFinite(options.maxAuthCodes) &&
|
|
46
|
+
options.maxAuthCodes > 0
|
|
47
|
+
? Math.floor(options.maxAuthCodes)
|
|
48
|
+
: undefined;
|
|
40
49
|
}
|
|
41
50
|
async getClient(clientId) {
|
|
42
51
|
return cloneClient(this.clients.get(clientId));
|
|
@@ -53,6 +62,7 @@ class MemoryOAuthStore extends base_js_1.OAuthStore {
|
|
|
53
62
|
firstParty: input.firstParty
|
|
54
63
|
};
|
|
55
64
|
this.clients.set(stored.clientId, stored);
|
|
65
|
+
this.enforceClientCapacity();
|
|
56
66
|
return cloneClient(stored);
|
|
57
67
|
}
|
|
58
68
|
async verifyClientSecret(clientId, secret) {
|
|
@@ -71,23 +81,52 @@ class MemoryOAuthStore extends base_js_1.OAuthStore {
|
|
|
71
81
|
async createAuthCode(code) {
|
|
72
82
|
const record = {
|
|
73
83
|
...code,
|
|
74
|
-
userId:
|
|
84
|
+
userId: (0, user_id_js_1.normalizeComparableUserId)(code.userId),
|
|
75
85
|
scope: code.scope ? [...code.scope] : undefined,
|
|
76
86
|
expiresAt: code.expiresAt,
|
|
77
87
|
metadata: code.metadata ? { ...code.metadata } : undefined
|
|
78
88
|
};
|
|
79
89
|
this.codes.set(record.code, record);
|
|
90
|
+
this.enforceCodeCapacity();
|
|
80
91
|
}
|
|
81
92
|
async consumeAuthCode(code) {
|
|
82
93
|
const record = this.codes.get(code);
|
|
83
94
|
if (!record) {
|
|
84
95
|
return null;
|
|
85
96
|
}
|
|
97
|
+
if (record.expiresAt.getTime() <= Date.now()) {
|
|
98
|
+
this.codes.delete(code);
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
86
101
|
this.codes.delete(code);
|
|
87
102
|
return cloneCode(record);
|
|
88
103
|
}
|
|
89
104
|
async close() {
|
|
90
105
|
return;
|
|
91
106
|
}
|
|
107
|
+
enforceClientCapacity() {
|
|
108
|
+
if (!this.maxClients) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
while (this.clients.size > this.maxClients) {
|
|
112
|
+
const oldest = this.clients.keys().next().value;
|
|
113
|
+
if (!oldest) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.clients.delete(oldest);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
enforceCodeCapacity() {
|
|
120
|
+
if (!this.maxAuthCodes) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
while (this.codes.size > this.maxAuthCodes) {
|
|
124
|
+
const oldest = this.codes.keys().next().value;
|
|
125
|
+
if (!oldest) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.codes.delete(oldest);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
92
131
|
}
|
|
93
132
|
exports.MemoryOAuthStore = MemoryOAuthStore;
|
package/dist/cjs/oauth/models.js
CHANGED
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OAuthCodeModel = exports.OAuthClientModel = void 0;
|
|
3
|
+
exports.OAuthCodeModel = exports.OAuthClientModel = exports.tableOptions = exports.integerIdType = void 0;
|
|
4
4
|
exports.initOAuthClientModel = initOAuthClientModel;
|
|
5
5
|
exports.initOAuthCodeModel = initOAuthCodeModel;
|
|
6
6
|
const sequelize_1 = require("sequelize");
|
|
7
7
|
const sequelize_utils_js_1 = require("../sequelize-utils.js");
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
function tableOptions(sequelize, tableName, tablePrefix, extra) {
|
|
12
|
-
const opts = { sequelize, tableName: (0, sequelize_utils_js_1.applyTablePrefix)(tablePrefix, tableName) };
|
|
13
|
-
if (extra) {
|
|
14
|
-
Object.assign(opts, extra);
|
|
15
|
-
}
|
|
16
|
-
if (sequelize_utils_js_1.DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
|
|
17
|
-
opts.charset = 'utf8mb4';
|
|
18
|
-
opts.collate = 'utf8mb4_unicode_ci';
|
|
19
|
-
}
|
|
20
|
-
return opts;
|
|
21
|
-
}
|
|
8
|
+
var sequelize_utils_js_2 = require("../sequelize-utils.js");
|
|
9
|
+
Object.defineProperty(exports, "integerIdType", { enumerable: true, get: function () { return sequelize_utils_js_2.integerIdType; } });
|
|
10
|
+
Object.defineProperty(exports, "tableOptions", { enumerable: true, get: function () { return sequelize_utils_js_2.tableOptions; } });
|
|
22
11
|
class OAuthClientModel extends sequelize_1.Model {
|
|
23
12
|
}
|
|
24
13
|
exports.OAuthClientModel = OAuthClientModel;
|
|
@@ -32,7 +21,7 @@ function initOAuthClientModel(sequelize, options = {}) {
|
|
|
32
21
|
metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null },
|
|
33
22
|
first_party: { type: sequelize_1.DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
|
|
34
23
|
}, {
|
|
35
|
-
...tableOptions(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false })
|
|
24
|
+
...(0, sequelize_utils_js_1.tableOptions)(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false })
|
|
36
25
|
});
|
|
37
26
|
return OAuthClientModel;
|
|
38
27
|
}
|
|
@@ -40,7 +29,7 @@ class OAuthCodeModel extends sequelize_1.Model {
|
|
|
40
29
|
}
|
|
41
30
|
exports.OAuthCodeModel = OAuthCodeModel;
|
|
42
31
|
function initOAuthCodeModel(sequelize, options = {}) {
|
|
43
|
-
const idType = integerIdType(sequelize);
|
|
32
|
+
const idType = (0, sequelize_utils_js_1.integerIdType)(sequelize);
|
|
44
33
|
OAuthCodeModel.init({
|
|
45
34
|
code: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
|
|
46
35
|
client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false },
|
|
@@ -52,7 +41,7 @@ function initOAuthCodeModel(sequelize, options = {}) {
|
|
|
52
41
|
expires: { type: sequelize_1.DataTypes.DATE, allowNull: false },
|
|
53
42
|
metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null }
|
|
54
43
|
}, {
|
|
55
|
-
...tableOptions(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false })
|
|
44
|
+
...(0, sequelize_utils_js_1.tableOptions)(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false })
|
|
56
45
|
});
|
|
57
46
|
return OAuthCodeModel;
|
|
58
47
|
}
|
|
@@ -1,66 +1,19 @@
|
|
|
1
|
-
import { Model, type Optional, type Sequelize } from 'sequelize';
|
|
2
1
|
import { OAuthStore, type AuthCode, type OAuthClient } from './base.js';
|
|
3
|
-
|
|
4
|
-
client_id: string;
|
|
5
|
-
client_secret: string;
|
|
6
|
-
name: string | null;
|
|
7
|
-
redirect_uris: string;
|
|
8
|
-
scope: string;
|
|
9
|
-
metadata: string | null;
|
|
10
|
-
first_party: boolean;
|
|
11
|
-
}
|
|
12
|
-
export type OAuthClientCreationAttributes = Optional<OAuthClientAttributes, 'client_secret' | 'name' | 'scope' | 'metadata' | 'first_party'>;
|
|
13
|
-
export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuthClientCreationAttributes> implements OAuthClientAttributes {
|
|
14
|
-
client_id: string;
|
|
15
|
-
client_secret: string;
|
|
16
|
-
name: string | null;
|
|
17
|
-
redirect_uris: string;
|
|
18
|
-
scope: string;
|
|
19
|
-
metadata: string | null;
|
|
20
|
-
first_party: boolean;
|
|
21
|
-
}
|
|
22
|
-
export declare function initOAuthClientModel(sequelize: Sequelize, options?: {
|
|
23
|
-
tablePrefix?: string;
|
|
24
|
-
}): typeof OAuthClientModel;
|
|
25
|
-
export interface OAuthCodeAttributes {
|
|
26
|
-
code: string;
|
|
27
|
-
client_id: string;
|
|
28
|
-
user_id: number;
|
|
29
|
-
redirect_uri: string;
|
|
30
|
-
scope: string;
|
|
31
|
-
code_challenge: string | null;
|
|
32
|
-
code_challenge_method: 'plain' | 'S256' | null;
|
|
33
|
-
expires: Date;
|
|
34
|
-
metadata: string | null;
|
|
35
|
-
}
|
|
36
|
-
export type OAuthCodeCreationAttributes = Optional<OAuthCodeAttributes, 'code_challenge' | 'code_challenge_method' | 'metadata'>;
|
|
37
|
-
export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCodeCreationAttributes> implements OAuthCodeAttributes {
|
|
38
|
-
code: string;
|
|
39
|
-
client_id: string;
|
|
40
|
-
user_id: number;
|
|
41
|
-
redirect_uri: string;
|
|
42
|
-
scope: string;
|
|
43
|
-
code_challenge: string | null;
|
|
44
|
-
code_challenge_method: 'plain' | 'S256' | null;
|
|
45
|
-
expires: Date;
|
|
46
|
-
metadata: string | null;
|
|
47
|
-
}
|
|
48
|
-
export declare function initOAuthCodeModel(sequelize: Sequelize, options?: {
|
|
49
|
-
tablePrefix?: string;
|
|
50
|
-
}): typeof OAuthCodeModel;
|
|
2
|
+
import { OAuthClientModel, OAuthCodeModel } from './models.js';
|
|
51
3
|
export interface SequelizeOAuthStoreOptions {
|
|
52
|
-
sequelize: Sequelize;
|
|
4
|
+
sequelize: import('sequelize').Sequelize;
|
|
53
5
|
tablePrefix?: string;
|
|
54
6
|
clientModel?: typeof OAuthClientModel;
|
|
55
7
|
codeModel?: typeof OAuthCodeModel;
|
|
56
|
-
clientModelFactory?: (sequelize: Sequelize, options?: {
|
|
8
|
+
clientModelFactory?: (sequelize: import('sequelize').Sequelize, options?: {
|
|
57
9
|
tablePrefix?: string;
|
|
58
10
|
}) => typeof OAuthClientModel;
|
|
59
|
-
codeModelFactory?: (sequelize: Sequelize, options?: {
|
|
11
|
+
codeModelFactory?: (sequelize: import('sequelize').Sequelize, options?: {
|
|
60
12
|
tablePrefix?: string;
|
|
61
13
|
}) => typeof OAuthCodeModel;
|
|
62
14
|
bcryptRounds?: number;
|
|
63
15
|
}
|
|
16
|
+
export { OAuthClientModel, OAuthCodeModel, initOAuthClientModel, initOAuthCodeModel, type OAuthClientAttributes, type OAuthClientCreationAttributes, type OAuthCodeAttributes, type OAuthCodeCreationAttributes } from './models.js';
|
|
64
17
|
export declare class SequelizeOAuthStore extends OAuthStore {
|
|
65
18
|
private readonly clients;
|
|
66
19
|
private readonly codes;
|