@dotpress/auth 2.0.0-alpha.4 → 2.0.0-alpha.5
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 +1 -1
- package/dist/index.d.ts +93 -165
- package/dist/index.js +271 -160
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4,6 +4,10 @@ declareContextKey("user", "authPlugin");
|
|
|
4
4
|
declareContextKey("auth", "authPlugin");
|
|
5
5
|
|
|
6
6
|
// src/auth.ts
|
|
7
|
+
import {
|
|
8
|
+
createMemoryRateLimitStore,
|
|
9
|
+
parseDuration
|
|
10
|
+
} from "@dotpress/core";
|
|
7
11
|
import bcrypt from "bcryptjs";
|
|
8
12
|
import {
|
|
9
13
|
CompactSign,
|
|
@@ -47,43 +51,6 @@ var isCookieAuthenticationAllowed = async (req, isTrustedOrigin) => {
|
|
|
47
51
|
return originHost !== void 0 && originHost === (req.header("host") ?? hostOf(req.url));
|
|
48
52
|
};
|
|
49
53
|
|
|
50
|
-
// src/rate-limit.ts
|
|
51
|
-
var memoryRateLimitStore = (options = {}) => {
|
|
52
|
-
const maxKeys = options.maxKeys ?? 1e4;
|
|
53
|
-
if (!Number.isInteger(maxKeys) || maxKeys < 1) {
|
|
54
|
-
throw new RangeError("Invalid maxKeys: expected a positive integer.");
|
|
55
|
-
}
|
|
56
|
-
const counters = /* @__PURE__ */ new Map();
|
|
57
|
-
const makeRoom = (now) => {
|
|
58
|
-
if (counters.size < maxKeys) return;
|
|
59
|
-
for (const [key, state] of counters) {
|
|
60
|
-
if (state.resetAt.getTime() <= now) counters.delete(key);
|
|
61
|
-
}
|
|
62
|
-
for (const key of counters.keys()) {
|
|
63
|
-
if (counters.size < maxKeys) break;
|
|
64
|
-
counters.delete(key);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
return {
|
|
68
|
-
async increment(key, windowSeconds) {
|
|
69
|
-
const now = Date.now();
|
|
70
|
-
const current = counters.get(key);
|
|
71
|
-
if (current && current.resetAt.getTime() > now) {
|
|
72
|
-
current.count += 1;
|
|
73
|
-
return { ...current };
|
|
74
|
-
}
|
|
75
|
-
counters.delete(key);
|
|
76
|
-
makeRoom(now);
|
|
77
|
-
const state = { count: 1, resetAt: new Date(now + windowSeconds * 1e3) };
|
|
78
|
-
counters.set(key, state);
|
|
79
|
-
return { ...state };
|
|
80
|
-
},
|
|
81
|
-
async reset(key) {
|
|
82
|
-
counters.delete(key);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
};
|
|
86
|
-
|
|
87
54
|
// src/auth.ts
|
|
88
55
|
var ALGORITHM = "RS256";
|
|
89
56
|
var ACCESS_TOKEN_TYPE = "at+jwt";
|
|
@@ -96,6 +63,67 @@ var AuthError = class extends Error {
|
|
|
96
63
|
}
|
|
97
64
|
code;
|
|
98
65
|
};
|
|
66
|
+
var internals = /* @__PURE__ */ new WeakMap();
|
|
67
|
+
var internalsOf = (auth) => typeof auth === "object" && auth !== null ? internals.get(auth) : void 0;
|
|
68
|
+
var wholeSeconds = (label, value, min) => {
|
|
69
|
+
const result = Math.round(
|
|
70
|
+
parseDuration(value, `createAuth(): ${label}`) / 1e3
|
|
71
|
+
);
|
|
72
|
+
if (result < min) {
|
|
73
|
+
throw new RangeError(
|
|
74
|
+
`createAuth(): ${label} must be at least ${min} second${min === 1 ? "" : "s"}.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
var assertKnown = (options, known, prefix = "") => {
|
|
80
|
+
if (options === void 0) return;
|
|
81
|
+
if (typeof options !== "object" || options === null) {
|
|
82
|
+
throw new TypeError(
|
|
83
|
+
`createAuth(): ${prefix ? prefix.slice(0, -1) : "options"} must be an object.`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
for (const key of Object.keys(options)) {
|
|
87
|
+
if (!known.includes(key)) {
|
|
88
|
+
throw new TypeError(`createAuth(): unknown option "${prefix}${key}".`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var OPTIONS = [
|
|
93
|
+
"keys",
|
|
94
|
+
"dataProvider",
|
|
95
|
+
"accessTokenTTL",
|
|
96
|
+
"session",
|
|
97
|
+
"issuer",
|
|
98
|
+
"audience",
|
|
99
|
+
"cookies",
|
|
100
|
+
"clients",
|
|
101
|
+
"onLoginFailed",
|
|
102
|
+
"onLoginSuccess",
|
|
103
|
+
"loginThrottle",
|
|
104
|
+
"beforeLogin",
|
|
105
|
+
"passwordHashRounds",
|
|
106
|
+
"usernameClaim",
|
|
107
|
+
"refreshTokenReuseInterval",
|
|
108
|
+
"onRefreshTokenReuse",
|
|
109
|
+
"getUserRoles"
|
|
110
|
+
];
|
|
111
|
+
var KEY_OPTIONS = [
|
|
112
|
+
"privateKey",
|
|
113
|
+
"publicKey",
|
|
114
|
+
"kid",
|
|
115
|
+
"additionalPublicKeys"
|
|
116
|
+
];
|
|
117
|
+
var SESSION_OPTIONS = [
|
|
118
|
+
"idleTimeout",
|
|
119
|
+
"absoluteTimeout"
|
|
120
|
+
];
|
|
121
|
+
var CLIENT_OPTIONS = [
|
|
122
|
+
"allowed",
|
|
123
|
+
"required",
|
|
124
|
+
"isUserRoleAllowed",
|
|
125
|
+
"sessions"
|
|
126
|
+
];
|
|
99
127
|
var DEFAULT_COOKIE_SETTINGS = {
|
|
100
128
|
enabled: false,
|
|
101
129
|
httpOnly: true,
|
|
@@ -117,15 +145,15 @@ var defaultUserRoles = (user) => {
|
|
|
117
145
|
}
|
|
118
146
|
return typeof role === "string" ? [role] : [];
|
|
119
147
|
};
|
|
120
|
-
var
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
148
|
+
var resolveSession = (session, name) => {
|
|
149
|
+
assertKnown(session, SESSION_OPTIONS, `${name}.`);
|
|
150
|
+
const resolved = {};
|
|
151
|
+
for (const key of SESSION_OPTIONS) {
|
|
152
|
+
if (session === void 0 || !(key in session)) continue;
|
|
153
|
+
const value = session[key];
|
|
154
|
+
resolved[key] = value === void 0 ? void 0 : wholeSeconds(`${name}.${key}`, value, 1);
|
|
128
155
|
}
|
|
156
|
+
return resolved;
|
|
129
157
|
};
|
|
130
158
|
var DEFAULT_LOGIN_LIMITS = {
|
|
131
159
|
usernameAndIp: { max: 5, window: 60 },
|
|
@@ -133,21 +161,39 @@ var DEFAULT_LOGIN_LIMITS = {
|
|
|
133
161
|
};
|
|
134
162
|
var resolveLoginThrottle = (input) => {
|
|
135
163
|
if (input === false) return void 0;
|
|
164
|
+
assertKnown(input, ["store", "limits"], "loginThrottle.");
|
|
165
|
+
assertKnown(
|
|
166
|
+
input?.limits,
|
|
167
|
+
["usernameAndIp", "username"],
|
|
168
|
+
"loginThrottle.limits."
|
|
169
|
+
);
|
|
136
170
|
const limit = (name) => {
|
|
137
171
|
const value = input?.limits?.[name];
|
|
138
172
|
if (value === false) return void 0;
|
|
139
173
|
const resolved = value ?? DEFAULT_LOGIN_LIMITS[name];
|
|
140
|
-
|
|
174
|
+
assertKnown(
|
|
175
|
+
resolved,
|
|
176
|
+
["max", "window"],
|
|
177
|
+
`loginThrottle.limits.${name}.`
|
|
178
|
+
);
|
|
179
|
+
if (!Number.isInteger(resolved.max) || resolved.max < 1) {
|
|
141
180
|
throw new RangeError(
|
|
142
|
-
`
|
|
181
|
+
`createAuth(): loginThrottle.limits.${name}.max must be a positive integer.`
|
|
143
182
|
);
|
|
144
183
|
}
|
|
145
|
-
return {
|
|
184
|
+
return {
|
|
185
|
+
max: resolved.max,
|
|
186
|
+
window: wholeSeconds(
|
|
187
|
+
`loginThrottle.limits.${name}.window`,
|
|
188
|
+
resolved.window,
|
|
189
|
+
1
|
|
190
|
+
)
|
|
191
|
+
};
|
|
146
192
|
};
|
|
147
|
-
const store = input?.store ??
|
|
193
|
+
const store = input?.store ?? createMemoryRateLimitStore();
|
|
148
194
|
if (typeof store.increment !== "function" || typeof store.reset !== "function") {
|
|
149
195
|
throw new TypeError(
|
|
150
|
-
"
|
|
196
|
+
"createAuth(): loginThrottle.store must have increment() and reset() methods."
|
|
151
197
|
);
|
|
152
198
|
}
|
|
153
199
|
return {
|
|
@@ -159,37 +205,62 @@ var resolveLoginThrottle = (input) => {
|
|
|
159
205
|
};
|
|
160
206
|
};
|
|
161
207
|
var resolveSettings = (settings) => {
|
|
162
|
-
if (
|
|
208
|
+
if (typeof settings !== "object" || settings === null) {
|
|
209
|
+
throw new TypeError("createAuth(): expected an object of options.");
|
|
210
|
+
}
|
|
211
|
+
if ("refreshTokenTTLInDays" in settings) {
|
|
163
212
|
throw new TypeError(
|
|
164
|
-
"createAuth()
|
|
213
|
+
"createAuth(): refreshTokenTTLInDays is replaced by session.idleTimeout."
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (typeof settings.keys === "object" && settings.keys !== null && "secretKey" in settings.keys) {
|
|
217
|
+
throw new TypeError(
|
|
218
|
+
"createAuth(): keys.secretKey is renamed keys.privateKey."
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
assertKnown(settings, OPTIONS);
|
|
222
|
+
assertKnown(settings.keys, KEY_OPTIONS, "keys.");
|
|
223
|
+
for (const [index, entry] of (settings.keys?.additionalPublicKeys ?? []).entries()) {
|
|
224
|
+
if (typeof entry !== "string") {
|
|
225
|
+
assertKnown(
|
|
226
|
+
entry,
|
|
227
|
+
["publicKey", "kid"],
|
|
228
|
+
`keys.additionalPublicKeys[${index}].`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
assertKnown(settings.clients, CLIENT_OPTIONS, "clients.");
|
|
233
|
+
assertKnown(
|
|
234
|
+
settings.cookies,
|
|
235
|
+
Object.keys(DEFAULT_COOKIE_SETTINGS),
|
|
236
|
+
"cookies."
|
|
237
|
+
);
|
|
238
|
+
if (!settings?.keys?.privateKey || !settings.keys.publicKey) {
|
|
239
|
+
throw new TypeError(
|
|
240
|
+
"createAuth(): keys.privateKey and keys.publicKey are required."
|
|
165
241
|
);
|
|
166
242
|
}
|
|
167
243
|
if (!settings.dataProvider) {
|
|
168
|
-
throw new TypeError("createAuth()
|
|
244
|
+
throw new TypeError("createAuth(): dataProvider is required.");
|
|
169
245
|
}
|
|
170
246
|
if (typeof settings.issuer !== "string" || settings.issuer === "") {
|
|
171
247
|
throw new TypeError(
|
|
172
|
-
"createAuth()
|
|
248
|
+
"createAuth(): issuer is required (the iss claim), such as the URL of the API."
|
|
173
249
|
);
|
|
174
250
|
}
|
|
175
251
|
if (settings.audience !== void 0 && settings.audience === "") {
|
|
176
|
-
throw new TypeError("
|
|
252
|
+
throw new TypeError("createAuth(): audience must be a non-empty string.");
|
|
177
253
|
}
|
|
178
254
|
const clients = settings.clients;
|
|
179
255
|
if (clients && (!Array.isArray(clients.allowed) || clients.allowed.length === 0 || clients.allowed.some((id) => typeof id !== "string" || id === ""))) {
|
|
180
256
|
throw new TypeError(
|
|
181
|
-
"
|
|
257
|
+
"createAuth(): clients.allowed must be a non-empty list of client identifiers."
|
|
182
258
|
);
|
|
183
259
|
}
|
|
184
260
|
const rounds = settings.passwordHashRounds;
|
|
185
261
|
if (rounds !== void 0 && (!Number.isInteger(rounds) || rounds < 4 || rounds > 31)) {
|
|
186
262
|
throw new RangeError(
|
|
187
|
-
"
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
if ("refreshTokenTTLInDays" in settings) {
|
|
191
|
-
throw new TypeError(
|
|
192
|
-
"refreshTokenTTLInDays is replaced by session.idleTimeout, in seconds."
|
|
263
|
+
"createAuth(): passwordHashRounds must be an integer between 4 and 31."
|
|
193
264
|
);
|
|
194
265
|
}
|
|
195
266
|
for (const origin of settings.cookies?.trustedOrigins ?? []) {
|
|
@@ -200,33 +271,33 @@ var resolveSettings = (settings) => {
|
|
|
200
271
|
}
|
|
201
272
|
if (!valid) {
|
|
202
273
|
throw new TypeError(
|
|
203
|
-
`
|
|
274
|
+
`createAuth(): cookies.trustedOrigins: "${origin}" is not an origin (expected a scheme and a host, such as https://app.example.com, without path or trailing slash).`
|
|
204
275
|
);
|
|
205
276
|
}
|
|
206
277
|
}
|
|
207
|
-
|
|
208
|
-
|
|
278
|
+
const session = resolveSession(settings.session, "session");
|
|
279
|
+
const clientSessions = {};
|
|
280
|
+
for (const [clientId, override] of Object.entries(clients?.sessions ?? {})) {
|
|
209
281
|
if (!clients?.allowed.includes(clientId)) {
|
|
210
282
|
throw new TypeError(
|
|
211
|
-
`
|
|
283
|
+
`createAuth(): clients.sessions: "${clientId}" is not in clients.allowed.`
|
|
212
284
|
);
|
|
213
285
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (reuseInterval !== void 0 && (!Number.isFinite(reuseInterval) || reuseInterval < 0)) {
|
|
218
|
-
throw new RangeError(
|
|
219
|
-
"Invalid refreshTokenReuseInterval: expected a non-negative number of seconds."
|
|
286
|
+
clientSessions[clientId] = resolveSession(
|
|
287
|
+
override,
|
|
288
|
+
`clients.sessions.${clientId}`
|
|
220
289
|
);
|
|
221
290
|
}
|
|
291
|
+
const optional = (label, fallback, min) => settings[label] === void 0 ? fallback : wholeSeconds(label, settings[label], min);
|
|
222
292
|
return {
|
|
223
293
|
keys: settings.keys,
|
|
224
294
|
dataProvider: settings.dataProvider,
|
|
225
|
-
accessTokenTTL:
|
|
295
|
+
accessTokenTTL: optional("accessTokenTTL", 900, 1),
|
|
226
296
|
session: {
|
|
227
|
-
idleTimeout:
|
|
228
|
-
absoluteTimeout:
|
|
297
|
+
idleTimeout: session.idleTimeout ?? DEFAULT_IDLE_TIMEOUT,
|
|
298
|
+
absoluteTimeout: session.absoluteTimeout
|
|
229
299
|
},
|
|
300
|
+
clientSessions,
|
|
230
301
|
issuer: settings.issuer,
|
|
231
302
|
audience: settings.audience ?? settings.issuer,
|
|
232
303
|
cookies: resolveCookies(settings.cookies),
|
|
@@ -237,7 +308,7 @@ var resolveSettings = (settings) => {
|
|
|
237
308
|
beforeLogin: settings.beforeLogin,
|
|
238
309
|
passwordHashRounds: settings.passwordHashRounds ?? 12,
|
|
239
310
|
usernameClaim: settings.usernameClaim ?? false,
|
|
240
|
-
refreshTokenReuseInterval:
|
|
311
|
+
refreshTokenReuseInterval: optional("refreshTokenReuseInterval", 10, 0),
|
|
241
312
|
onRefreshTokenReuse: settings.onRefreshTokenReuse,
|
|
242
313
|
getUserRoles: settings.getUserRoles ?? defaultUserRoles
|
|
243
314
|
};
|
|
@@ -252,12 +323,12 @@ var cookieName = (setting, name, base, attributes) => {
|
|
|
252
323
|
const lowerName = name.toLowerCase();
|
|
253
324
|
if (lowerName.startsWith("__host-") && !hostAllowed) {
|
|
254
325
|
throw new TypeError(
|
|
255
|
-
`
|
|
326
|
+
`createAuth(): cookies.${setting}: a __Host- cookie must be secure, without domain, and with path "/".`
|
|
256
327
|
);
|
|
257
328
|
}
|
|
258
329
|
if (lowerName.startsWith("__secure-") && !secure) {
|
|
259
330
|
throw new TypeError(
|
|
260
|
-
`
|
|
331
|
+
`createAuth(): cookies.${setting}: a __Secure- cookie must be secure.`
|
|
261
332
|
);
|
|
262
333
|
}
|
|
263
334
|
return name;
|
|
@@ -269,9 +340,7 @@ var resolveCookies = (settings = {}) => {
|
|
|
269
340
|
}
|
|
270
341
|
for (const key of ["path", "refreshTokenPath"]) {
|
|
271
342
|
if (!merged[key].startsWith("/")) {
|
|
272
|
-
throw new TypeError(
|
|
273
|
-
`Invalid cookies.${key}: expected a path starting with "/".`
|
|
274
|
-
);
|
|
343
|
+
throw new TypeError(`createAuth(): cookies.${key} must start with "/".`);
|
|
275
344
|
}
|
|
276
345
|
}
|
|
277
346
|
const { secure, domain } = merged;
|
|
@@ -298,14 +367,14 @@ var loadKey = async (kind, load) => {
|
|
|
298
367
|
return await load();
|
|
299
368
|
} catch (error) {
|
|
300
369
|
throw new TypeError(
|
|
301
|
-
`
|
|
370
|
+
`createAuth(): keys.${kind} must be an RSA PEM key (${error.message}).`,
|
|
302
371
|
{ cause: error }
|
|
303
372
|
);
|
|
304
373
|
}
|
|
305
374
|
};
|
|
306
375
|
var loadPublicKey = async (kind, pem, kid) => {
|
|
307
376
|
if (pem.includes("BEGIN RSA PUBLIC KEY")) {
|
|
308
|
-
throw new TypeError(`
|
|
377
|
+
throw new TypeError(`createAuth(): keys.${kind}: ${PKCS1_PUBLIC_KEY_ERROR}`);
|
|
309
378
|
}
|
|
310
379
|
const key = await loadKey(
|
|
311
380
|
kind,
|
|
@@ -316,27 +385,27 @@ var loadPublicKey = async (kind, pem, kid) => {
|
|
|
316
385
|
return { kid: kid ?? await calculateJwkThumbprint(jwk), key, jwk };
|
|
317
386
|
};
|
|
318
387
|
var importKeys = async ({
|
|
319
|
-
|
|
388
|
+
privateKey,
|
|
320
389
|
publicKey,
|
|
321
390
|
kid,
|
|
322
391
|
additionalPublicKeys = []
|
|
323
392
|
}) => {
|
|
324
|
-
if (
|
|
393
|
+
if (privateKey.includes("BEGIN RSA PRIVATE KEY")) {
|
|
325
394
|
throw new TypeError(
|
|
326
|
-
'
|
|
395
|
+
'createAuth(): keys.privateKey: PKCS#1 keys ("BEGIN RSA PRIVATE KEY") are not supported, use PKCS#8 ("BEGIN PRIVATE KEY"). Convert it with: openssl pkcs8 -topk8 -nocrypt -in private.pem -out private.pkcs8.pem'
|
|
327
396
|
);
|
|
328
397
|
}
|
|
329
|
-
const
|
|
330
|
-
"
|
|
331
|
-
() => importPKCS8(
|
|
398
|
+
const signingKey = await loadKey(
|
|
399
|
+
"privateKey",
|
|
400
|
+
() => importPKCS8(privateKey, ALGORITHM)
|
|
332
401
|
);
|
|
333
402
|
const active = await loadPublicKey("publicKey", publicKey, kid);
|
|
334
403
|
try {
|
|
335
|
-
const probe = await new CompactSign(new Uint8Array([1])).setProtectedHeader({ alg: ALGORITHM }).sign(
|
|
404
|
+
const probe = await new CompactSign(new Uint8Array([1])).setProtectedHeader({ alg: ALGORITHM }).sign(signingKey);
|
|
336
405
|
await compactVerify(probe, active.key);
|
|
337
406
|
} catch (error) {
|
|
338
407
|
throw new TypeError(
|
|
339
|
-
"
|
|
408
|
+
"createAuth(): keys.publicKey does not match keys.privateKey.",
|
|
340
409
|
{ cause: error }
|
|
341
410
|
);
|
|
342
411
|
}
|
|
@@ -353,11 +422,11 @@ var importKeys = async ({
|
|
|
353
422
|
verificationKeys.push(loaded);
|
|
354
423
|
} else if (existing.jwk.n !== loaded.jwk.n || existing.jwk.e !== loaded.jwk.e) {
|
|
355
424
|
throw new TypeError(
|
|
356
|
-
`
|
|
425
|
+
`createAuth(): keys: kid "${loaded.kid}" is used by several keys.`
|
|
357
426
|
);
|
|
358
427
|
}
|
|
359
428
|
}
|
|
360
|
-
return {
|
|
429
|
+
return { signingKey, kid: active.kid, verificationKeys };
|
|
361
430
|
};
|
|
362
431
|
var verifyWithKeys = async (token, verificationKeys, verify) => {
|
|
363
432
|
const { kid } = decodeProtectedHeader(token);
|
|
@@ -379,14 +448,14 @@ var verifyWithKeys = async (token, verificationKeys, verify) => {
|
|
|
379
448
|
var BCRYPT_HASH = /^\$2[aby]\$\d{2}\$[./A-Za-z0-9]{53}$/;
|
|
380
449
|
var nowInSeconds = () => Math.floor(Date.now() / 1e3);
|
|
381
450
|
var toSession = (claims, roles) => {
|
|
382
|
-
if (typeof claims.sub !== "string" || claims.sub === "") {
|
|
451
|
+
if (typeof claims.sub !== "string" || claims.sub === "" || typeof claims.sid !== "string" || claims.sid === "") {
|
|
383
452
|
throw new AuthError("INVALID_ACCESS_TOKEN");
|
|
384
453
|
}
|
|
385
454
|
return {
|
|
386
455
|
userId: claims.sub,
|
|
387
456
|
username: typeof claims.username === "string" ? claims.username : void 0,
|
|
388
|
-
|
|
389
|
-
sessionId:
|
|
457
|
+
accessTokenId: typeof claims.jti === "string" ? claims.jti : "",
|
|
458
|
+
sessionId: claims.sid,
|
|
390
459
|
clientId: typeof claims.client_id === "string" ? claims.client_id : void 0,
|
|
391
460
|
roles,
|
|
392
461
|
claims
|
|
@@ -411,7 +480,7 @@ var createAuth = (input) => {
|
|
|
411
480
|
return dummyHash;
|
|
412
481
|
};
|
|
413
482
|
const sessionPolicy = (clientId) => {
|
|
414
|
-
const override = clientId === void 0 ? void 0 : settings.
|
|
483
|
+
const override = clientId === void 0 ? void 0 : settings.clientSessions[clientId];
|
|
415
484
|
if (!override) return settings.session;
|
|
416
485
|
return {
|
|
417
486
|
idleTimeout: override.idleTimeout ?? settings.session.idleTimeout,
|
|
@@ -423,8 +492,8 @@ var createAuth = (input) => {
|
|
|
423
492
|
return absoluteTimeout === void 0 ? void 0 : authTime + absoluteTimeout;
|
|
424
493
|
};
|
|
425
494
|
const signAccessToken = async (claims) => {
|
|
426
|
-
const {
|
|
427
|
-
const
|
|
495
|
+
const { signingKey, kid } = await getKeys();
|
|
496
|
+
const accessTokenId = crypto.randomUUID();
|
|
428
497
|
const issuedAt = nowInSeconds();
|
|
429
498
|
const expiresAt = Math.min(
|
|
430
499
|
issuedAt + settings.accessTokenTTL,
|
|
@@ -434,17 +503,21 @@ var createAuth = (input) => {
|
|
|
434
503
|
// Personal data: written on demand only.
|
|
435
504
|
...settings.usernameClaim && claims.username && { username: claims.username },
|
|
436
505
|
auth_time: claims.auth_time,
|
|
437
|
-
|
|
506
|
+
sid: claims.sid,
|
|
438
507
|
...claims.client_id !== void 0 && { client_id: claims.client_id }
|
|
439
|
-
}).setProtectedHeader({ alg: ALGORITHM, typ: ACCESS_TOKEN_TYPE, kid }).setSubject(claims.sub).setIssuer(settings.issuer).setAudience(settings.audience).setJti(
|
|
440
|
-
return {
|
|
508
|
+
}).setProtectedHeader({ alg: ALGORITHM, typ: ACCESS_TOKEN_TYPE, kid }).setSubject(claims.sub).setIssuer(settings.issuer).setAudience(settings.audience).setJti(accessTokenId).setIssuedAt(issuedAt).setExpirationTime(expiresAt).sign(signingKey);
|
|
509
|
+
return {
|
|
510
|
+
accessTokenId,
|
|
511
|
+
accessToken: token,
|
|
512
|
+
expiresAt: new Date(expiresAt * 1e3)
|
|
513
|
+
};
|
|
441
514
|
};
|
|
442
515
|
const generateRefreshToken = async () => {
|
|
443
516
|
const token = toHex(crypto.getRandomValues(new Uint8Array(128)));
|
|
444
517
|
return { token, tokenHash: await sha256(token) };
|
|
445
518
|
};
|
|
446
|
-
const issueTokens = async (claims,
|
|
447
|
-
const access = await signAccessToken({ ...claims, sid:
|
|
519
|
+
const issueTokens = async (claims, sessionId) => {
|
|
520
|
+
const access = await signAccessToken({ ...claims, sid: sessionId });
|
|
448
521
|
const refreshToken = await generateRefreshToken();
|
|
449
522
|
const end = sessionEnd(claims.auth_time, claims.client_id);
|
|
450
523
|
const refreshTokenExpiresAt = new Date(
|
|
@@ -454,10 +527,10 @@ var createAuth = (input) => {
|
|
|
454
527
|
)
|
|
455
528
|
);
|
|
456
529
|
await settings.dataProvider.storeRefreshToken({
|
|
457
|
-
|
|
530
|
+
tokenHash: refreshToken.tokenHash,
|
|
458
531
|
userId: claims.sub,
|
|
459
|
-
|
|
460
|
-
|
|
532
|
+
accessTokenId: access.accessTokenId,
|
|
533
|
+
sessionId,
|
|
461
534
|
expiresAt: refreshTokenExpiresAt
|
|
462
535
|
});
|
|
463
536
|
return {
|
|
@@ -473,17 +546,17 @@ var createAuth = (input) => {
|
|
|
473
546
|
if (usedAt !== void 0 && Date.now() - usedAt < settings.refreshTokenReuseInterval * 1e3) {
|
|
474
547
|
return;
|
|
475
548
|
}
|
|
476
|
-
const
|
|
477
|
-
if (
|
|
478
|
-
await settings.dataProvider.
|
|
549
|
+
const sessionId = stored.sessionId ?? void 0;
|
|
550
|
+
if (sessionId) {
|
|
551
|
+
await settings.dataProvider.revokeSessionTokens(sessionId);
|
|
479
552
|
} else {
|
|
480
|
-
await settings.dataProvider.
|
|
553
|
+
await settings.dataProvider.revokeUserTokens(stored.userId);
|
|
481
554
|
}
|
|
482
555
|
try {
|
|
483
556
|
await settings.onRefreshTokenReuse?.({
|
|
484
557
|
userId: stored.userId,
|
|
485
|
-
|
|
486
|
-
|
|
558
|
+
sessionId,
|
|
559
|
+
accessTokenId: stored.accessTokenId
|
|
487
560
|
});
|
|
488
561
|
} catch {
|
|
489
562
|
}
|
|
@@ -507,7 +580,6 @@ var createAuth = (input) => {
|
|
|
507
580
|
client_id: clientId
|
|
508
581
|
};
|
|
509
582
|
};
|
|
510
|
-
const generateAccessToken = async (subject) => signAccessToken(sessionClaims(subject));
|
|
511
583
|
let routesPath;
|
|
512
584
|
let cors;
|
|
513
585
|
const isTrustedOrigin = (req) => (origin) => {
|
|
@@ -519,12 +591,12 @@ var createAuth = (input) => {
|
|
|
519
591
|
const bindRoutesPath = (path) => {
|
|
520
592
|
if (typeof path !== "string" || !path.startsWith("/")) {
|
|
521
593
|
throw new TypeError(
|
|
522
|
-
`
|
|
594
|
+
`authPlugin(): the routes path must start with "/", received ${JSON.stringify(path)}.`
|
|
523
595
|
);
|
|
524
596
|
}
|
|
525
597
|
if (routesPath !== void 0 && routesPath !== path) {
|
|
526
598
|
throw new Error(
|
|
527
|
-
`
|
|
599
|
+
`authPlugin(): this auth service already serves routes under "${routesPath}": create one auth service per app to serve them under "${path}".`
|
|
528
600
|
);
|
|
529
601
|
}
|
|
530
602
|
routesPath = path;
|
|
@@ -539,16 +611,8 @@ var createAuth = (input) => {
|
|
|
539
611
|
const { httpOnly, secure, sameSite, path, domain } = settings.cookies;
|
|
540
612
|
return { httpOnly, secure, sameSite, path, domain };
|
|
541
613
|
};
|
|
542
|
-
|
|
543
|
-
settings,
|
|
614
|
+
const auth = {
|
|
544
615
|
isCookieAuthenticationAllowed: (req) => isCookieAuthenticationAllowed(req, isTrustedOrigin(req)),
|
|
545
|
-
init: async (options = {}) => {
|
|
546
|
-
if (options.routesPath !== void 0) bindRoutesPath(options.routesPath);
|
|
547
|
-
if (options.cors !== void 0) cors = options.cors;
|
|
548
|
-
await Promise.all([getKeys(), getDummyHash()]);
|
|
549
|
-
},
|
|
550
|
-
generateAccessToken,
|
|
551
|
-
generateRefreshToken,
|
|
552
616
|
async generateTokensForUser(subject) {
|
|
553
617
|
return issueTokens(sessionClaims(subject), crypto.randomUUID());
|
|
554
618
|
},
|
|
@@ -574,7 +638,7 @@ var createAuth = (input) => {
|
|
|
574
638
|
);
|
|
575
639
|
const refreshTokenHash = await sha256(refreshToken);
|
|
576
640
|
const stored = await settings.dataProvider.findRefreshToken(refreshTokenHash);
|
|
577
|
-
if (!userId || !stored || stored.userId !== userId || stored.
|
|
641
|
+
if (!userId || !stored || stored.userId !== userId || stored.accessTokenId !== claims.jti || stored.revokedAt || // Client removed from the configuration: the session ends.
|
|
578
642
|
!isClientAccepted(clientId)) {
|
|
579
643
|
throw new AuthError("INVALID_REFRESH_TOKEN");
|
|
580
644
|
}
|
|
@@ -590,10 +654,10 @@ var createAuth = (input) => {
|
|
|
590
654
|
}
|
|
591
655
|
const user = await settings.dataProvider.findUserById(userId);
|
|
592
656
|
if (!user) {
|
|
593
|
-
if (stored.
|
|
594
|
-
await settings.dataProvider.
|
|
657
|
+
if (stored.sessionId) {
|
|
658
|
+
await settings.dataProvider.revokeSessionTokens(stored.sessionId);
|
|
595
659
|
} else {
|
|
596
|
-
await settings.dataProvider.
|
|
660
|
+
await settings.dataProvider.revokeUserTokens(userId);
|
|
597
661
|
}
|
|
598
662
|
throw new AuthError("INVALID_REFRESH_TOKEN");
|
|
599
663
|
}
|
|
@@ -604,9 +668,9 @@ var createAuth = (input) => {
|
|
|
604
668
|
auth_time: authTime,
|
|
605
669
|
client_id: clientId
|
|
606
670
|
},
|
|
607
|
-
// Tokens stored
|
|
608
|
-
// so that concurrent rotations land in the same
|
|
609
|
-
stored.
|
|
671
|
+
// Tokens stored before sessions (V1) start one, derived from the
|
|
672
|
+
// token so that concurrent rotations land in the same session.
|
|
673
|
+
stored.sessionId || stored.accessTokenId
|
|
610
674
|
);
|
|
611
675
|
if (!stored.isUsed) {
|
|
612
676
|
await settings.dataProvider.markRefreshTokenAsUsed(
|
|
@@ -652,19 +716,27 @@ var createAuth = (input) => {
|
|
|
652
716
|
await sha256(refreshToken)
|
|
653
717
|
);
|
|
654
718
|
if (!stored) return void 0;
|
|
655
|
-
const sessionId = stored.
|
|
719
|
+
const sessionId = stored.sessionId || void 0;
|
|
656
720
|
if (scope === "all") {
|
|
657
721
|
if (stored.revokedAt || stored.isUsed || stored.expiresAt.getTime() <= Date.now()) {
|
|
658
722
|
return void 0;
|
|
659
723
|
}
|
|
660
|
-
await dataProvider.
|
|
724
|
+
await dataProvider.revokeUserTokens(stored.userId);
|
|
661
725
|
} else if (sessionId) {
|
|
662
|
-
await dataProvider.
|
|
726
|
+
await dataProvider.revokeSessionTokens(sessionId);
|
|
663
727
|
} else {
|
|
664
|
-
await dataProvider.
|
|
728
|
+
await dataProvider.revokeUserTokens(stored.userId);
|
|
665
729
|
}
|
|
666
730
|
return { userId: stored.userId, sessionId };
|
|
667
731
|
},
|
|
732
|
+
async revokeUserSessions(userId) {
|
|
733
|
+
if (typeof userId !== "string" || userId === "") {
|
|
734
|
+
throw new TypeError(
|
|
735
|
+
"revokeUserSessions(): userId must be a non-empty string."
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
await settings.dataProvider.revokeUserTokens(userId);
|
|
739
|
+
},
|
|
668
740
|
async getJwks() {
|
|
669
741
|
const { verificationKeys } = await getKeys();
|
|
670
742
|
return {
|
|
@@ -683,13 +755,13 @@ var createAuth = (input) => {
|
|
|
683
755
|
return end === void 0 ? void 0 : new Date(end * 1e3);
|
|
684
756
|
},
|
|
685
757
|
getUserRoles: (user) => settings.getUserRoles(user),
|
|
686
|
-
setAuthCookies(res, tokens
|
|
758
|
+
setAuthCookies(res, tokens) {
|
|
687
759
|
const cookies = settings.cookies;
|
|
688
760
|
if (!cookies.enabled) return;
|
|
689
761
|
const expiresAt = tokens.refreshTokenExpiresAt?.getTime();
|
|
690
762
|
const options = {
|
|
691
763
|
...cookieOptions(),
|
|
692
|
-
maxAge:
|
|
764
|
+
maxAge: expiresAt === void 0 ? settings.session.idleTimeout : Math.max(0, Math.ceil((expiresAt - Date.now()) / 1e3))
|
|
693
765
|
};
|
|
694
766
|
res.setCookie(cookies.refreshTokenName, tokens.refreshToken, {
|
|
695
767
|
...options,
|
|
@@ -711,7 +783,7 @@ var createAuth = (input) => {
|
|
|
711
783
|
res.clearCookie(cookies.accessTokenName, cookieOptions());
|
|
712
784
|
}
|
|
713
785
|
},
|
|
714
|
-
hashPassword: (password
|
|
786
|
+
hashPassword: (password) => generatePasswordHash(password, { rounds: settings.passwordHashRounds }),
|
|
715
787
|
passwordNeedsRehash: (passwordHash) => BCRYPT_HASH.test(passwordHash) && bcrypt.getRounds(passwordHash) !== settings.passwordHashRounds,
|
|
716
788
|
// No length check: the password was verified, and a longer V1 password
|
|
717
789
|
// is truncated the same way.
|
|
@@ -724,16 +796,39 @@ var createAuth = (input) => {
|
|
|
724
796
|
return false;
|
|
725
797
|
}
|
|
726
798
|
};
|
|
799
|
+
internals.set(auth, {
|
|
800
|
+
settings,
|
|
801
|
+
init: async (options = {}) => {
|
|
802
|
+
if (options.routesPath !== void 0) bindRoutesPath(options.routesPath);
|
|
803
|
+
if (options.cors !== void 0) cors = options.cors;
|
|
804
|
+
await Promise.all([getKeys(), getDummyHash()]);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
return auth;
|
|
727
808
|
};
|
|
728
809
|
var MAX_PASSWORD_BYTES = 72;
|
|
729
810
|
var isPasswordTooLong = (password) => bcrypt.truncates(password);
|
|
730
|
-
var generatePasswordHash = async (password,
|
|
811
|
+
var generatePasswordHash = async (password, options = {}) => {
|
|
812
|
+
if (typeof options !== "object" || options === null) {
|
|
813
|
+
throw new TypeError("generatePasswordHash(): options must be an object.");
|
|
814
|
+
}
|
|
815
|
+
for (const key of Object.keys(options)) {
|
|
816
|
+
if (key !== "rounds") {
|
|
817
|
+
throw new TypeError(`generatePasswordHash(): unknown option "${key}".`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
const { rounds = 12 } = options;
|
|
821
|
+
if (!Number.isInteger(rounds) || rounds < 4 || rounds > 31) {
|
|
822
|
+
throw new RangeError(
|
|
823
|
+
"generatePasswordHash(): rounds must be an integer between 4 and 31."
|
|
824
|
+
);
|
|
825
|
+
}
|
|
731
826
|
if (isPasswordTooLong(password)) {
|
|
732
827
|
throw new RangeError(
|
|
733
828
|
`Password too long: bcrypt reads ${MAX_PASSWORD_BYTES} bytes at most (UTF-8). Check it with isPasswordTooLong().`
|
|
734
829
|
);
|
|
735
830
|
}
|
|
736
|
-
return bcrypt.hash(password,
|
|
831
|
+
return bcrypt.hash(password, rounds);
|
|
737
832
|
};
|
|
738
833
|
|
|
739
834
|
// src/middlewares.ts
|
|
@@ -749,7 +844,7 @@ var requireAuth = defineMiddleware(
|
|
|
749
844
|
);
|
|
750
845
|
var requireRole = (...roles) => {
|
|
751
846
|
if (roles.length === 0) {
|
|
752
|
-
throw new TypeError("requireRole()
|
|
847
|
+
throw new TypeError("requireRole(): expected at least one role.");
|
|
753
848
|
}
|
|
754
849
|
return defineMiddleware((ctx) => {
|
|
755
850
|
if (!ctx.user || !ctx.auth) return unauthorizedError();
|
|
@@ -759,7 +854,7 @@ var requireRole = (...roles) => {
|
|
|
759
854
|
};
|
|
760
855
|
var requireClient = (...clientIds) => {
|
|
761
856
|
if (clientIds.length === 0) {
|
|
762
|
-
throw new TypeError("requireClient()
|
|
857
|
+
throw new TypeError("requireClient(): expected at least one client.");
|
|
763
858
|
}
|
|
764
859
|
return defineMiddleware((ctx) => {
|
|
765
860
|
if (!ctx.user || !ctx.auth) return unauthorizedError();
|
|
@@ -776,6 +871,11 @@ import {
|
|
|
776
871
|
tooManyRequestsError,
|
|
777
872
|
unauthorizedError as unauthorizedError2
|
|
778
873
|
} from "@dotpress/core";
|
|
874
|
+
var PLUGIN_OPTIONS = [
|
|
875
|
+
"prefix",
|
|
876
|
+
"routes",
|
|
877
|
+
"jwks"
|
|
878
|
+
];
|
|
779
879
|
var AUTH_SECURITY_SCHEMES = {
|
|
780
880
|
bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT" }
|
|
781
881
|
};
|
|
@@ -802,7 +902,6 @@ var findAccessToken = (req, cookies) => {
|
|
|
802
902
|
}
|
|
803
903
|
return void 0;
|
|
804
904
|
};
|
|
805
|
-
var extractAccessToken = (req, cookies) => findAccessToken(req, cookies)?.token;
|
|
806
905
|
var loginThrottleKeys = async (throttle, username, ipAddress) => {
|
|
807
906
|
const normalized = username.trim().toLowerCase();
|
|
808
907
|
const { usernameAndIp, username: perUsername } = throttle.limits;
|
|
@@ -838,8 +937,7 @@ var refreshErrorResponse = (error, fallback) => {
|
|
|
838
937
|
}
|
|
839
938
|
return internalError(fallback);
|
|
840
939
|
};
|
|
841
|
-
var registerRoutes = (group, auth) => {
|
|
842
|
-
const { settings } = auth;
|
|
940
|
+
var registerRoutes = (group, auth, settings) => {
|
|
843
941
|
const { dataProvider, cookies } = settings;
|
|
844
942
|
const expiration = (accessTokenExpiresAt, sessionExpiresAt) => ({
|
|
845
943
|
expiresIn: Math.max(
|
|
@@ -944,7 +1042,11 @@ var registerRoutes = (group, auth) => {
|
|
|
944
1042
|
if (!passwordValid) {
|
|
945
1043
|
return loginFailed({ ...event, errorType: "invalid_password" });
|
|
946
1044
|
}
|
|
947
|
-
if (clientId !== void 0 && clients?.isUserRoleAllowed && !clients.isUserRoleAllowed(
|
|
1045
|
+
if (clientId !== void 0 && clients?.isUserRoleAllowed && !clients.isUserRoleAllowed({
|
|
1046
|
+
userId: user.userId,
|
|
1047
|
+
role: user.role,
|
|
1048
|
+
clientId
|
|
1049
|
+
})) {
|
|
948
1050
|
return loginFailed({ ...event, errorType: "invalid_role" });
|
|
949
1051
|
}
|
|
950
1052
|
const tokens = await auth.generateTokensForUser({
|
|
@@ -1123,9 +1225,9 @@ var registerRoutes = (group, auth) => {
|
|
|
1123
1225
|
const revoked = refreshToken ? await auth.revokeSession(refreshToken, { scope }) : void 0;
|
|
1124
1226
|
if (!revoked && session) {
|
|
1125
1227
|
if (scope === "all") {
|
|
1126
|
-
await dataProvider.
|
|
1228
|
+
await dataProvider.revokeUserTokens(session.userId);
|
|
1127
1229
|
} else if (session.sessionId) {
|
|
1128
|
-
await dataProvider.
|
|
1230
|
+
await dataProvider.revokeSessionTokens(session.sessionId);
|
|
1129
1231
|
}
|
|
1130
1232
|
} else if (!revoked && scope === "all") {
|
|
1131
1233
|
return unauthorizedError2();
|
|
@@ -1137,13 +1239,28 @@ var registerRoutes = (group, auth) => {
|
|
|
1137
1239
|
};
|
|
1138
1240
|
var normalizePrefix = (prefix) => `/${prefix.split("/").filter(Boolean).join("/")}`;
|
|
1139
1241
|
var authPlugin = (auth, options = {}) => {
|
|
1242
|
+
const internal = internalsOf(auth);
|
|
1243
|
+
if (!internal) {
|
|
1244
|
+
throw new TypeError(
|
|
1245
|
+
"authPlugin(): expected an auth service created with createAuth()."
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
if (typeof options !== "object" || options === null) {
|
|
1249
|
+
throw new TypeError("authPlugin(): options must be an object.");
|
|
1250
|
+
}
|
|
1251
|
+
for (const key of Object.keys(options)) {
|
|
1252
|
+
if (!PLUGIN_OPTIONS.includes(key)) {
|
|
1253
|
+
throw new TypeError(`authPlugin(): unknown option "${key}".`);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
const { settings } = internal;
|
|
1140
1257
|
const prefix = options.prefix ?? "/auth";
|
|
1141
1258
|
return definePlugin({
|
|
1142
1259
|
name: "auth",
|
|
1143
1260
|
auth,
|
|
1144
1261
|
securitySchemes: AUTH_SECURITY_SCHEMES,
|
|
1145
1262
|
async setup(api) {
|
|
1146
|
-
await
|
|
1263
|
+
await internal.init({
|
|
1147
1264
|
...options.routes === false ? {} : { routesPath: api.publicPath(normalizePrefix(prefix)) },
|
|
1148
1265
|
...api.cors ? { cors: api.cors } : {}
|
|
1149
1266
|
});
|
|
@@ -1151,8 +1268,7 @@ var authPlugin = (auth, options = {}) => {
|
|
|
1151
1268
|
api.decorateContext("auth", () => void 0);
|
|
1152
1269
|
const rejectedTokens = /* @__PURE__ */ new WeakSet();
|
|
1153
1270
|
const authenticate = async (ctx) => {
|
|
1154
|
-
const
|
|
1155
|
-
const found = findAccessToken(ctx.req, cookies);
|
|
1271
|
+
const found = findAccessToken(ctx.req, settings.cookies);
|
|
1156
1272
|
if (!found) return;
|
|
1157
1273
|
const { token } = found;
|
|
1158
1274
|
if (found.source === "cookie" && !await auth.isCookieAuthenticationAllowed(ctx.req)) {
|
|
@@ -1164,9 +1280,7 @@ var authPlugin = (auth, options = {}) => {
|
|
|
1164
1280
|
}
|
|
1165
1281
|
try {
|
|
1166
1282
|
const session = await auth.verifyAccessToken(token);
|
|
1167
|
-
const user = await
|
|
1168
|
-
session.userId
|
|
1169
|
-
);
|
|
1283
|
+
const user = await settings.dataProvider.findUserById(session.userId);
|
|
1170
1284
|
if (!user) {
|
|
1171
1285
|
if (found.source === "header") rejectedTokens.add(ctx);
|
|
1172
1286
|
return;
|
|
@@ -1197,7 +1311,7 @@ var authPlugin = (auth, options = {}) => {
|
|
|
1197
1311
|
};
|
|
1198
1312
|
});
|
|
1199
1313
|
if (options.jwks) {
|
|
1200
|
-
api.
|
|
1314
|
+
api.defineRoute({
|
|
1201
1315
|
path: "/.well-known/jwks.json",
|
|
1202
1316
|
meta: { tags: ["auth"], summary: "Public keys of the access tokens" },
|
|
1203
1317
|
handler: async ({ res }) => {
|
|
@@ -1207,7 +1321,7 @@ var authPlugin = (auth, options = {}) => {
|
|
|
1207
1321
|
});
|
|
1208
1322
|
}
|
|
1209
1323
|
if (options.routes !== false) {
|
|
1210
|
-
registerRoutes(api.
|
|
1324
|
+
registerRoutes(api.createGroup(prefix), auth, settings);
|
|
1211
1325
|
}
|
|
1212
1326
|
}
|
|
1213
1327
|
});
|
|
@@ -1215,13 +1329,10 @@ var authPlugin = (auth, options = {}) => {
|
|
|
1215
1329
|
export {
|
|
1216
1330
|
AUTH_SECURITY_SCHEMES,
|
|
1217
1331
|
AuthError,
|
|
1218
|
-
MAX_PASSWORD_BYTES,
|
|
1219
1332
|
authPlugin,
|
|
1220
1333
|
createAuth,
|
|
1221
|
-
extractAccessToken,
|
|
1222
1334
|
generatePasswordHash,
|
|
1223
1335
|
isPasswordTooLong,
|
|
1224
|
-
memoryRateLimitStore,
|
|
1225
1336
|
requireAuth,
|
|
1226
1337
|
requireClient,
|
|
1227
1338
|
requireRole
|