@sqrzro/server 2.0.0-bz.12 → 2.0.0-bz.13
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/auth.js +81 -450
- package/dist/cache.js +6 -44
- package/dist/chunk-7XG6NQUS.js +268 -0
- package/dist/chunk-ALBKLI2J.js +30 -0
- package/dist/chunk-G4EG2ECB.js +47 -0
- package/dist/chunk-GOSRKBZX.js +20 -0
- package/dist/forms.js +9 -302
- package/dist/lists.js +4 -30
- package/dist/mail.js +2 -40
- package/dist/middleware.js +6 -30
- package/dist/schema.js +12 -69
- package/dist/url.js +6 -54
- package/package.json +3 -2
package/dist/auth.js
CHANGED
|
@@ -1,427 +1,59 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
-
|
|
29
|
-
// src/auth/index.ts
|
|
30
|
-
var auth_exports = {};
|
|
31
|
-
__export(auth_exports, {
|
|
32
|
-
checkMFAEnabled: () => checkMFAEnabled,
|
|
33
|
-
checkPasswordComplexity: () => checkPasswordComplexity,
|
|
34
|
-
checkRouteAllowed: () => checkRouteAllowed,
|
|
35
|
-
checkSessionExists: () => checkSessionExists,
|
|
36
|
-
checkUserHasMFA: () => checkUserHasMFA,
|
|
37
|
-
createUserSession: () => createUserSession,
|
|
38
|
-
generateID: () => generateID,
|
|
39
|
-
generateMFA: () => generateMFA,
|
|
40
|
-
getAllowedRoles: () => getAllowedRoles,
|
|
41
|
-
getClientByID: () => getClientByID,
|
|
42
|
-
getPasswordComplexity: () => getPasswordComplexity,
|
|
43
|
-
getScopeByID: () => getScopeByID,
|
|
44
|
-
getScopes: () => getScopes,
|
|
45
|
-
getSessionID: () => getSessionID,
|
|
46
|
-
getSessionUser: () => getSessionUser,
|
|
47
|
-
handleClientAuth: () => handleClientAuth,
|
|
48
|
-
handleLoginForm: () => handleLoginForm,
|
|
49
|
-
handleLogout: () => handleLogout,
|
|
50
|
-
handleMFAForm: () => handleMFAForm,
|
|
51
|
-
handlePasswordForm: () => handlePasswordForm,
|
|
52
|
-
handlePasswordResetForm: () => handlePasswordResetForm,
|
|
53
|
-
handleSession: () => handleSession,
|
|
54
|
-
hashPassword: () => hashPassword,
|
|
55
|
-
invalidateSession: () => invalidateSession,
|
|
56
|
-
invalidateUserSessions: () => invalidateUserSessions,
|
|
57
|
-
lucia: () => lucia,
|
|
58
|
-
registerClient: () => registerClient,
|
|
59
|
-
registerUser: () => registerUser,
|
|
60
|
-
setScopes: () => setScopes,
|
|
61
|
-
verifyPassword: () => verifyPassword
|
|
62
|
-
});
|
|
63
|
-
module.exports = __toCommonJS(auth_exports);
|
|
1
|
+
import {
|
|
2
|
+
getFromCache,
|
|
3
|
+
setToCache
|
|
4
|
+
} from "./chunk-GOSRKBZX.js";
|
|
5
|
+
import {
|
|
6
|
+
ValidationError_default,
|
|
7
|
+
createSchema,
|
|
8
|
+
submitForm
|
|
9
|
+
} from "./chunk-7XG6NQUS.js";
|
|
10
|
+
import {
|
|
11
|
+
authClientTable,
|
|
12
|
+
authMFATable,
|
|
13
|
+
authResetTable,
|
|
14
|
+
authSessionTable,
|
|
15
|
+
authUserTable
|
|
16
|
+
} from "./chunk-G4EG2ECB.js";
|
|
17
|
+
import {
|
|
18
|
+
getOrigin
|
|
19
|
+
} from "./chunk-ALBKLI2J.js";
|
|
64
20
|
|
|
65
21
|
// src/auth/AuthService.ts
|
|
66
|
-
|
|
22
|
+
import { and as and2, eq as eq2, inArray } from "drizzle-orm";
|
|
67
23
|
|
|
68
24
|
// src/database/DatabaseService.ts
|
|
69
|
-
|
|
70
|
-
|
|
25
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
26
|
+
import postgres from "postgres";
|
|
71
27
|
function createSingleton() {
|
|
72
28
|
if (!process.env.DATABASE_URL) {
|
|
73
29
|
throw new Error("DATABASE_URL is not defined");
|
|
74
30
|
}
|
|
75
|
-
return
|
|
31
|
+
return drizzle(postgres(process.env.DATABASE_URL, { prepare: false }));
|
|
76
32
|
}
|
|
77
33
|
var db = globalThis.db ?? createSingleton();
|
|
78
34
|
if (!process.env.VERCEL_ENV) {
|
|
79
35
|
globalThis.db = db;
|
|
80
36
|
}
|
|
81
37
|
|
|
82
|
-
// src/database/schema.ts
|
|
83
|
-
var import_pg_core = require("drizzle-orm/pg-core");
|
|
84
|
-
var DEFAULT_ROLE = 10;
|
|
85
|
-
var mfaType = (0, import_pg_core.pgEnum)("mfaType", ["TOTP", "HARDWARE"]);
|
|
86
|
-
var scope = (0, import_pg_core.pgEnum)("scope", ["ANON", "MFA", "AUTHED"]);
|
|
87
|
-
var authSchema = (0, import_pg_core.pgSchema)("auth");
|
|
88
|
-
var authUserTable = authSchema.table("user_credentials", {
|
|
89
|
-
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
90
|
-
email: (0, import_pg_core.text)("email").notNull().unique(),
|
|
91
|
-
password: (0, import_pg_core.text)("password"),
|
|
92
|
-
role: (0, import_pg_core.integer)("role").notNull().default(DEFAULT_ROLE)
|
|
93
|
-
});
|
|
94
|
-
var authSessionTable = authSchema.table("sessions", {
|
|
95
|
-
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
96
|
-
userId: (0, import_pg_core.text)("userId").notNull().references(() => authUserTable.id),
|
|
97
|
-
scope: scope("scope").notNull().default("ANON"),
|
|
98
|
-
expiresAt: (0, import_pg_core.timestamp)("expiresAt").notNull()
|
|
99
|
-
});
|
|
100
|
-
var authResetTable = authSchema.table("resets", {
|
|
101
|
-
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
102
|
-
userId: (0, import_pg_core.text)("userId").notNull().references(() => authUserTable.id),
|
|
103
|
-
expiresAt: (0, import_pg_core.timestamp)("expiresAt").notNull()
|
|
104
|
-
});
|
|
105
|
-
var authMFATable = authSchema.table("mfas", {
|
|
106
|
-
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
107
|
-
name: (0, import_pg_core.text)("name").notNull(),
|
|
108
|
-
userId: (0, import_pg_core.text)("userId").notNull().references(() => authUserTable.id),
|
|
109
|
-
type: mfaType("type").notNull().default("TOTP"),
|
|
110
|
-
secret: (0, import_pg_core.text)("secret").notNull(),
|
|
111
|
-
verifiedAt: (0, import_pg_core.timestamp)("verifiedAt")
|
|
112
|
-
});
|
|
113
|
-
var authClientTable = authSchema.table("client_credentials", {
|
|
114
|
-
id: (0, import_pg_core.text)("id").primaryKey(),
|
|
115
|
-
alias: (0, import_pg_core.text)("alias").notNull().unique(),
|
|
116
|
-
secret: (0, import_pg_core.text)("secret").notNull().unique()
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// src/forms/ValidationError.ts
|
|
120
|
-
var ValidationError = class extends Error {
|
|
121
|
-
constructor(messages2) {
|
|
122
|
-
super(JSON.stringify(messages2));
|
|
123
|
-
this.name = "ValidationError";
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
var ValidationError_default = ValidationError;
|
|
127
|
-
|
|
128
|
-
// src/forms/ValidationService.ts
|
|
129
|
-
var import_joi = __toESM(require("joi"));
|
|
130
|
-
|
|
131
|
-
// src/forms/lang.ts
|
|
132
|
-
var messages = {
|
|
133
|
-
"alternatives.all": "",
|
|
134
|
-
"alternatives.any": "",
|
|
135
|
-
"alternatives.match": "",
|
|
136
|
-
"alternatives.one": "",
|
|
137
|
-
"alternatives.types": "",
|
|
138
|
-
"any.custom": "",
|
|
139
|
-
"any.default": "",
|
|
140
|
-
"any.failover": "",
|
|
141
|
-
"any.invalid": "",
|
|
142
|
-
"any.only": "",
|
|
143
|
-
"any.ref": "",
|
|
144
|
-
"any.required": "{{#label}} is required",
|
|
145
|
-
"any.unknown": "",
|
|
146
|
-
"array.base": "",
|
|
147
|
-
"array.excludes": "",
|
|
148
|
-
"array.includesRequiredBoth": "",
|
|
149
|
-
"array.includesRequiredKnowns": "",
|
|
150
|
-
"array.includesRequiredUnknowns": "",
|
|
151
|
-
"array.includes": "",
|
|
152
|
-
"array.length": "",
|
|
153
|
-
"array.max": "",
|
|
154
|
-
"array.min": "",
|
|
155
|
-
"array.orderedLength": "",
|
|
156
|
-
"array.sort": "",
|
|
157
|
-
"array.sort.mismatching": "",
|
|
158
|
-
"array.sort.unsupported": "",
|
|
159
|
-
"array.sparse": "",
|
|
160
|
-
"array.unique": "",
|
|
161
|
-
"array.hasKnown": "",
|
|
162
|
-
"array.hasUnknown": "",
|
|
163
|
-
"binary.base": "",
|
|
164
|
-
"binary.length": "",
|
|
165
|
-
"binary.max": "",
|
|
166
|
-
"binary.min": "",
|
|
167
|
-
"boolean.base": "",
|
|
168
|
-
"date.base": "",
|
|
169
|
-
"date.format": "",
|
|
170
|
-
"date.greater": "",
|
|
171
|
-
"date.less": "",
|
|
172
|
-
"date.max": "",
|
|
173
|
-
"date.min": "",
|
|
174
|
-
"date.strict": "",
|
|
175
|
-
"function.arity": "",
|
|
176
|
-
"function.class": "",
|
|
177
|
-
"function.maxArity": "",
|
|
178
|
-
"function.minArity": "",
|
|
179
|
-
"number.base": "{{#label}} should be a number",
|
|
180
|
-
"number.greater": "",
|
|
181
|
-
"number.infinity": "",
|
|
182
|
-
"number.integer": "",
|
|
183
|
-
"number.less": "",
|
|
184
|
-
"number.max": "",
|
|
185
|
-
"number.min": "{{#label}} should be greater than or equal to {{#limit}}",
|
|
186
|
-
"number.multiple": "",
|
|
187
|
-
"number.negative": "",
|
|
188
|
-
"number.port": "",
|
|
189
|
-
"number.positive": "",
|
|
190
|
-
"number.precision": "",
|
|
191
|
-
"number.unsafe": "",
|
|
192
|
-
"object.unknown": "",
|
|
193
|
-
"object.and": "",
|
|
194
|
-
"object.assert": "",
|
|
195
|
-
"object.base": "",
|
|
196
|
-
"object.length": "",
|
|
197
|
-
"object.max": "",
|
|
198
|
-
"object.min": "",
|
|
199
|
-
"object.missing": "",
|
|
200
|
-
"object.nand": "",
|
|
201
|
-
"object.pattern.match": "",
|
|
202
|
-
"object.refType": "",
|
|
203
|
-
"object.regex": "",
|
|
204
|
-
"object.rename.multiple": "",
|
|
205
|
-
"object.rename.override": "",
|
|
206
|
-
"object.schema": "",
|
|
207
|
-
"object.instance": "",
|
|
208
|
-
"object.with": "",
|
|
209
|
-
"object.without": "",
|
|
210
|
-
"object.xor": "",
|
|
211
|
-
"object.oxor": "",
|
|
212
|
-
"string.alphanum": "",
|
|
213
|
-
"string.base64": "",
|
|
214
|
-
"string.base": "",
|
|
215
|
-
"string.creditCard": "",
|
|
216
|
-
"string.dataUri": "",
|
|
217
|
-
"string.domain": "",
|
|
218
|
-
"string.email": "",
|
|
219
|
-
"string.empty": "{{#label}} is required",
|
|
220
|
-
"string.guid": "",
|
|
221
|
-
"string.hexAlign": "",
|
|
222
|
-
"string.hex": "",
|
|
223
|
-
"string.hostname": "",
|
|
224
|
-
"string.ipVersion": "",
|
|
225
|
-
"string.ip": "",
|
|
226
|
-
"string.isoDate": "",
|
|
227
|
-
"string.isoDuration": "",
|
|
228
|
-
"string.length": "",
|
|
229
|
-
"string.lowercase": "",
|
|
230
|
-
"string.max": "",
|
|
231
|
-
"string.min": "",
|
|
232
|
-
"string.normalize": "",
|
|
233
|
-
"string.pattern.base": "",
|
|
234
|
-
"string.pattern.name": "",
|
|
235
|
-
"string.pattern.invert.base": "",
|
|
236
|
-
"string.pattern.invert.name": "",
|
|
237
|
-
"string.token": "",
|
|
238
|
-
"string.trim": "",
|
|
239
|
-
"string.uppercase": "",
|
|
240
|
-
"string.uri": "",
|
|
241
|
-
"string.uriCustomScheme": "",
|
|
242
|
-
"string.uriRelativeOnly": "",
|
|
243
|
-
"symbol.base": "",
|
|
244
|
-
"symbol.map": ""
|
|
245
|
-
};
|
|
246
|
-
var lang_default = messages;
|
|
247
|
-
|
|
248
|
-
// src/forms/ValidationService.ts
|
|
249
|
-
function getErrorMessages() {
|
|
250
|
-
return Object.entries(lang_default).reduce((acc, [key, value]) => {
|
|
251
|
-
if (!value) {
|
|
252
|
-
return acc;
|
|
253
|
-
}
|
|
254
|
-
return {
|
|
255
|
-
...acc,
|
|
256
|
-
[key]: value
|
|
257
|
-
};
|
|
258
|
-
}, {});
|
|
259
|
-
}
|
|
260
|
-
function transformErrors(error) {
|
|
261
|
-
const messages2 = error.details.reduce(
|
|
262
|
-
(acc, cur) => ({
|
|
263
|
-
...acc,
|
|
264
|
-
[cur.path.join(".")]: cur.message.replace(/"/gu, "")
|
|
265
|
-
}),
|
|
266
|
-
{}
|
|
267
|
-
);
|
|
268
|
-
return new ValidationError_default(messages2);
|
|
269
|
-
}
|
|
270
|
-
async function validateSchema(formData, validation) {
|
|
271
|
-
try {
|
|
272
|
-
const validated = await validation.validateAsync(formData, {
|
|
273
|
-
abortEarly: false,
|
|
274
|
-
messages: getErrorMessages()
|
|
275
|
-
});
|
|
276
|
-
return [validated, null];
|
|
277
|
-
} catch (err) {
|
|
278
|
-
if (err instanceof import_joi.default.ValidationError) {
|
|
279
|
-
return [null, transformErrors(err)];
|
|
280
|
-
}
|
|
281
|
-
if (err instanceof Error) {
|
|
282
|
-
return [null, err];
|
|
283
|
-
}
|
|
284
|
-
return [null, new Error("Unknown validation error occured")];
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
function createSchema(schema) {
|
|
288
|
-
return import_joi.default.object(schema);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// src/forms/FormService.ts
|
|
292
|
-
function serializeError(err) {
|
|
293
|
-
return {
|
|
294
|
-
cause: err.cause,
|
|
295
|
-
message: err.message,
|
|
296
|
-
name: err.name,
|
|
297
|
-
stack: err.stack
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
function hasFn(args) {
|
|
301
|
-
return Boolean(Object.prototype.hasOwnProperty.call(args, "fn"));
|
|
302
|
-
}
|
|
303
|
-
async function submitForm(args) {
|
|
304
|
-
let data = { ...args.formData };
|
|
305
|
-
if (args.request) {
|
|
306
|
-
const [validated, validationError] = await validateSchema(args.formData, args.request);
|
|
307
|
-
if (validationError !== null) {
|
|
308
|
-
if (validationError instanceof ValidationError_default) {
|
|
309
|
-
args.onValidationError?.(validationError);
|
|
310
|
-
}
|
|
311
|
-
return [null, serializeError(validationError)];
|
|
312
|
-
}
|
|
313
|
-
data = validated;
|
|
314
|
-
}
|
|
315
|
-
if (!hasFn(args)) {
|
|
316
|
-
try {
|
|
317
|
-
await args.onSuccess?.(data);
|
|
318
|
-
} catch (err) {
|
|
319
|
-
if (err instanceof Error) {
|
|
320
|
-
return [null, serializeError(err)];
|
|
321
|
-
}
|
|
322
|
-
return [
|
|
323
|
-
null,
|
|
324
|
-
serializeError(
|
|
325
|
-
new Error("The submitForm onSuccess function encountered an unknown error")
|
|
326
|
-
)
|
|
327
|
-
];
|
|
328
|
-
}
|
|
329
|
-
return [data, null];
|
|
330
|
-
}
|
|
331
|
-
let model = null;
|
|
332
|
-
try {
|
|
333
|
-
model = await args.fn(data);
|
|
334
|
-
} catch (err) {
|
|
335
|
-
if (err instanceof ValidationError_default) {
|
|
336
|
-
args.onValidationError?.(err);
|
|
337
|
-
return [null, serializeError(err)];
|
|
338
|
-
}
|
|
339
|
-
if (err instanceof Error) {
|
|
340
|
-
return [null, serializeError(err)];
|
|
341
|
-
}
|
|
342
|
-
return [
|
|
343
|
-
null,
|
|
344
|
-
serializeError(
|
|
345
|
-
new Error("The function supplied to submitForm encountered an unknown error")
|
|
346
|
-
)
|
|
347
|
-
];
|
|
348
|
-
}
|
|
349
|
-
if (!model) {
|
|
350
|
-
return [
|
|
351
|
-
null,
|
|
352
|
-
serializeError(
|
|
353
|
-
new Error("No model has been returned from the function supplied to submitForm")
|
|
354
|
-
)
|
|
355
|
-
];
|
|
356
|
-
}
|
|
357
|
-
try {
|
|
358
|
-
await args.onSuccess?.(model);
|
|
359
|
-
} catch (err) {
|
|
360
|
-
if (err instanceof Error) {
|
|
361
|
-
return [null, serializeError(err)];
|
|
362
|
-
}
|
|
363
|
-
return [
|
|
364
|
-
null,
|
|
365
|
-
serializeError(
|
|
366
|
-
new Error("The submitForm onSuccess function encountered an unknown error")
|
|
367
|
-
)
|
|
368
|
-
];
|
|
369
|
-
}
|
|
370
|
-
return [model, null];
|
|
371
|
-
}
|
|
372
|
-
|
|
373
38
|
// src/auth/MFAService.ts
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
39
|
+
import { and, eq, isNotNull, isNull } from "drizzle-orm";
|
|
40
|
+
import qrcode from "qrcode";
|
|
41
|
+
import { authenticator } from "otplib";
|
|
377
42
|
|
|
378
43
|
// src/auth/MFARequest.ts
|
|
379
|
-
|
|
44
|
+
import Joi from "joi";
|
|
380
45
|
var MFARequest = createSchema({
|
|
381
|
-
token:
|
|
46
|
+
token: Joi.string().pattern(/^[0-9]{6}$/u).required()
|
|
382
47
|
});
|
|
383
48
|
var MFARequest_default = MFARequest;
|
|
384
49
|
|
|
385
50
|
// src/auth/SessionService.ts
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
// src/cache/CacheService.ts
|
|
393
|
-
var import_redis = require("redis");
|
|
394
|
-
async function getClient() {
|
|
395
|
-
const client = (0, import_redis.createClient)();
|
|
396
|
-
await client.connect();
|
|
397
|
-
return client;
|
|
398
|
-
}
|
|
399
|
-
async function getFromCache(key) {
|
|
400
|
-
const client = await getClient();
|
|
401
|
-
return client.get(key);
|
|
402
|
-
}
|
|
403
|
-
async function setToCache(key, value) {
|
|
404
|
-
const client = await getClient();
|
|
405
|
-
await client.set(key, value);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// src/url/URLService.ts
|
|
409
|
-
var import_headers = require("next/headers");
|
|
410
|
-
function getOrigin() {
|
|
411
|
-
const origin = (0, import_headers.headers)().get("x-origin");
|
|
412
|
-
if (origin) {
|
|
413
|
-
return origin;
|
|
414
|
-
}
|
|
415
|
-
const proto = (0, import_headers.headers)().get("x-forwarded-proto");
|
|
416
|
-
const host = (0, import_headers.headers)().get("x-forwarded-host");
|
|
417
|
-
if (proto && host) {
|
|
418
|
-
return `${proto}://${host}`;
|
|
419
|
-
}
|
|
420
|
-
throw new Error("No origin could be determined");
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// src/auth/SessionService.ts
|
|
424
|
-
var import_utility = require("@sqrzro/utility");
|
|
51
|
+
import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle";
|
|
52
|
+
import { Lucia, generateId } from "lucia";
|
|
53
|
+
import { cookies } from "next/headers";
|
|
54
|
+
import { NextResponse } from "next/server";
|
|
55
|
+
import { match } from "path-to-regexp";
|
|
56
|
+
import { getFromObject } from "@sqrzro/utility";
|
|
425
57
|
var DEFAULT_REDIRECT = "/auth/login";
|
|
426
58
|
var ID_LENGTH = 16;
|
|
427
59
|
var DEFAULT_SCOPES = {
|
|
@@ -438,8 +70,8 @@ var DEFAULT_SCOPES = {
|
|
|
438
70
|
redirectOnAuth: "/"
|
|
439
71
|
}
|
|
440
72
|
};
|
|
441
|
-
var adapter = new
|
|
442
|
-
var lucia = new
|
|
73
|
+
var adapter = new DrizzlePostgreSQLAdapter(db, authSessionTable, authUserTable);
|
|
74
|
+
var lucia = new Lucia(adapter, {
|
|
443
75
|
sessionCookie: {
|
|
444
76
|
attributes: {
|
|
445
77
|
secure: process.env.NODE_ENV === "production"
|
|
@@ -455,24 +87,24 @@ var lucia = new import_lucia.Lucia(adapter, {
|
|
|
455
87
|
})
|
|
456
88
|
});
|
|
457
89
|
function generateID(length = ID_LENGTH) {
|
|
458
|
-
return
|
|
90
|
+
return generateId(length);
|
|
459
91
|
}
|
|
460
92
|
async function invalidateSession(id) {
|
|
461
93
|
const cookie = lucia.createBlankSessionCookie();
|
|
462
|
-
|
|
94
|
+
cookies().set(cookie.name, cookie.value, cookie.attributes);
|
|
463
95
|
return lucia.invalidateSession(id);
|
|
464
96
|
}
|
|
465
97
|
async function invalidateUserSessions(id) {
|
|
466
98
|
return lucia.invalidateUserSessions(id);
|
|
467
99
|
}
|
|
468
|
-
async function createUserSession(id,
|
|
469
|
-
const session = await lucia.createSession(id, { scope
|
|
100
|
+
async function createUserSession(id, scope = "ANON") {
|
|
101
|
+
const session = await lucia.createSession(id, { scope });
|
|
470
102
|
const sessionCookie = lucia.createSessionCookie(session.id);
|
|
471
|
-
|
|
103
|
+
cookies().set(sessionCookie.name, sessionCookie.value, sessionCookie.attributes);
|
|
472
104
|
return true;
|
|
473
105
|
}
|
|
474
106
|
function getSessionID() {
|
|
475
|
-
return
|
|
107
|
+
return cookies().get(lucia.sessionCookieName)?.value ?? null;
|
|
476
108
|
}
|
|
477
109
|
function checkSessionExists() {
|
|
478
110
|
return Boolean(getSessionID());
|
|
@@ -486,7 +118,7 @@ async function getSessionUser() {
|
|
|
486
118
|
if (!user?.role || !getAllowedRoles().includes(user.role)) {
|
|
487
119
|
return null;
|
|
488
120
|
}
|
|
489
|
-
return
|
|
121
|
+
return getFromObject(user, ["id", "email", "role"]);
|
|
490
122
|
}
|
|
491
123
|
function checkRouteAllowed(pathname, route) {
|
|
492
124
|
if (!route) {
|
|
@@ -495,7 +127,7 @@ function checkRouteAllowed(pathname, route) {
|
|
|
495
127
|
if (route === "*") {
|
|
496
128
|
return true;
|
|
497
129
|
}
|
|
498
|
-
return
|
|
130
|
+
return match(route)(pathname) !== false;
|
|
499
131
|
}
|
|
500
132
|
async function getScopes() {
|
|
501
133
|
const scopes = await getFromCache(`${getOrigin()}:scopes`);
|
|
@@ -525,14 +157,14 @@ async function setScopes(customScopes) {
|
|
|
525
157
|
async function validateSessionFromID(sessionID, pathname) {
|
|
526
158
|
const scopes = await getScopes();
|
|
527
159
|
const { session, user } = await lucia.validateSession(sessionID);
|
|
528
|
-
const
|
|
529
|
-
return checkRouteAllowed(pathname,
|
|
160
|
+
const scope = scopes[session && getAllowedRoles().includes(user?.role) ? session.scope : "ANON"];
|
|
161
|
+
return checkRouteAllowed(pathname, scope.allowedRoute) ? null : scope.redirectOnUnauth || DEFAULT_REDIRECT;
|
|
530
162
|
}
|
|
531
163
|
async function handleSession(request, customScopes) {
|
|
532
164
|
const sessionID = request.nextUrl.searchParams.get("id") || "";
|
|
533
165
|
const pathname = request.nextUrl.searchParams.get("pathname") || "/";
|
|
534
166
|
await setScopes(customScopes);
|
|
535
|
-
return
|
|
167
|
+
return NextResponse.json({
|
|
536
168
|
redirect: await validateSessionFromID(sessionID, pathname)
|
|
537
169
|
});
|
|
538
170
|
}
|
|
@@ -545,13 +177,13 @@ async function generateMFA(name, email) {
|
|
|
545
177
|
if (!checkMFAEnabled() || !email) {
|
|
546
178
|
return null;
|
|
547
179
|
}
|
|
548
|
-
const [user] = await db.select().from(authUserTable).where(
|
|
180
|
+
const [user] = await db.select().from(authUserTable).where(eq(authUserTable.email, email)).limit(1);
|
|
549
181
|
if (!user) {
|
|
550
182
|
return null;
|
|
551
183
|
}
|
|
552
|
-
const secret =
|
|
553
|
-
const otpauth =
|
|
554
|
-
await db.delete(authMFATable).where(
|
|
184
|
+
const secret = authenticator.generateSecret();
|
|
185
|
+
const otpauth = authenticator.keyuri(email, name, secret);
|
|
186
|
+
await db.delete(authMFATable).where(and(eq(authMFATable.userId, user.id), isNull(authMFATable.verifiedAt)));
|
|
555
187
|
await db.insert(authMFATable).values({
|
|
556
188
|
id: generateID(),
|
|
557
189
|
name: "Default",
|
|
@@ -559,7 +191,7 @@ async function generateMFA(name, email) {
|
|
|
559
191
|
userId: user.id
|
|
560
192
|
});
|
|
561
193
|
return new Promise((resolve, reject) => {
|
|
562
|
-
|
|
194
|
+
qrcode.toDataURL(
|
|
563
195
|
otpauth,
|
|
564
196
|
{ rendererOpts: { quality: 1 }, margin: 0, scale: 2 },
|
|
565
197
|
(err, data) => {
|
|
@@ -575,24 +207,24 @@ async function checkUserHasMFA(user) {
|
|
|
575
207
|
if (!checkMFAEnabled()) {
|
|
576
208
|
return false;
|
|
577
209
|
}
|
|
578
|
-
const [mfa] = await db.select().from(authMFATable).where(
|
|
210
|
+
const [mfa] = await db.select().from(authMFATable).where(and(eq(authMFATable.userId, user.id), isNotNull(authMFATable.verifiedAt))).limit(1);
|
|
579
211
|
return Boolean(mfa);
|
|
580
212
|
}
|
|
581
213
|
async function markAsVerified(userID) {
|
|
582
214
|
if (!checkMFAEnabled()) {
|
|
583
215
|
return;
|
|
584
216
|
}
|
|
585
|
-
await db.update(authMFATable).set({ verifiedAt: /* @__PURE__ */ new Date() }).where(
|
|
217
|
+
await db.update(authMFATable).set({ verifiedAt: /* @__PURE__ */ new Date() }).where(eq(authMFATable.userId, userID));
|
|
586
218
|
}
|
|
587
219
|
async function validateUserToken(userID, token) {
|
|
588
220
|
if (!checkMFAEnabled()) {
|
|
589
221
|
return false;
|
|
590
222
|
}
|
|
591
|
-
const [mfa] = await db.select().from(authMFATable).where(
|
|
223
|
+
const [mfa] = await db.select().from(authMFATable).where(eq(authMFATable.userId, userID)).limit(1);
|
|
592
224
|
if (!mfa) {
|
|
593
225
|
return false;
|
|
594
226
|
}
|
|
595
|
-
return
|
|
227
|
+
return authenticator.check(token, mfa.secret);
|
|
596
228
|
}
|
|
597
229
|
async function handleMFA(formData) {
|
|
598
230
|
if (!checkMFAEnabled()) {
|
|
@@ -622,7 +254,7 @@ async function handleMFAForm(formData) {
|
|
|
622
254
|
}
|
|
623
255
|
|
|
624
256
|
// src/auth/PasswordService.ts
|
|
625
|
-
|
|
257
|
+
import bcrypt from "bcryptjs";
|
|
626
258
|
var PW_SALT_ROUNDS = 12;
|
|
627
259
|
var PASSWORD_RULES = {
|
|
628
260
|
min: 8,
|
|
@@ -654,14 +286,14 @@ var PASSWORD_FUNCTIONS = {
|
|
|
654
286
|
symbol: checkPasswordSymbol
|
|
655
287
|
};
|
|
656
288
|
async function hashPassword(password) {
|
|
657
|
-
const hash = await
|
|
289
|
+
const hash = await bcrypt.hash(password, PW_SALT_ROUNDS);
|
|
658
290
|
return hash;
|
|
659
291
|
}
|
|
660
292
|
async function verifyPassword(data, encrypted) {
|
|
661
293
|
if (!data || !encrypted) {
|
|
662
294
|
return false;
|
|
663
295
|
}
|
|
664
|
-
const verified = await
|
|
296
|
+
const verified = await bcrypt.compare(data, encrypted);
|
|
665
297
|
return verified;
|
|
666
298
|
}
|
|
667
299
|
async function getPasswordComplexity(password, rules = PASSWORD_RULES) {
|
|
@@ -678,17 +310,17 @@ async function checkPasswordComplexity(password, rules = PASSWORD_RULES) {
|
|
|
678
310
|
}
|
|
679
311
|
|
|
680
312
|
// src/auth/LoginRequest.ts
|
|
681
|
-
|
|
313
|
+
import Joi2 from "joi";
|
|
682
314
|
var LoginRequest = createSchema({
|
|
683
|
-
email:
|
|
684
|
-
password:
|
|
315
|
+
email: Joi2.string().email({ minDomainSegments: 2, tlds: false }).required(),
|
|
316
|
+
password: Joi2.string().min(8).required()
|
|
685
317
|
});
|
|
686
318
|
var LoginRequest_default = LoginRequest;
|
|
687
319
|
|
|
688
320
|
// src/auth/PasswordRequest.ts
|
|
689
|
-
|
|
321
|
+
import Joi3 from "joi";
|
|
690
322
|
var PasswordRequest = createSchema({
|
|
691
|
-
email:
|
|
323
|
+
email: Joi3.string().max(60).email({ minDomainSegments: 2, tlds: false }).required().messages({
|
|
692
324
|
"any.required": "Please provide your email address, so we can send you a reset link",
|
|
693
325
|
"string.empty": "Please provide your email address, so we can send you a reset link",
|
|
694
326
|
"string.email": "Please make sure your email address is valid",
|
|
@@ -698,10 +330,10 @@ var PasswordRequest = createSchema({
|
|
|
698
330
|
var PasswordRequest_default = PasswordRequest;
|
|
699
331
|
|
|
700
332
|
// src/auth/PasswordResetRequest.ts
|
|
701
|
-
|
|
333
|
+
import Joi4 from "joi";
|
|
702
334
|
var PasswordResetRequest = createSchema({
|
|
703
|
-
token:
|
|
704
|
-
password:
|
|
335
|
+
token: Joi4.string().pattern(/[a-z0-9]{40}/u).required(),
|
|
336
|
+
password: Joi4.string().required().messages({
|
|
705
337
|
"any.required": "Please provide your new password",
|
|
706
338
|
"string.empty": "Please provide your new password"
|
|
707
339
|
})
|
|
@@ -726,11 +358,11 @@ function getAllowedRoles() {
|
|
|
726
358
|
}
|
|
727
359
|
async function handleUserSession(userID) {
|
|
728
360
|
await createUserSession(userID, checkMFAEnabled() ? "MFA" : "AUTHED");
|
|
729
|
-
const
|
|
730
|
-
return
|
|
361
|
+
const scope = await getScopeByID("AUTHED");
|
|
362
|
+
return scope?.redirectOnAuth || null;
|
|
731
363
|
}
|
|
732
364
|
async function getUserByEmail(email) {
|
|
733
|
-
const [user] = await db.select().from(authUserTable).where((
|
|
365
|
+
const [user] = await db.select().from(authUserTable).where(and2(eq2(authUserTable.email, email), inArray(authUserTable.role, getAllowedRoles()))).limit(1);
|
|
734
366
|
return user;
|
|
735
367
|
}
|
|
736
368
|
async function loginUser({ email, password }) {
|
|
@@ -766,7 +398,7 @@ async function createPasswordResetToken(email) {
|
|
|
766
398
|
if (!user) {
|
|
767
399
|
return null;
|
|
768
400
|
}
|
|
769
|
-
await db.delete(authResetTable).where((
|
|
401
|
+
await db.delete(authResetTable).where(eq2(authResetTable.userId, user.id));
|
|
770
402
|
const id = generateID(RESET_TOKEN_LENGTH);
|
|
771
403
|
await db.insert(authResetTable).values({
|
|
772
404
|
id,
|
|
@@ -791,17 +423,17 @@ async function handlePasswordForm(formData, mailFn) {
|
|
|
791
423
|
return response;
|
|
792
424
|
}
|
|
793
425
|
async function validatePasswordResetToken(password, token) {
|
|
794
|
-
const [result] = await db.select().from(authResetTable).where((
|
|
426
|
+
const [result] = await db.select().from(authResetTable).where(eq2(authResetTable.id, token)).limit(1);
|
|
795
427
|
if (!result) {
|
|
796
428
|
return null;
|
|
797
429
|
}
|
|
798
|
-
await db.delete(authResetTable).where((
|
|
430
|
+
await db.delete(authResetTable).where(eq2(authResetTable.id, token));
|
|
799
431
|
if (!result || result.expiresAt < /* @__PURE__ */ new Date()) {
|
|
800
432
|
return null;
|
|
801
433
|
}
|
|
802
434
|
await invalidateUserSessions(result.userId);
|
|
803
435
|
await db.update(authUserTable).set({ password: await hashPassword(password) }).where(
|
|
804
|
-
(
|
|
436
|
+
and2(eq2(authUserTable.id, result.userId), inArray(authUserTable.role, getAllowedRoles()))
|
|
805
437
|
);
|
|
806
438
|
return result.userId;
|
|
807
439
|
}
|
|
@@ -822,11 +454,11 @@ async function handlePasswordResetForm(formData) {
|
|
|
822
454
|
}
|
|
823
455
|
|
|
824
456
|
// src/auth/ClientService.ts
|
|
825
|
-
|
|
457
|
+
import { eq as eq3 } from "drizzle-orm";
|
|
826
458
|
var ID_LENGTH2 = 16;
|
|
827
459
|
var SECRET_LENGTH = 64;
|
|
828
460
|
async function getClientByID(id) {
|
|
829
|
-
const [client] = await db.select().from(authClientTable).where((
|
|
461
|
+
const [client] = await db.select().from(authClientTable).where(eq3(authClientTable.id, id)).limit(1);
|
|
830
462
|
return client;
|
|
831
463
|
}
|
|
832
464
|
async function registerClient({
|
|
@@ -842,22 +474,21 @@ async function registerClient({
|
|
|
842
474
|
return client;
|
|
843
475
|
}
|
|
844
476
|
async function handleClientAuth(request) {
|
|
845
|
-
const { headers
|
|
846
|
-
const header =
|
|
477
|
+
const { headers } = request;
|
|
478
|
+
const header = headers.get("authorization");
|
|
847
479
|
if (!header) {
|
|
848
480
|
return null;
|
|
849
481
|
}
|
|
850
482
|
const auth = Buffer.from(header.replace("Basic ", ""), "base64").toString("utf-8").replace(/:$/u, "");
|
|
851
483
|
const [id, ...secret] = auth.split("-");
|
|
852
|
-
const [client] = await db.select().from(authClientTable).where((
|
|
484
|
+
const [client] = await db.select().from(authClientTable).where(eq3(authClientTable.id, id)).limit(1);
|
|
853
485
|
if (!client) {
|
|
854
486
|
return null;
|
|
855
487
|
}
|
|
856
488
|
const isVerified = await verifyPassword(secret.join(""), client.secret);
|
|
857
489
|
return isVerified ? client : null;
|
|
858
490
|
}
|
|
859
|
-
|
|
860
|
-
0 && (module.exports = {
|
|
491
|
+
export {
|
|
861
492
|
checkMFAEnabled,
|
|
862
493
|
checkPasswordComplexity,
|
|
863
494
|
checkRouteAllowed,
|
|
@@ -888,4 +519,4 @@ async function handleClientAuth(request) {
|
|
|
888
519
|
registerUser,
|
|
889
520
|
setScopes,
|
|
890
521
|
verifyPassword
|
|
891
|
-
}
|
|
522
|
+
};
|