@arch-cadre/core 0.0.41 → 0.0.43
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/core/auth/augment.js +6 -14
- package/dist/core/auth/email-verification.js +33 -43
- package/dist/core/auth/events.js +1 -2
- package/dist/core/auth/logic.js +71 -90
- package/dist/core/auth/password-reset.js +46 -56
- package/dist/core/auth/rbac.js +73 -90
- package/dist/core/auth/session.js +51 -66
- package/dist/core/auth/types.js +1 -2
- package/dist/core/auth/utils/encode.js +5 -9
- package/dist/core/auth/utils/encryption.js +12 -18
- package/dist/core/auth/validation.js +25 -28
- package/dist/core/bootstrap.js +16 -19
- package/dist/core/config.js +1 -4
- package/dist/core/config.server.js +14 -54
- package/dist/core/event-bus.js +2 -5
- package/dist/core/filesystem/index.js +7 -24
- package/dist/core/filesystem/providers/local.js +7 -14
- package/dist/core/filesystem/service.js +2 -5
- package/dist/core/filesystem/types.js +1 -2
- package/dist/core/notifications/actions.js +22 -28
- package/dist/core/notifications/index.js +3 -19
- package/dist/core/notifications/service.js +6 -9
- package/dist/core/notifications/types.js +1 -2
- package/dist/core/setup.js +7 -10
- package/dist/core/types.js +1 -2
- package/dist/index.js +7 -23
- package/dist/server/auth/email.js +9 -13
- package/dist/server/auth/password.js +6 -14
- package/dist/server/auth/types.js +1 -17
- package/dist/server/auth/user.js +76 -91
- package/dist/server/database/inject.js +2 -6
- package/dist/server/database/schema.js +107 -110
- package/dist/server/database/types.js +1 -2
- package/dist/server/emails/index.js +4 -10
- package/dist/server.js +24 -40
- package/package.json +2 -4
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var _a, _b, _c;
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.registerIdentityAugmenter = registerIdentityAugmenter;
|
|
5
|
-
exports.registerSessionAugmenter = registerSessionAugmenter;
|
|
6
|
-
exports.registerPasswordResetSessionAugmenter = registerPasswordResetSessionAugmenter;
|
|
7
|
-
exports.augmentUser = augmentUser;
|
|
8
|
-
exports.augmentSession = augmentSession;
|
|
9
|
-
exports.augmentPasswordResetSession = augmentPasswordResetSession;
|
|
10
2
|
const globalForAugment = globalThis;
|
|
11
3
|
const identityAugmenters = (_a = globalForAugment.__KRYO_IDENTITY_AUGMENTERS__) !== null && _a !== void 0 ? _a : new Set();
|
|
12
4
|
const sessionAugmenters = (_b = globalForAugment.__KRYO_SESSION_AUGMENTERS__) !== null && _b !== void 0 ? _b : new Set();
|
|
@@ -15,19 +7,19 @@ globalForAugment.__KRYO_IDENTITY_AUGMENTERS__ = identityAugmenters;
|
|
|
15
7
|
globalForAugment.__KRYO_SESSION_AUGMENTERS__ = sessionAugmenters;
|
|
16
8
|
globalForAugment.__KRYO_PASSWORD_RESET_SESSION_AUGMENTERS__ =
|
|
17
9
|
passwordResetSessionAugmenters;
|
|
18
|
-
function registerIdentityAugmenter(augmenter) {
|
|
10
|
+
export function registerIdentityAugmenter(augmenter) {
|
|
19
11
|
identityAugmenters.add(augmenter);
|
|
20
12
|
}
|
|
21
|
-
function registerSessionAugmenter(augmenter) {
|
|
13
|
+
export function registerSessionAugmenter(augmenter) {
|
|
22
14
|
sessionAugmenters.add(augmenter);
|
|
23
15
|
}
|
|
24
|
-
function registerPasswordResetSessionAugmenter(augmenter) {
|
|
16
|
+
export function registerPasswordResetSessionAugmenter(augmenter) {
|
|
25
17
|
passwordResetSessionAugmenters.add(augmenter);
|
|
26
18
|
}
|
|
27
19
|
/**
|
|
28
20
|
* EXECUTION FUNCTIONS
|
|
29
21
|
*/
|
|
30
|
-
async function augmentUser(user, coreRbacData) {
|
|
22
|
+
export async function augmentUser(user, coreRbacData) {
|
|
31
23
|
let augmentedData = coreRbacData || {};
|
|
32
24
|
for (const augmenter of identityAugmenters) {
|
|
33
25
|
const data = await augmenter(user);
|
|
@@ -35,7 +27,7 @@ async function augmentUser(user, coreRbacData) {
|
|
|
35
27
|
}
|
|
36
28
|
return { ...user, ...augmentedData };
|
|
37
29
|
}
|
|
38
|
-
async function augmentSession(session) {
|
|
30
|
+
export async function augmentSession(session) {
|
|
39
31
|
let augmentedData = {};
|
|
40
32
|
for (const augmenter of sessionAugmenters) {
|
|
41
33
|
const data = await augmenter(session);
|
|
@@ -43,7 +35,7 @@ async function augmentSession(session) {
|
|
|
43
35
|
}
|
|
44
36
|
return { ...session, ...augmentedData };
|
|
45
37
|
}
|
|
46
|
-
async function augmentPasswordResetSession(session) {
|
|
38
|
+
export async function augmentPasswordResetSession(session) {
|
|
47
39
|
let augmentedData = {};
|
|
48
40
|
for (const augmenter of passwordResetSessionAugmenters) {
|
|
49
41
|
const data = await augmenter(session);
|
|
@@ -1,28 +1,18 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
"use server";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const date_fns_1 = require("date-fns");
|
|
13
|
-
const drizzle_orm_1 = require("drizzle-orm");
|
|
14
|
-
const headers_1 = require("next/headers");
|
|
15
|
-
const inject_1 = require("../../server/database/inject");
|
|
16
|
-
const schema_1 = require("../../server/database/schema");
|
|
17
|
-
const index_1 = require("../../server/emails/index");
|
|
18
|
-
const logic_1 = require("./logic");
|
|
19
|
-
const session_1 = require("./session");
|
|
20
|
-
const encode_1 = require("./utils/encode");
|
|
2
|
+
import { addHours } from "date-fns";
|
|
3
|
+
import { and, eq } from "drizzle-orm";
|
|
4
|
+
import { cookies } from "next/headers";
|
|
5
|
+
import { db } from "../../server/database/inject";
|
|
6
|
+
import { emailVerificationTable } from "../../server/database/schema";
|
|
7
|
+
import { sendVerifyEmail } from "../../server/emails/index";
|
|
8
|
+
import { registerSecurityRequirement } from "./logic";
|
|
9
|
+
import { getCurrentSession } from "./session";
|
|
10
|
+
import { generateRandomOTP } from "./utils/encode";
|
|
21
11
|
/**
|
|
22
12
|
* Register Email Verification as a Core Security Requirement.
|
|
23
13
|
*/
|
|
24
|
-
async function initEmailVerification() {
|
|
25
|
-
|
|
14
|
+
export async function initEmailVerification() {
|
|
15
|
+
registerSecurityRequirement(async (_session, user) => {
|
|
26
16
|
if (!user.emailVerifiedAt) {
|
|
27
17
|
return {
|
|
28
18
|
satisfied: false,
|
|
@@ -35,26 +25,26 @@ async function initEmailVerification() {
|
|
|
35
25
|
/**
|
|
36
26
|
* Retrieves a specific email verification request for a user.
|
|
37
27
|
*/
|
|
38
|
-
async function getUserEmailVerificationRequest(userId, id) {
|
|
39
|
-
const [session] = await
|
|
28
|
+
export async function getUserEmailVerificationRequest(userId, id) {
|
|
29
|
+
const [session] = await db
|
|
40
30
|
.select()
|
|
41
|
-
.from(
|
|
42
|
-
.where(
|
|
31
|
+
.from(emailVerificationTable)
|
|
32
|
+
.where(and(eq(emailVerificationTable.id, id), eq(emailVerificationTable.userId, userId)));
|
|
43
33
|
return session;
|
|
44
34
|
}
|
|
45
35
|
/**
|
|
46
36
|
* Creates a new email verification request, deleting any existing one for the user.
|
|
47
37
|
*/
|
|
48
|
-
async function createEmailVerificationRequest(userId, email) {
|
|
38
|
+
export async function createEmailVerificationRequest(userId, email) {
|
|
49
39
|
await deleteUserEmailVerificationRequest(userId);
|
|
50
|
-
const code =
|
|
51
|
-
const [verificationRequest] = await
|
|
52
|
-
.insert(
|
|
40
|
+
const code = generateRandomOTP();
|
|
41
|
+
const [verificationRequest] = await db
|
|
42
|
+
.insert(emailVerificationTable)
|
|
53
43
|
.values({
|
|
54
44
|
userId,
|
|
55
45
|
code,
|
|
56
46
|
email,
|
|
57
|
-
expiresAt: new Date(
|
|
47
|
+
expiresAt: new Date(addHours(new Date(), 1)),
|
|
58
48
|
})
|
|
59
49
|
.returning();
|
|
60
50
|
return verificationRequest;
|
|
@@ -62,22 +52,22 @@ async function createEmailVerificationRequest(userId, email) {
|
|
|
62
52
|
/**
|
|
63
53
|
* Deletes all email verification requests for a user.
|
|
64
54
|
*/
|
|
65
|
-
async function deleteUserEmailVerificationRequest(userId) {
|
|
66
|
-
await
|
|
67
|
-
.delete(
|
|
68
|
-
.where(
|
|
55
|
+
export async function deleteUserEmailVerificationRequest(userId) {
|
|
56
|
+
await db
|
|
57
|
+
.delete(emailVerificationTable)
|
|
58
|
+
.where(eq(emailVerificationTable.userId, userId));
|
|
69
59
|
}
|
|
70
60
|
/**
|
|
71
61
|
* Sends a verification email with the OTP code.
|
|
72
62
|
*/
|
|
73
|
-
async function sendVerificationEmail(email, code) {
|
|
74
|
-
await
|
|
63
|
+
export async function sendVerificationEmail(email, code) {
|
|
64
|
+
await sendVerifyEmail(email, code);
|
|
75
65
|
}
|
|
76
66
|
/**
|
|
77
67
|
* Sets the email verification request ID in a cookie.
|
|
78
68
|
*/
|
|
79
|
-
async function setEmailVerificationRequestCookie(request) {
|
|
80
|
-
const cookieStore = await
|
|
69
|
+
export async function setEmailVerificationRequestCookie(request) {
|
|
70
|
+
const cookieStore = await cookies();
|
|
81
71
|
cookieStore.set("email_verification", request.id, {
|
|
82
72
|
httpOnly: true,
|
|
83
73
|
path: "/",
|
|
@@ -89,20 +79,20 @@ async function setEmailVerificationRequestCookie(request) {
|
|
|
89
79
|
/**
|
|
90
80
|
* Removes the email verification request cookie.
|
|
91
81
|
*/
|
|
92
|
-
async function deleteEmailVerificationRequestCookie() {
|
|
93
|
-
const cookieStore = await
|
|
82
|
+
export async function deleteEmailVerificationRequestCookie() {
|
|
83
|
+
const cookieStore = await cookies();
|
|
94
84
|
cookieStore.delete("email_verification");
|
|
95
85
|
}
|
|
96
86
|
/**
|
|
97
87
|
* Retrieves the current email verification request based on session and cookie.
|
|
98
88
|
*/
|
|
99
|
-
async function getUserEmailVerificationRequestFromRequest() {
|
|
89
|
+
export async function getUserEmailVerificationRequestFromRequest() {
|
|
100
90
|
var _a, _b;
|
|
101
|
-
const { user } = await
|
|
91
|
+
const { user } = await getCurrentSession();
|
|
102
92
|
if (!user) {
|
|
103
93
|
return null;
|
|
104
94
|
}
|
|
105
|
-
const cookieStore = await
|
|
95
|
+
const cookieStore = await cookies();
|
|
106
96
|
const id = (_b = (_a = cookieStore.get("email_verification")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null;
|
|
107
97
|
if (!id) {
|
|
108
98
|
return null;
|
package/dist/core/auth/events.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/core/auth/logic.js
CHANGED
|
@@ -1,68 +1,48 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
"use server";
|
|
3
2
|
var _a, _b, _c, _d;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
exports.signIn = signIn;
|
|
15
|
-
exports.signUp = signUp;
|
|
16
|
-
exports.finalizeLogin = finalizeLogin;
|
|
17
|
-
exports.signOut = signOut;
|
|
18
|
-
const drizzle_orm_1 = require("drizzle-orm");
|
|
19
|
-
const password_1 = require("../../server/auth/password");
|
|
20
|
-
const user_1 = require("../../server/auth/user");
|
|
21
|
-
const inject_1 = require("../../server/database/inject");
|
|
22
|
-
const schema_1 = require("../../server/database/schema");
|
|
23
|
-
const event_bus_1 = require("../event-bus");
|
|
24
|
-
const augment_1 = require("./augment");
|
|
25
|
-
Object.defineProperty(exports, "augmentSession", { enumerable: true, get: function () { return augment_1.augmentSession; } });
|
|
26
|
-
Object.defineProperty(exports, "augmentUser", { enumerable: true, get: function () { return augment_1.augmentUser; } });
|
|
27
|
-
Object.defineProperty(exports, "registerIdentityAugmenter", { enumerable: true, get: function () { return augment_1.registerIdentityAugmenter; } });
|
|
28
|
-
Object.defineProperty(exports, "registerPasswordResetSessionAugmenter", { enumerable: true, get: function () { return augment_1.registerPasswordResetSessionAugmenter; } });
|
|
29
|
-
Object.defineProperty(exports, "registerSessionAugmenter", { enumerable: true, get: function () { return augment_1.registerSessionAugmenter; } });
|
|
30
|
-
const email_verification_1 = require("./email-verification");
|
|
31
|
-
const session_1 = require("./session");
|
|
32
|
-
const validation_1 = require("./validation");
|
|
3
|
+
import { eq, inArray } from "drizzle-orm";
|
|
4
|
+
import { verifyPasswordHash, verifyPasswordStrength, } from "../../server/auth/password";
|
|
5
|
+
import { createUser, getUserById, getUserFromEmail, getUserPasswordHash, verifyUsernameInput, } from "../../server/auth/user";
|
|
6
|
+
import { db } from "../../server/database/inject";
|
|
7
|
+
import { permissionsTable, rolesTable, rolesToPermissionsTable, usersToPermissionsTable, usersToRolesTable, } from "../../server/database/schema";
|
|
8
|
+
import { eventBus } from "../event-bus";
|
|
9
|
+
import { augmentSession, augmentUser, registerIdentityAugmenter, registerPasswordResetSessionAugmenter, registerSessionAugmenter, } from "./augment";
|
|
10
|
+
import { createEmailVerificationRequest, sendVerificationEmail, setEmailVerificationRequestCookie, } from "./email-verification";
|
|
11
|
+
import { createSession, deleteSessionTokenCookie, generateSessionToken, getCurrentSession, invalidateSession, setSessionTokenCookie, } from "./session";
|
|
12
|
+
import { loginSchema, registerSchema, } from "./validation";
|
|
33
13
|
/**
|
|
34
14
|
* Podstawowy moduł rozszerzający tożsamość dla ról i uprawnień
|
|
35
15
|
*/
|
|
36
16
|
async function coreRbacAugmenter(user) {
|
|
37
17
|
try {
|
|
38
18
|
// 1. Fetch direct roles
|
|
39
|
-
const userRoles = await
|
|
40
|
-
.select({ name:
|
|
41
|
-
.from(
|
|
42
|
-
.innerJoin(
|
|
43
|
-
.where(
|
|
19
|
+
const userRoles = await db
|
|
20
|
+
.select({ name: rolesTable.name })
|
|
21
|
+
.from(usersToRolesTable)
|
|
22
|
+
.innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))
|
|
23
|
+
.where(eq(usersToRolesTable.userId, user.id));
|
|
44
24
|
const roles = userRoles.map((r) => r.name);
|
|
45
25
|
// 2. Fetch direct permissions
|
|
46
|
-
const userDirectPerms = await
|
|
47
|
-
.select({ name:
|
|
48
|
-
.from(
|
|
49
|
-
.innerJoin(
|
|
50
|
-
.where(
|
|
26
|
+
const userDirectPerms = await db
|
|
27
|
+
.select({ name: permissionsTable.name })
|
|
28
|
+
.from(usersToPermissionsTable)
|
|
29
|
+
.innerJoin(permissionsTable, eq(usersToPermissionsTable.permissionId, permissionsTable.id))
|
|
30
|
+
.where(eq(usersToPermissionsTable.userId, user.id));
|
|
51
31
|
const directPerms = userDirectPerms.map((p) => p.name);
|
|
52
32
|
// 3. Fetch permissions from roles
|
|
53
33
|
let rolePerms = [];
|
|
54
34
|
if (roles.length > 0) {
|
|
55
|
-
const roleIdsResult = await
|
|
56
|
-
.select({ id:
|
|
57
|
-
.from(
|
|
58
|
-
.where(
|
|
35
|
+
const roleIdsResult = await db
|
|
36
|
+
.select({ id: rolesTable.id })
|
|
37
|
+
.from(rolesTable)
|
|
38
|
+
.where(inArray(rolesTable.name, roles));
|
|
59
39
|
const roleIds = roleIdsResult.map((r) => r.id);
|
|
60
40
|
if (roleIds.length > 0) {
|
|
61
|
-
const rolePermsData = await
|
|
62
|
-
.select({ name:
|
|
63
|
-
.from(
|
|
64
|
-
.innerJoin(
|
|
65
|
-
.where(
|
|
41
|
+
const rolePermsData = await db
|
|
42
|
+
.select({ name: permissionsTable.name })
|
|
43
|
+
.from(rolesToPermissionsTable)
|
|
44
|
+
.innerJoin(permissionsTable, eq(rolesToPermissionsTable.permissionId, permissionsTable.id))
|
|
45
|
+
.where(inArray(rolesToPermissionsTable.roleId, roleIds));
|
|
66
46
|
rolePerms = rolePermsData.map((p) => p.name);
|
|
67
47
|
}
|
|
68
48
|
}
|
|
@@ -86,19 +66,20 @@ globalForAuth.__KRYO_SECURITY_REQUIREMENTS__ = securityRequirements;
|
|
|
86
66
|
globalForAuth.__KRYO_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;
|
|
87
67
|
globalForAuth.__KRYO_EMAIL_VERIFICATION_VALIDATORS__ =
|
|
88
68
|
emailVerificationValidators;
|
|
89
|
-
async function registerAuthValidator(validator) {
|
|
69
|
+
export async function registerAuthValidator(validator) {
|
|
90
70
|
authValidators.add(validator);
|
|
91
71
|
}
|
|
92
|
-
async function registerPasswordResetValidator(validator) {
|
|
72
|
+
export async function registerPasswordResetValidator(validator) {
|
|
93
73
|
passwordResetValidators.add(validator);
|
|
94
74
|
}
|
|
95
|
-
async function registerEmailVerificationValidator(validator) {
|
|
75
|
+
export async function registerEmailVerificationValidator(validator) {
|
|
96
76
|
emailVerificationValidators.add(validator);
|
|
97
77
|
}
|
|
98
|
-
|
|
78
|
+
export { registerIdentityAugmenter, registerSessionAugmenter, registerPasswordResetSessionAugmenter, augmentUser, augmentSession, };
|
|
79
|
+
export async function registerSecurityRequirement(requirement) {
|
|
99
80
|
securityRequirements.add(requirement);
|
|
100
81
|
}
|
|
101
|
-
async function runPasswordResetValidators(userId) {
|
|
82
|
+
export async function runPasswordResetValidators(userId) {
|
|
102
83
|
for (const validator of passwordResetValidators) {
|
|
103
84
|
const interception = await validator(userId);
|
|
104
85
|
if (interception)
|
|
@@ -106,7 +87,7 @@ async function runPasswordResetValidators(userId) {
|
|
|
106
87
|
}
|
|
107
88
|
return null;
|
|
108
89
|
}
|
|
109
|
-
async function runEmailVerificationValidators(userId) {
|
|
90
|
+
export async function runEmailVerificationValidators(userId) {
|
|
110
91
|
for (const validator of emailVerificationValidators) {
|
|
111
92
|
const interception = await validator(userId);
|
|
112
93
|
if (interception)
|
|
@@ -118,14 +99,14 @@ async function runEmailVerificationValidators(userId) {
|
|
|
118
99
|
* Augments a base user with data from all registered modules.
|
|
119
100
|
* This is now just a wrapper that includes core RBAC data.
|
|
120
101
|
*/
|
|
121
|
-
async function performFullUserAugmentation(user) {
|
|
102
|
+
export async function performFullUserAugmentation(user) {
|
|
122
103
|
const coreRbacData = await coreRbacAugmenter(user);
|
|
123
|
-
return await
|
|
104
|
+
return await augmentUser(user, coreRbacData);
|
|
124
105
|
}
|
|
125
106
|
/**
|
|
126
107
|
* Checks if the current session satisfies all registered security requirements.
|
|
127
108
|
*/
|
|
128
|
-
async function checkSecurity(session, user, requiredRoles, requiredPermissions, fallbackRedirect) {
|
|
109
|
+
export async function checkSecurity(session, user, requiredRoles, requiredPermissions, fallbackRedirect) {
|
|
129
110
|
var _a;
|
|
130
111
|
if (!user) {
|
|
131
112
|
console.warn("User is required for security check");
|
|
@@ -179,14 +160,14 @@ async function checkSecurity(session, user, requiredRoles, requiredPermissions,
|
|
|
179
160
|
/**
|
|
180
161
|
* Sign In Logic
|
|
181
162
|
*/
|
|
182
|
-
async function signIn(data) {
|
|
183
|
-
const { email, password } = await
|
|
184
|
-
const user = await
|
|
163
|
+
export async function signIn(data) {
|
|
164
|
+
const { email, password } = await loginSchema.parseAsync(data);
|
|
165
|
+
const user = await getUserFromEmail(email);
|
|
185
166
|
if (!user) {
|
|
186
167
|
return { status: "ERROR", message: "Invalid email or password" };
|
|
187
168
|
}
|
|
188
|
-
const passwordHash = await
|
|
189
|
-
if (!passwordHash || !(await
|
|
169
|
+
const passwordHash = await getUserPasswordHash(user.id);
|
|
170
|
+
if (!passwordHash || !(await verifyPasswordHash(passwordHash, password))) {
|
|
190
171
|
return { status: "ERROR", message: "Invalid email or password" };
|
|
191
172
|
}
|
|
192
173
|
// Interception Layer
|
|
@@ -196,11 +177,11 @@ async function signIn(data) {
|
|
|
196
177
|
return interception;
|
|
197
178
|
}
|
|
198
179
|
const sessionFlags = {};
|
|
199
|
-
const sessionToken = await
|
|
200
|
-
const session = await
|
|
201
|
-
await
|
|
180
|
+
const sessionToken = await generateSessionToken();
|
|
181
|
+
const session = await createSession(sessionToken, user.id, sessionFlags);
|
|
182
|
+
await setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
202
183
|
const fullUser = await performFullUserAugmentation(user);
|
|
203
|
-
await
|
|
184
|
+
await eventBus.publish("auth:session-created", { session, user: fullUser });
|
|
204
185
|
return {
|
|
205
186
|
status: "SUCCESS",
|
|
206
187
|
session: { ...session },
|
|
@@ -210,24 +191,24 @@ async function signIn(data) {
|
|
|
210
191
|
/**
|
|
211
192
|
* Sign Up Logic
|
|
212
193
|
*/
|
|
213
|
-
async function signUp(data) {
|
|
214
|
-
const { email, username, password } =
|
|
215
|
-
if (!(await
|
|
194
|
+
export async function signUp(data) {
|
|
195
|
+
const { email, username, password } = registerSchema.parse(data);
|
|
196
|
+
if (!(await verifyUsernameInput(username))) {
|
|
216
197
|
throw new Error("Invalid username");
|
|
217
198
|
}
|
|
218
|
-
if (!(await
|
|
199
|
+
if (!(await verifyPasswordStrength(password))) {
|
|
219
200
|
throw new Error("Weak password");
|
|
220
201
|
}
|
|
221
|
-
const user = await
|
|
222
|
-
const verificationRequest = await
|
|
223
|
-
await
|
|
224
|
-
await
|
|
202
|
+
const user = await createUser(email, username, password);
|
|
203
|
+
const verificationRequest = await createEmailVerificationRequest(user.id, user.email);
|
|
204
|
+
await sendVerificationEmail(verificationRequest.email, verificationRequest.code);
|
|
205
|
+
await setEmailVerificationRequestCookie(verificationRequest);
|
|
225
206
|
const sessionFlags = {};
|
|
226
|
-
const sessionToken = await
|
|
227
|
-
const session = await
|
|
228
|
-
await
|
|
207
|
+
const sessionToken = await generateSessionToken();
|
|
208
|
+
const session = await createSession(sessionToken, user.id, sessionFlags);
|
|
209
|
+
await setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
229
210
|
const fullUser = await performFullUserAugmentation(user);
|
|
230
|
-
await
|
|
211
|
+
await eventBus.publish("auth:session-created", { session, user: fullUser });
|
|
231
212
|
return {
|
|
232
213
|
session: { ...session },
|
|
233
214
|
user: { ...fullUser },
|
|
@@ -236,13 +217,13 @@ async function signUp(data) {
|
|
|
236
217
|
/**
|
|
237
218
|
* Finalizes login after a challenge
|
|
238
219
|
*/
|
|
239
|
-
async function finalizeLogin(userId, flags) {
|
|
240
|
-
const sessionToken = await
|
|
241
|
-
const session = await
|
|
242
|
-
await
|
|
243
|
-
const user = await
|
|
220
|
+
export async function finalizeLogin(userId, flags) {
|
|
221
|
+
const sessionToken = await generateSessionToken();
|
|
222
|
+
const session = await createSession(sessionToken, userId, flags);
|
|
223
|
+
await setSessionTokenCookie(sessionToken, session.expiresAt);
|
|
224
|
+
const user = await getUserById(userId);
|
|
244
225
|
if (user) {
|
|
245
|
-
await
|
|
226
|
+
await eventBus.publish("auth:session-created", { session, user });
|
|
246
227
|
}
|
|
247
228
|
return {
|
|
248
229
|
session: session ? { ...session } : null,
|
|
@@ -252,13 +233,13 @@ async function finalizeLogin(userId, flags) {
|
|
|
252
233
|
/**
|
|
253
234
|
* Sign Out
|
|
254
235
|
*/
|
|
255
|
-
async function signOut() {
|
|
256
|
-
const { session, user } = await
|
|
236
|
+
export async function signOut() {
|
|
237
|
+
const { session, user } = await getCurrentSession();
|
|
257
238
|
if (session) {
|
|
258
239
|
if (user) {
|
|
259
|
-
await
|
|
240
|
+
await eventBus.publish("auth:signed-out", { userId: user.id });
|
|
260
241
|
}
|
|
261
|
-
await
|
|
262
|
-
await
|
|
242
|
+
await invalidateSession(session.id);
|
|
243
|
+
await deleteSessionTokenCookie();
|
|
263
244
|
}
|
|
264
245
|
}
|
|
@@ -1,37 +1,27 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
"use server";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const date_fns_1 = require("date-fns");
|
|
15
|
-
const drizzle_orm_1 = require("drizzle-orm");
|
|
16
|
-
const headers_1 = require("next/headers");
|
|
17
|
-
const inject_1 = require("../../server/database/inject");
|
|
18
|
-
const schema_1 = require("../../server/database/schema");
|
|
19
|
-
const index_1 = require("../../server/emails/index");
|
|
20
|
-
const augment_1 = require("./augment");
|
|
21
|
-
const logic_1 = require("./logic");
|
|
22
|
-
const encode_1 = require("./utils/encode");
|
|
2
|
+
import { sha256 } from "@oslojs/crypto/sha2";
|
|
3
|
+
import { encodeHexLowerCase } from "@oslojs/encoding";
|
|
4
|
+
import { addHours } from "date-fns";
|
|
5
|
+
import { eq } from "drizzle-orm";
|
|
6
|
+
import { cookies } from "next/headers";
|
|
7
|
+
import { db } from "../../server/database/inject";
|
|
8
|
+
import { passwordResetSessionTable, userTable, } from "../../server/database/schema";
|
|
9
|
+
import { sendResetPassword } from "../../server/emails/index";
|
|
10
|
+
import { augmentPasswordResetSession } from "./augment";
|
|
11
|
+
import { performFullUserAugmentation } from "./logic";
|
|
12
|
+
import { generateRandomOTP } from "./utils/encode";
|
|
23
13
|
/**
|
|
24
14
|
* Creates a new password reset session.
|
|
25
15
|
*/
|
|
26
|
-
async function createPasswordResetSession(token, userId, email) {
|
|
27
|
-
const sessionId =
|
|
28
|
-
const [session] = await
|
|
29
|
-
.insert(
|
|
16
|
+
export async function createPasswordResetSession(token, userId, email) {
|
|
17
|
+
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
|
18
|
+
const [session] = await db
|
|
19
|
+
.insert(passwordResetSessionTable)
|
|
30
20
|
.values({
|
|
31
21
|
id: sessionId,
|
|
32
22
|
email: email,
|
|
33
|
-
code:
|
|
34
|
-
expiresAt: new Date(
|
|
23
|
+
code: generateRandomOTP(),
|
|
24
|
+
expiresAt: new Date(addHours(new Date(), 1)),
|
|
35
25
|
userId: userId,
|
|
36
26
|
})
|
|
37
27
|
.returning();
|
|
@@ -41,59 +31,59 @@ async function createPasswordResetSession(token, userId, email) {
|
|
|
41
31
|
* Validates the password reset session token and retrieves user data.
|
|
42
32
|
* The user data is augmented by registered modules (e.g. 2FA).
|
|
43
33
|
*/
|
|
44
|
-
async function validatePasswordResetSessionToken(token) {
|
|
45
|
-
const sessionId =
|
|
46
|
-
const [row] = await
|
|
34
|
+
export async function validatePasswordResetSessionToken(token) {
|
|
35
|
+
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
|
36
|
+
const [row] = await db
|
|
47
37
|
.select({
|
|
48
|
-
session:
|
|
49
|
-
user:
|
|
38
|
+
session: passwordResetSessionTable,
|
|
39
|
+
user: userTable,
|
|
50
40
|
})
|
|
51
|
-
.from(
|
|
52
|
-
.innerJoin(
|
|
53
|
-
.where(
|
|
41
|
+
.from(passwordResetSessionTable)
|
|
42
|
+
.innerJoin(userTable, eq(passwordResetSessionTable.userId, userTable.id))
|
|
43
|
+
.where(eq(passwordResetSessionTable.id, sessionId));
|
|
54
44
|
if (!row || !row.user) {
|
|
55
45
|
return { session: null, user: null };
|
|
56
46
|
}
|
|
57
47
|
const { session: baseSession, user: baseUser } = row;
|
|
58
48
|
// Check for expiration
|
|
59
49
|
if (new Date() > baseSession.expiresAt) {
|
|
60
|
-
await
|
|
61
|
-
.delete(
|
|
62
|
-
.where(
|
|
50
|
+
await db
|
|
51
|
+
.delete(passwordResetSessionTable)
|
|
52
|
+
.where(eq(passwordResetSessionTable.id, baseSession.id));
|
|
63
53
|
return { session: null, user: null };
|
|
64
54
|
}
|
|
65
55
|
// STRICTLY remove non-serializable and sensitive fields
|
|
66
56
|
const { password, recovery_code, ...safeUser } = baseUser;
|
|
67
57
|
// AUGMENT (EXTENSIBILITY POINTS)
|
|
68
|
-
const user = await
|
|
69
|
-
const session = await
|
|
58
|
+
const user = await performFullUserAugmentation(safeUser);
|
|
59
|
+
const session = await augmentPasswordResetSession(baseSession);
|
|
70
60
|
return { session, user };
|
|
71
61
|
}
|
|
72
62
|
/**
|
|
73
63
|
* Marks the password reset session as email verified.
|
|
74
64
|
*/
|
|
75
|
-
async function setPasswordResetSessionAsEmailVerified(sessionId) {
|
|
76
|
-
await
|
|
77
|
-
.update(
|
|
65
|
+
export async function setPasswordResetSessionAsEmailVerified(sessionId) {
|
|
66
|
+
await db
|
|
67
|
+
.update(passwordResetSessionTable)
|
|
78
68
|
.set({
|
|
79
69
|
emailVerified: true,
|
|
80
70
|
})
|
|
81
|
-
.where(
|
|
71
|
+
.where(eq(passwordResetSessionTable.id, sessionId));
|
|
82
72
|
}
|
|
83
73
|
/**
|
|
84
74
|
* Invalidates all password reset sessions for a user.
|
|
85
75
|
*/
|
|
86
|
-
async function invalidateUserPasswordResetSessions(userId) {
|
|
87
|
-
await
|
|
88
|
-
.delete(
|
|
89
|
-
.where(
|
|
76
|
+
export async function invalidateUserPasswordResetSessions(userId) {
|
|
77
|
+
await db
|
|
78
|
+
.delete(passwordResetSessionTable)
|
|
79
|
+
.where(eq(passwordResetSessionTable.userId, userId));
|
|
90
80
|
}
|
|
91
81
|
/**
|
|
92
82
|
* Validates the current password reset session from cookies.
|
|
93
83
|
*/
|
|
94
|
-
async function getCurrentPasswordResetSession() {
|
|
84
|
+
export async function getCurrentPasswordResetSession() {
|
|
95
85
|
var _a, _b;
|
|
96
|
-
const cookieStore = await
|
|
86
|
+
const cookieStore = await cookies();
|
|
97
87
|
const token = (_b = (_a = cookieStore.get("password_reset_session")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null;
|
|
98
88
|
if (token === null) {
|
|
99
89
|
return { session: null, user: null };
|
|
@@ -107,8 +97,8 @@ async function getCurrentPasswordResetSession() {
|
|
|
107
97
|
/**
|
|
108
98
|
* Sets the password reset session token cookie.
|
|
109
99
|
*/
|
|
110
|
-
async function setPasswordResetSessionTokenCookie(token, expiresAt) {
|
|
111
|
-
const cookieStore = await
|
|
100
|
+
export async function setPasswordResetSessionTokenCookie(token, expiresAt) {
|
|
101
|
+
const cookieStore = await cookies();
|
|
112
102
|
cookieStore.set("password_reset_session", token, {
|
|
113
103
|
expires: expiresAt,
|
|
114
104
|
sameSite: "lax",
|
|
@@ -120,13 +110,13 @@ async function setPasswordResetSessionTokenCookie(token, expiresAt) {
|
|
|
120
110
|
/**
|
|
121
111
|
* Deletes the password reset session token cookie.
|
|
122
112
|
*/
|
|
123
|
-
async function deletePasswordResetSessionTokenCookie() {
|
|
124
|
-
const cookieStore = await
|
|
113
|
+
export async function deletePasswordResetSessionTokenCookie() {
|
|
114
|
+
const cookieStore = await cookies();
|
|
125
115
|
cookieStore.delete("password_reset_session");
|
|
126
116
|
}
|
|
127
117
|
/**
|
|
128
118
|
* Sends a password reset email with the OTP code.
|
|
129
119
|
*/
|
|
130
|
-
async function sendPasswordResetEmail(email, code) {
|
|
131
|
-
await
|
|
120
|
+
export async function sendPasswordResetEmail(email, code) {
|
|
121
|
+
await sendResetPassword(email, code);
|
|
132
122
|
}
|