@arch-cadre/core 0.0.42 → 0.0.44

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.
Files changed (213) hide show
  1. package/dist/core/auth/augment.d.ts +18 -0
  2. package/dist/core/auth/augment.d.ts.map +1 -0
  3. package/dist/core/auth/augment.js +45 -0
  4. package/dist/core/auth/email-verification.d.ts +58 -0
  5. package/dist/core/auth/email-verification.d.ts.map +1 -0
  6. package/dist/core/auth/email-verification.js +105 -0
  7. package/dist/core/auth/events.d.ts +53 -0
  8. package/dist/core/auth/events.d.ts.map +1 -0
  9. package/dist/core/auth/events.js +1 -0
  10. package/dist/core/auth/logic.d.ts +106 -0
  11. package/dist/core/auth/logic.d.ts.map +1 -0
  12. package/dist/core/auth/logic.js +245 -0
  13. package/dist/core/auth/password-reset.d.ts +35 -0
  14. package/dist/core/auth/password-reset.d.ts.map +1 -0
  15. package/dist/core/auth/password-reset.js +122 -0
  16. package/dist/core/auth/rbac.d.ts +56 -0
  17. package/dist/core/auth/rbac.d.ts.map +1 -0
  18. package/dist/core/auth/rbac.js +134 -0
  19. package/dist/core/auth/session.d.ts +50 -0
  20. package/dist/core/auth/session.d.ts.map +1 -0
  21. package/dist/core/auth/session.js +152 -0
  22. package/dist/core/auth/types.d.ts +52 -0
  23. package/dist/core/auth/types.d.ts.map +1 -0
  24. package/dist/core/auth/types.js +1 -0
  25. package/dist/core/auth/utils/encode.d.ts +12 -0
  26. package/dist/core/auth/utils/encode.d.ts.map +1 -0
  27. package/dist/core/auth/utils/encode.js +20 -0
  28. package/dist/core/auth/utils/{encryption.d.mts → encryption.d.ts} +5 -8
  29. package/dist/core/auth/utils/encryption.d.ts.map +1 -0
  30. package/dist/core/auth/utils/encryption.js +62 -0
  31. package/dist/core/auth/validation.d.ts +44 -0
  32. package/dist/core/auth/validation.d.ts.map +1 -0
  33. package/dist/core/auth/validation.js +41 -0
  34. package/dist/core/bootstrap.d.ts +2 -0
  35. package/dist/core/bootstrap.d.ts.map +1 -0
  36. package/dist/core/bootstrap.js +51 -0
  37. package/dist/core/config.d.ts +9 -0
  38. package/dist/core/config.d.ts.map +1 -0
  39. package/dist/core/config.js +3 -0
  40. package/dist/core/config.server.d.ts +12 -0
  41. package/dist/core/config.server.d.ts.map +1 -0
  42. package/dist/core/config.server.js +61 -0
  43. package/dist/core/event-bus.d.ts +14 -0
  44. package/dist/core/event-bus.d.ts.map +1 -0
  45. package/dist/core/event-bus.js +51 -0
  46. package/dist/core/filesystem/index.d.ts +4 -0
  47. package/dist/core/filesystem/index.d.ts.map +1 -0
  48. package/dist/core/filesystem/index.js +10 -0
  49. package/dist/core/filesystem/providers/local.d.ts +8 -0
  50. package/dist/core/filesystem/providers/local.d.ts.map +1 -0
  51. package/dist/core/filesystem/providers/local.js +42 -0
  52. package/dist/core/filesystem/service.d.ts +16 -0
  53. package/dist/core/filesystem/service.d.ts.map +1 -0
  54. package/dist/core/filesystem/service.js +51 -0
  55. package/dist/core/filesystem/types.d.ts +19 -0
  56. package/dist/core/filesystem/types.d.ts.map +1 -0
  57. package/dist/core/filesystem/types.js +1 -0
  58. package/dist/core/notifications/actions.d.ts +54 -0
  59. package/dist/core/notifications/actions.d.ts.map +1 -0
  60. package/dist/core/notifications/actions.js +43 -0
  61. package/dist/core/notifications/index.d.ts +4 -0
  62. package/dist/core/notifications/index.d.ts.map +1 -0
  63. package/dist/core/notifications/index.js +3 -0
  64. package/dist/core/notifications/service.d.ts +7 -0
  65. package/dist/core/notifications/service.d.ts.map +1 -0
  66. package/dist/core/notifications/service.js +32 -0
  67. package/dist/core/notifications/types.d.ts +17 -0
  68. package/dist/core/notifications/types.d.ts.map +1 -0
  69. package/dist/core/notifications/types.js +1 -0
  70. package/dist/core/setup.d.ts +6 -0
  71. package/dist/core/setup.d.ts.map +1 -0
  72. package/dist/core/setup.js +25 -0
  73. package/dist/core/types.d.ts +10 -0
  74. package/dist/core/types.d.ts.map +1 -0
  75. package/dist/core/types.js +1 -0
  76. package/dist/index.d.ts +8 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +16 -0
  79. package/dist/server/auth/email.d.ts +10 -0
  80. package/dist/server/auth/email.d.ts.map +1 -0
  81. package/dist/server/auth/email.js +20 -0
  82. package/dist/server/auth/{password.d.mts → password.d.ts} +4 -7
  83. package/dist/server/auth/password.d.ts.map +1 -0
  84. package/dist/server/auth/password.js +30 -0
  85. package/dist/server/auth/types.d.ts +13 -0
  86. package/dist/server/auth/types.d.ts.map +1 -0
  87. package/dist/server/auth/types.js +1 -0
  88. package/dist/server/auth/user.d.ts +54 -0
  89. package/dist/server/auth/user.d.ts.map +1 -0
  90. package/dist/server/auth/user.js +222 -0
  91. package/dist/server/database/inject.d.ts +11 -0
  92. package/dist/server/database/inject.d.ts.map +1 -0
  93. package/dist/server/database/inject.js +29 -0
  94. package/dist/server/database/schema.d.ts +2953 -0
  95. package/dist/server/database/{schema.d.mts.map → schema.d.ts.map} +1 -1
  96. package/dist/server/database/schema.js +192 -0
  97. package/dist/server/database/types.d.ts +12 -0
  98. package/dist/server/database/types.d.ts.map +1 -0
  99. package/dist/server/database/types.js +1 -0
  100. package/dist/server/emails/index.d.ts +23 -0
  101. package/dist/server/emails/index.d.ts.map +1 -0
  102. package/dist/server/emails/index.js +67 -0
  103. package/dist/server.d.ts +25 -0
  104. package/dist/server.d.ts.map +1 -0
  105. package/dist/server.js +32 -0
  106. package/package.json +5 -6
  107. package/dist/_virtual/_rolldown/runtime.mjs +0 -1
  108. package/dist/core/auth/augment.d.mts +0 -20
  109. package/dist/core/auth/augment.d.mts.map +0 -1
  110. package/dist/core/auth/augment.mjs +0 -2
  111. package/dist/core/auth/augment.mjs.map +0 -1
  112. package/dist/core/auth/email-verification.d.mts +0 -62
  113. package/dist/core/auth/email-verification.d.mts.map +0 -1
  114. package/dist/core/auth/email-verification.mjs +0 -2
  115. package/dist/core/auth/email-verification.mjs.map +0 -1
  116. package/dist/core/auth/logic.d.mts +0 -110
  117. package/dist/core/auth/logic.d.mts.map +0 -1
  118. package/dist/core/auth/logic.mjs +0 -2
  119. package/dist/core/auth/logic.mjs.map +0 -1
  120. package/dist/core/auth/password-reset.d.mts +0 -39
  121. package/dist/core/auth/password-reset.d.mts.map +0 -1
  122. package/dist/core/auth/password-reset.mjs +0 -2
  123. package/dist/core/auth/password-reset.mjs.map +0 -1
  124. package/dist/core/auth/rbac.d.mts +0 -61
  125. package/dist/core/auth/rbac.d.mts.map +0 -1
  126. package/dist/core/auth/rbac.mjs +0 -2
  127. package/dist/core/auth/rbac.mjs.map +0 -1
  128. package/dist/core/auth/session.d.mts +0 -54
  129. package/dist/core/auth/session.d.mts.map +0 -1
  130. package/dist/core/auth/session.mjs +0 -2
  131. package/dist/core/auth/session.mjs.map +0 -1
  132. package/dist/core/auth/types.d.mts +0 -55
  133. package/dist/core/auth/types.d.mts.map +0 -1
  134. package/dist/core/auth/utils/encode.d.mts +0 -15
  135. package/dist/core/auth/utils/encode.d.mts.map +0 -1
  136. package/dist/core/auth/utils/encode.mjs +0 -2
  137. package/dist/core/auth/utils/encode.mjs.map +0 -1
  138. package/dist/core/auth/utils/encryption.d.mts.map +0 -1
  139. package/dist/core/auth/utils/encryption.mjs +0 -2
  140. package/dist/core/auth/utils/encryption.mjs.map +0 -1
  141. package/dist/core/auth/validation.d.mts +0 -48
  142. package/dist/core/auth/validation.d.mts.map +0 -1
  143. package/dist/core/auth/validation.mjs +0 -2
  144. package/dist/core/auth/validation.mjs.map +0 -1
  145. package/dist/core/bootstrap.d.mts +0 -5
  146. package/dist/core/bootstrap.d.mts.map +0 -1
  147. package/dist/core/bootstrap.mjs +0 -2
  148. package/dist/core/bootstrap.mjs.map +0 -1
  149. package/dist/core/config.d.mts +0 -11
  150. package/dist/core/config.d.mts.map +0 -1
  151. package/dist/core/config.mjs +0 -2
  152. package/dist/core/config.mjs.map +0 -1
  153. package/dist/core/config.server.d.mts +0 -16
  154. package/dist/core/config.server.d.mts.map +0 -1
  155. package/dist/core/config.server.mjs +0 -2
  156. package/dist/core/config.server.mjs.map +0 -1
  157. package/dist/core/event-bus.d.mts +0 -17
  158. package/dist/core/event-bus.d.mts.map +0 -1
  159. package/dist/core/event-bus.mjs +0 -2
  160. package/dist/core/event-bus.mjs.map +0 -1
  161. package/dist/core/filesystem/index.mjs +0 -2
  162. package/dist/core/filesystem/index.mjs.map +0 -1
  163. package/dist/core/filesystem/providers/local.mjs +0 -2
  164. package/dist/core/filesystem/providers/local.mjs.map +0 -1
  165. package/dist/core/filesystem/service.d.mts +0 -19
  166. package/dist/core/filesystem/service.d.mts.map +0 -1
  167. package/dist/core/filesystem/service.mjs +0 -2
  168. package/dist/core/filesystem/service.mjs.map +0 -1
  169. package/dist/core/filesystem/types.d.mts +0 -22
  170. package/dist/core/filesystem/types.d.mts.map +0 -1
  171. package/dist/core/notifications/actions.d.mts +0 -58
  172. package/dist/core/notifications/actions.d.mts.map +0 -1
  173. package/dist/core/notifications/actions.mjs +0 -2
  174. package/dist/core/notifications/actions.mjs.map +0 -1
  175. package/dist/core/notifications/index.mjs +0 -1
  176. package/dist/core/notifications/service.d.mts +0 -9
  177. package/dist/core/notifications/service.d.mts.map +0 -1
  178. package/dist/core/notifications/service.mjs +0 -2
  179. package/dist/core/notifications/service.mjs.map +0 -1
  180. package/dist/core/notifications/types.d.mts +0 -21
  181. package/dist/core/notifications/types.d.mts.map +0 -1
  182. package/dist/core/setup.d.mts +0 -9
  183. package/dist/core/setup.d.mts.map +0 -1
  184. package/dist/core/setup.mjs +0 -2
  185. package/dist/core/setup.mjs.map +0 -1
  186. package/dist/core/types.d.mts +0 -13
  187. package/dist/core/types.d.mts.map +0 -1
  188. package/dist/index.d.mts +0 -8
  189. package/dist/index.mjs +0 -1
  190. package/dist/server/auth/email.d.mts +0 -13
  191. package/dist/server/auth/email.d.mts.map +0 -1
  192. package/dist/server/auth/email.mjs +0 -2
  193. package/dist/server/auth/email.mjs.map +0 -1
  194. package/dist/server/auth/password.d.mts.map +0 -1
  195. package/dist/server/auth/password.mjs +0 -2
  196. package/dist/server/auth/password.mjs.map +0 -1
  197. package/dist/server/auth/user.d.mts +0 -58
  198. package/dist/server/auth/user.d.mts.map +0 -1
  199. package/dist/server/auth/user.mjs +0 -2
  200. package/dist/server/auth/user.mjs.map +0 -1
  201. package/dist/server/database/inject.d.mts +0 -15
  202. package/dist/server/database/inject.d.mts.map +0 -1
  203. package/dist/server/database/inject.mjs +0 -2
  204. package/dist/server/database/inject.mjs.map +0 -1
  205. package/dist/server/database/schema.d.mts +0 -2962
  206. package/dist/server/database/schema.mjs +0 -2
  207. package/dist/server/database/schema.mjs.map +0 -1
  208. package/dist/server/emails/index.d.mts +0 -26
  209. package/dist/server/emails/index.d.mts.map +0 -1
  210. package/dist/server/emails/index.mjs +0 -2
  211. package/dist/server/emails/index.mjs.map +0 -1
  212. package/dist/server.d.mts +0 -26
  213. package/dist/server.mjs +0 -1
@@ -0,0 +1,35 @@
1
+ import type { PasswordResetAuthSession, PasswordResetSession } from "./types";
2
+ /**
3
+ * Creates a new password reset session.
4
+ */
5
+ export declare function createPasswordResetSession(token: string, userId: string, email: string): Promise<PasswordResetSession>;
6
+ /**
7
+ * Validates the password reset session token and retrieves user data.
8
+ * The user data is augmented by registered modules (e.g. 2FA).
9
+ */
10
+ export declare function validatePasswordResetSessionToken(token: string): Promise<PasswordResetAuthSession>;
11
+ /**
12
+ * Marks the password reset session as email verified.
13
+ */
14
+ export declare function setPasswordResetSessionAsEmailVerified(sessionId: string): Promise<void>;
15
+ /**
16
+ * Invalidates all password reset sessions for a user.
17
+ */
18
+ export declare function invalidateUserPasswordResetSessions(userId: string): Promise<void>;
19
+ /**
20
+ * Validates the current password reset session from cookies.
21
+ */
22
+ export declare function getCurrentPasswordResetSession(): Promise<PasswordResetAuthSession>;
23
+ /**
24
+ * Sets the password reset session token cookie.
25
+ */
26
+ export declare function setPasswordResetSessionTokenCookie(token: string, expiresAt: Date): Promise<void>;
27
+ /**
28
+ * Deletes the password reset session token cookie.
29
+ */
30
+ export declare function deletePasswordResetSessionTokenCookie(): Promise<void>;
31
+ /**
32
+ * Sends a password reset email with the OTP code.
33
+ */
34
+ export declare function sendPasswordResetEmail(email: string, code: string): Promise<void>;
35
+ //# sourceMappingURL=password-reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-reset.d.ts","sourceRoot":"","sources":["../../../src/core/auth/password-reset.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAG9E;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC,CAe/B;AAED;;;GAGG;AACH,wBAAsB,iCAAiC,CACrD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,CAAC,CAoCnC;AAED;;GAEG;AACH,wBAAsB,sCAAsC,CAC1D,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;GAEG;AACH,wBAAsB,mCAAmC,CACvD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAIf;AAED;;GAEG;AACH,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAexF;AAED;;GAEG;AACH,wBAAsB,kCAAkC,CACtD,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;GAEG;AACH,wBAAsB,qCAAqC,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3E;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -0,0 +1,122 @@
1
+ "use server";
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";
13
+ /**
14
+ * Creates a new password reset session.
15
+ */
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)
20
+ .values({
21
+ id: sessionId,
22
+ email: email,
23
+ code: generateRandomOTP(),
24
+ expiresAt: new Date(addHours(new Date(), 1)),
25
+ userId: userId,
26
+ })
27
+ .returning();
28
+ return session;
29
+ }
30
+ /**
31
+ * Validates the password reset session token and retrieves user data.
32
+ * The user data is augmented by registered modules (e.g. 2FA).
33
+ */
34
+ export async function validatePasswordResetSessionToken(token) {
35
+ const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
36
+ const [row] = await db
37
+ .select({
38
+ session: passwordResetSessionTable,
39
+ user: userTable,
40
+ })
41
+ .from(passwordResetSessionTable)
42
+ .innerJoin(userTable, eq(passwordResetSessionTable.userId, userTable.id))
43
+ .where(eq(passwordResetSessionTable.id, sessionId));
44
+ if (!row || !row.user) {
45
+ return { session: null, user: null };
46
+ }
47
+ const { session: baseSession, user: baseUser } = row;
48
+ // Check for expiration
49
+ if (new Date() > baseSession.expiresAt) {
50
+ await db
51
+ .delete(passwordResetSessionTable)
52
+ .where(eq(passwordResetSessionTable.id, baseSession.id));
53
+ return { session: null, user: null };
54
+ }
55
+ // STRICTLY remove non-serializable and sensitive fields
56
+ const { password, recovery_code, ...safeUser } = baseUser;
57
+ // AUGMENT (EXTENSIBILITY POINTS)
58
+ const user = await performFullUserAugmentation(safeUser);
59
+ const session = await augmentPasswordResetSession(baseSession);
60
+ return { session, user };
61
+ }
62
+ /**
63
+ * Marks the password reset session as email verified.
64
+ */
65
+ export async function setPasswordResetSessionAsEmailVerified(sessionId) {
66
+ await db
67
+ .update(passwordResetSessionTable)
68
+ .set({
69
+ emailVerified: true,
70
+ })
71
+ .where(eq(passwordResetSessionTable.id, sessionId));
72
+ }
73
+ /**
74
+ * Invalidates all password reset sessions for a user.
75
+ */
76
+ export async function invalidateUserPasswordResetSessions(userId) {
77
+ await db
78
+ .delete(passwordResetSessionTable)
79
+ .where(eq(passwordResetSessionTable.userId, userId));
80
+ }
81
+ /**
82
+ * Validates the current password reset session from cookies.
83
+ */
84
+ export async function getCurrentPasswordResetSession() {
85
+ var _a, _b;
86
+ const cookieStore = await cookies();
87
+ const token = (_b = (_a = cookieStore.get("password_reset_session")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null;
88
+ if (token === null) {
89
+ return { session: null, user: null };
90
+ }
91
+ const result = await validatePasswordResetSessionToken(token);
92
+ if (result.session === null) {
93
+ await deletePasswordResetSessionTokenCookie();
94
+ }
95
+ return result;
96
+ }
97
+ /**
98
+ * Sets the password reset session token cookie.
99
+ */
100
+ export async function setPasswordResetSessionTokenCookie(token, expiresAt) {
101
+ const cookieStore = await cookies();
102
+ cookieStore.set("password_reset_session", token, {
103
+ expires: expiresAt,
104
+ sameSite: "lax",
105
+ httpOnly: true,
106
+ path: "/",
107
+ secure: process.env.NODE_ENV === "production",
108
+ });
109
+ }
110
+ /**
111
+ * Deletes the password reset session token cookie.
112
+ */
113
+ export async function deletePasswordResetSessionTokenCookie() {
114
+ const cookieStore = await cookies();
115
+ cookieStore.delete("password_reset_session");
116
+ }
117
+ /**
118
+ * Sends a password reset email with the OTP code.
119
+ */
120
+ export async function sendPasswordResetEmail(email, code) {
121
+ await sendResetPassword(email, code);
122
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * CORE RBAC LOGIC
3
+ * This file handles all database operations for Roles and Permissions.
4
+ */
5
+ export declare function getRoles(): Promise<{
6
+ id: string;
7
+ name: string;
8
+ description: string | null;
9
+ }[]>;
10
+ export declare function getRoleById(roleId: string): Promise<{
11
+ id: string;
12
+ name: string;
13
+ description: string | null;
14
+ }>;
15
+ export declare function createRole(name: string, description?: string): Promise<{
16
+ id: string;
17
+ name: string;
18
+ description: string | null;
19
+ }[]>;
20
+ export declare function deleteRole(roleId: string): Promise<import("pg").QueryResult<never>>;
21
+ export declare function getPermissions(): Promise<{
22
+ id: string;
23
+ name: string;
24
+ description: string | null;
25
+ }[]>;
26
+ export declare function createPermission(name: string, description?: string): Promise<{
27
+ id: string;
28
+ name: string;
29
+ description: string | null;
30
+ }[]>;
31
+ export declare function deletePermission(permissionId: string): Promise<import("pg").QueryResult<never>>;
32
+ export declare function getRolePermissions(roleId: string): Promise<{
33
+ id: string;
34
+ name: string;
35
+ }[]>;
36
+ export declare function assignPermissionToRole(roleId: string, permissionId: string): Promise<import("pg").QueryResult<never>>;
37
+ export declare function revokePermissionFromRole(roleId: string, permissionId: string): Promise<import("pg").QueryResult<never>>;
38
+ export declare function assignRoleToUser(userId: string, roleId: string): Promise<import("pg").QueryResult<never>>;
39
+ export declare function revokeRoleFromUser(userId: string, roleId: string): Promise<import("pg").QueryResult<never>>;
40
+ export declare function assignPermissionToUser(userId: string, permissionId: string): Promise<import("pg").QueryResult<never>>;
41
+ export declare function revokePermissionFromUser(userId: string, permissionId: string): Promise<import("pg").QueryResult<never>>;
42
+ export declare function getUserRbacData(userId: string): Promise<{
43
+ roles: {
44
+ id: string;
45
+ name: string;
46
+ }[];
47
+ directPermissions: {
48
+ id: string;
49
+ name: string;
50
+ }[];
51
+ effectivePermissions: {
52
+ id: string;
53
+ name: string;
54
+ }[];
55
+ }>;
56
+ //# sourceMappingURL=rbac.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbac.d.ts","sourceRoot":"","sources":["../../../src/core/auth/rbac.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AAIH,wBAAsB,QAAQ;;;;KAE7B;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM;;;;GAM/C;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;;;;KAElE;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,4CAE9C;AAID,wBAAsB,cAAc;;;;KAKnC;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;;;;KAKxE;AAED,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,4CAI1D;AAID,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM;;;KAYtD;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,4CAMrB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,4CAUrB;AAID,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,4CAKpE;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,4CAStE;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,4CAMrB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,4CAUrB;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;YAwCP,MAAM;cAAQ,MAAM;;GAUhE"}
@@ -0,0 +1,134 @@
1
+ "use server";
2
+ import { and, eq, inArray } from "drizzle-orm";
3
+ import { db } from "../../server/database/inject";
4
+ import { permissionsTable, rolesTable, rolesToPermissionsTable, usersToPermissionsTable, usersToRolesTable, } from "../../server/database/schema";
5
+ import { notificationService } from "../notifications/index";
6
+ // Ensure notification service is loaded
7
+ if (typeof window === "undefined") {
8
+ notificationService.init();
9
+ }
10
+ /**
11
+ * CORE RBAC LOGIC
12
+ * This file handles all database operations for Roles and Permissions.
13
+ */
14
+ // --- Roles ---
15
+ export async function getRoles() {
16
+ return await db.select().from(rolesTable).orderBy(rolesTable.name);
17
+ }
18
+ export async function getRoleById(roleId) {
19
+ const [role] = await db
20
+ .select()
21
+ .from(rolesTable)
22
+ .where(eq(rolesTable.id, roleId));
23
+ return role;
24
+ }
25
+ export async function createRole(name, description) {
26
+ return await db.insert(rolesTable).values({ name, description }).returning();
27
+ }
28
+ export async function deleteRole(roleId) {
29
+ return await db.delete(rolesTable).where(eq(rolesTable.id, roleId));
30
+ }
31
+ // --- Permissions ---
32
+ export async function getPermissions() {
33
+ return await db
34
+ .select()
35
+ .from(permissionsTable)
36
+ .orderBy(permissionsTable.name);
37
+ }
38
+ export async function createPermission(name, description) {
39
+ return await db
40
+ .insert(permissionsTable)
41
+ .values({ name, description })
42
+ .returning();
43
+ }
44
+ export async function deletePermission(permissionId) {
45
+ return await db
46
+ .delete(permissionsTable)
47
+ .where(eq(permissionsTable.id, permissionId));
48
+ }
49
+ // --- Mappings ---
50
+ export async function getRolePermissions(roleId) {
51
+ return await db
52
+ .select({
53
+ id: permissionsTable.id,
54
+ name: permissionsTable.name,
55
+ })
56
+ .from(rolesToPermissionsTable)
57
+ .innerJoin(permissionsTable, eq(rolesToPermissionsTable.permissionId, permissionsTable.id))
58
+ .where(eq(rolesToPermissionsTable.roleId, roleId));
59
+ }
60
+ export async function assignPermissionToRole(roleId, permissionId) {
61
+ return await db
62
+ .insert(rolesToPermissionsTable)
63
+ .values({ roleId, permissionId })
64
+ .onConflictDoNothing();
65
+ }
66
+ export async function revokePermissionFromRole(roleId, permissionId) {
67
+ return await db
68
+ .delete(rolesToPermissionsTable)
69
+ .where(and(eq(rolesToPermissionsTable.roleId, roleId), eq(rolesToPermissionsTable.permissionId, permissionId)));
70
+ }
71
+ // --- User Assignment ---
72
+ export async function assignRoleToUser(userId, roleId) {
73
+ return await db
74
+ .insert(usersToRolesTable)
75
+ .values({ userId, roleId })
76
+ .onConflictDoNothing();
77
+ }
78
+ export async function revokeRoleFromUser(userId, roleId) {
79
+ return await db
80
+ .delete(usersToRolesTable)
81
+ .where(and(eq(usersToRolesTable.userId, userId), eq(usersToRolesTable.roleId, roleId)));
82
+ }
83
+ export async function assignPermissionToUser(userId, permissionId) {
84
+ return await db
85
+ .insert(usersToPermissionsTable)
86
+ .values({ userId, permissionId })
87
+ .onConflictDoNothing();
88
+ }
89
+ export async function revokePermissionFromUser(userId, permissionId) {
90
+ return await db
91
+ .delete(usersToPermissionsTable)
92
+ .where(and(eq(usersToPermissionsTable.userId, userId), eq(usersToPermissionsTable.permissionId, permissionId)));
93
+ }
94
+ export async function getUserRbacData(userId) {
95
+ const roles = await db
96
+ .select({
97
+ id: rolesTable.id,
98
+ name: rolesTable.name,
99
+ })
100
+ .from(usersToRolesTable)
101
+ .innerJoin(rolesTable, eq(usersToRolesTable.roleId, rolesTable.id))
102
+ .where(eq(usersToRolesTable.userId, userId));
103
+ const directPermissions = await db
104
+ .select({
105
+ id: permissionsTable.id,
106
+ name: permissionsTable.name,
107
+ })
108
+ .from(usersToPermissionsTable)
109
+ .innerJoin(permissionsTable, eq(usersToPermissionsTable.permissionId, permissionsTable.id))
110
+ .where(eq(usersToPermissionsTable.userId, userId));
111
+ // Fetch inherited permissions from roles
112
+ let rolePermissions = [];
113
+ if (roles.length > 0) {
114
+ const roleIds = roles.map((r) => r.id);
115
+ rolePermissions = await db
116
+ .select({
117
+ id: permissionsTable.id,
118
+ name: permissionsTable.name,
119
+ })
120
+ .from(rolesToPermissionsTable)
121
+ .innerJoin(permissionsTable, eq(rolesToPermissionsTable.permissionId, permissionsTable.id))
122
+ .where(inArray(rolesToPermissionsTable.roleId, roleIds));
123
+ }
124
+ // Combine for effective permissions
125
+ const effectiveMap = new Map();
126
+ for (const p of [...directPermissions, ...rolePermissions]) {
127
+ effectiveMap.set(p.id, p);
128
+ }
129
+ return {
130
+ roles,
131
+ directPermissions,
132
+ effectivePermissions: Array.from(effectiveMap.values()),
133
+ };
134
+ }
@@ -0,0 +1,50 @@
1
+ import type { AuthSession, Session, SessionFlags, UserSession } from "./types";
2
+ /**
3
+ * Returns the user's IP address.
4
+ */
5
+ export declare function getIPAddress(): Promise<string | null>;
6
+ /**
7
+ * Validates the session token.
8
+ */
9
+ export declare function validateSessionToken(token: string): Promise<AuthSession>;
10
+ /**
11
+ * Returns the current user session from cookies.
12
+ */
13
+ export declare const getCurrentSession: () => Promise<AuthSession>;
14
+ /**
15
+ * Invalidates a single session.
16
+ */
17
+ export declare function invalidateSession(sessionId: string): Promise<void>;
18
+ /**
19
+ * Invalidates all user sessions.
20
+ */
21
+ export declare function invalidateUserSessions(userId: string): Promise<void>;
22
+ /**
23
+ * Sets the session token in a cookie.
24
+ */
25
+ export declare function setSessionTokenCookie(token: string, expiresAt: Date): Promise<void>;
26
+ /**
27
+ * Removes the session token cookie.
28
+ */
29
+ export declare function deleteSessionTokenCookie(): Promise<void>;
30
+ /**
31
+ * Generates a new random session token.
32
+ */
33
+ export declare function generateSessionToken(): Promise<string>;
34
+ /**
35
+ * Creates a new session in the database.
36
+ */
37
+ export declare function createSession(token: string, userId: string, flags: SessionFlags): Promise<Session>;
38
+ /**
39
+ * Signs the user out and redirects to the sign-in page.
40
+ */
41
+ export declare function sessionSignOut(): Promise<void>;
42
+ /**
43
+ * Get all active sessions for a user.
44
+ */
45
+ export declare function getUserSessions(userId: string, currentSessionId: string): Promise<UserSession[]>;
46
+ /**
47
+ * Invalidate all sessions for a user except the specified current one.
48
+ */
49
+ export declare function invalidateOtherSessions(userId: string, currentSessionId: string): Promise<void>;
50
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/auth/session.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,YAAY,EAEZ,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE3D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,CAAC,CAoCtB;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,WAAW,CAS7D,CAAC;AAEF;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExE;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,IAAI,GACd,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG9D;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAI5D;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;GAEG;AACH,wBAAsB,cAAc,kBASnC;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CAYxB;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,152 @@
1
+ "use server";
2
+ import { sha256 } from "@oslojs/crypto/sha2";
3
+ import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase, } from "@oslojs/encoding";
4
+ import { addDays } from "date-fns";
5
+ import { and, eq, ne } from "drizzle-orm";
6
+ import { cookies, headers } from "next/headers";
7
+ import { redirect } from "next/navigation";
8
+ import { db } from "../../server/database/inject";
9
+ import { sessionTable, userTable } from "../../server/database/schema";
10
+ import { augmentSession } from "./augment";
11
+ import { performFullUserAugmentation } from "./logic";
12
+ /**
13
+ * Returns the user's IP address.
14
+ */
15
+ export async function getIPAddress() {
16
+ return (await headers()).get("x-forwarded-for");
17
+ }
18
+ /**
19
+ * Validates the session token.
20
+ */
21
+ export async function validateSessionToken(token) {
22
+ const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
23
+ const [row] = await db
24
+ .select({
25
+ session: sessionTable,
26
+ user: userTable,
27
+ })
28
+ .from(sessionTable)
29
+ .innerJoin(userTable, eq(sessionTable.userId, userTable.id))
30
+ .where(eq(sessionTable.id, sessionId));
31
+ if (!row || !row.user) {
32
+ return { session: null, user: null };
33
+ }
34
+ const { session: baseSession, user: baseUser } = row;
35
+ // STRICTLY remove non-serializable and sensitive fields
36
+ const { password, recovery_code, ...safeUser } = baseUser;
37
+ // Check if session is expired
38
+ if (new Date() > baseSession.expiresAt) {
39
+ await db.delete(sessionTable).where(eq(sessionTable.id, baseSession.id));
40
+ return { session: null, user: null };
41
+ }
42
+ // AUGMENT (EXTENSIBILITY POINTS)
43
+ const augmentedUser = await performFullUserAugmentation(safeUser);
44
+ const augmentedSession = await augmentSession(baseSession);
45
+ // ENSURE PLAIN OBJECTS for Client Components
46
+ return {
47
+ session: augmentedSession ? { ...augmentedSession } : null,
48
+ user: augmentedUser ? { ...augmentedUser } : null,
49
+ };
50
+ }
51
+ /**
52
+ * Returns the current user session from cookies.
53
+ */
54
+ export const getCurrentSession = async () => {
55
+ var _a, _b;
56
+ const cookieStore = await cookies();
57
+ const token = (_b = (_a = cookieStore.get("session")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : null;
58
+ if (token === null) {
59
+ return { session: null, user: null };
60
+ }
61
+ return await validateSessionToken(token);
62
+ };
63
+ /**
64
+ * Invalidates a single session.
65
+ */
66
+ export async function invalidateSession(sessionId) {
67
+ await db.delete(sessionTable).where(eq(sessionTable.id, sessionId));
68
+ }
69
+ /**
70
+ * Invalidates all user sessions.
71
+ */
72
+ export async function invalidateUserSessions(userId) {
73
+ await db.delete(sessionTable).where(eq(sessionTable.userId, userId));
74
+ }
75
+ /**
76
+ * Sets the session token in a cookie.
77
+ */
78
+ export async function setSessionTokenCookie(token, expiresAt) {
79
+ const cookieStore = await cookies();
80
+ cookieStore.set("session", token, {
81
+ httpOnly: true,
82
+ path: "/",
83
+ secure: process.env.NODE_ENV === "production",
84
+ sameSite: "lax",
85
+ expires: expiresAt,
86
+ });
87
+ }
88
+ /**
89
+ * Removes the session token cookie.
90
+ */
91
+ export async function deleteSessionTokenCookie() {
92
+ const cookieStore = await cookies();
93
+ cookieStore.delete("session");
94
+ }
95
+ /**
96
+ * Generates a new random session token.
97
+ */
98
+ export async function generateSessionToken() {
99
+ const tokenBytes = new Uint8Array(20);
100
+ crypto.getRandomValues(tokenBytes);
101
+ return encodeBase32LowerCaseNoPadding(tokenBytes).toLowerCase();
102
+ }
103
+ /**
104
+ * Creates a new session in the database.
105
+ */
106
+ export async function createSession(token, userId, flags) {
107
+ const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
108
+ const [session] = await db
109
+ .insert(sessionTable)
110
+ .values({
111
+ id: sessionId,
112
+ expiresAt: new Date(addDays(new Date(), 7)),
113
+ active_organization_id: flags.activeOrganizationId,
114
+ userId: userId,
115
+ })
116
+ .returning();
117
+ return session;
118
+ }
119
+ /**
120
+ * Signs the user out and redirects to the sign-in page.
121
+ */
122
+ export async function sessionSignOut() {
123
+ const { session } = await getCurrentSession();
124
+ if (session) {
125
+ await invalidateSession(session.id);
126
+ await deleteSessionTokenCookie();
127
+ }
128
+ redirect("/signin");
129
+ }
130
+ /**
131
+ * Get all active sessions for a user.
132
+ */
133
+ export async function getUserSessions(userId, currentSessionId) {
134
+ const sessions = await db
135
+ .select()
136
+ .from(sessionTable)
137
+ .where(eq(sessionTable.userId, userId));
138
+ return sessions.map((session) => ({
139
+ id: session.id,
140
+ createdAt: session.createdAt,
141
+ expiresAt: session.expiresAt,
142
+ isCurrent: session.id === currentSessionId,
143
+ }));
144
+ }
145
+ /**
146
+ * Invalidate all sessions for a user except the specified current one.
147
+ */
148
+ export async function invalidateOtherSessions(userId, currentSessionId) {
149
+ await db
150
+ .delete(sessionTable)
151
+ .where(and(eq(sessionTable.userId, userId), ne(sessionTable.id, currentSessionId)));
152
+ }
@@ -0,0 +1,52 @@
1
+ import type { passwordResetSessionTable, sessionTable, userTable } from "../../server/database/schema";
2
+ import type { UserPermission, UserRole } from "../types";
3
+ export type { UserRole, UserPermission };
4
+ export type User = typeof userTable.$inferSelect;
5
+ export type Session = typeof sessionTable.$inferSelect & Record<string, any>;
6
+ export type PasswordResetSession = typeof passwordResetSessionTable.$inferSelect & Record<string, any>;
7
+ /**
8
+ * Represents a user with all potential extensions.
9
+ * Use this type in UI components that require data added by modules.
10
+ */
11
+ export type FullUser = User & Record<string, any> & {
12
+ roles: UserRole[];
13
+ permissions: UserPermission[];
14
+ };
15
+ /**
16
+ * Basic session context.
17
+ */
18
+ export interface AuthSession {
19
+ session: Session | null;
20
+ user: FullUser | null;
21
+ }
22
+ export interface SessionFlags {
23
+ [key: string]: any;
24
+ }
25
+ export type UserSession = {
26
+ id: string;
27
+ createdAt: Date;
28
+ expiresAt: Date;
29
+ isCurrent: boolean;
30
+ [key: string]: any;
31
+ };
32
+ export type AuthResponse = {
33
+ status: "SUCCESS";
34
+ session: Session;
35
+ user: FullUser;
36
+ redirect?: string;
37
+ } | {
38
+ status: "CHALLENGE_REQUIRED";
39
+ type: string;
40
+ userId: string;
41
+ tempToken?: string;
42
+ redirect?: string;
43
+ } | {
44
+ status: "ERROR";
45
+ message: string;
46
+ redirect?: string;
47
+ };
48
+ export interface PasswordResetAuthSession {
49
+ session: PasswordResetSession | null;
50
+ user: FullUser | null;
51
+ }
52
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/auth/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EACzB,YAAY,EACZ,SAAS,EACV,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEzD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;AAEzC,MAAM,MAAM,IAAI,GAAG,OAAO,SAAS,CAAC,YAAY,CAAC;AACjD,MAAM,MAAM,OAAO,GAAG,OAAO,YAAY,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC7E,MAAM,MAAM,oBAAoB,GAC9B,OAAO,yBAAyB,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,IAAI,GACzB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B,CAAC;AAEJ;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E;IACA,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACrC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;CACvB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Generates a random one-time code (OTP).
3
+ * @param length Length of the generated code (default 6).
4
+ * @returns A random uppercase base32 string.
5
+ */
6
+ export declare function generateRandomOTP(length?: number): string;
7
+ /**
8
+ * Generates a random recovery code.
9
+ * @returns A random uppercase base32 string.
10
+ */
11
+ export declare function generateRandomRecoveryCode(): string;
12
+ //# sourceMappingURL=encode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../../../src/core/auth/utils/encode.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,SAAI,GAAG,MAAM,CAIpD;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAInD"}
@@ -0,0 +1,20 @@
1
+ import { encodeBase32UpperCaseNoPadding } from "@oslojs/encoding";
2
+ /**
3
+ * Generates a random one-time code (OTP).
4
+ * @param length Length of the generated code (default 6).
5
+ * @returns A random uppercase base32 string.
6
+ */
7
+ export function generateRandomOTP(length = 6) {
8
+ const bytes = new Uint8Array(5);
9
+ crypto.getRandomValues(bytes);
10
+ return encodeBase32UpperCaseNoPadding(bytes).substring(0, length);
11
+ }
12
+ /**
13
+ * Generates a random recovery code.
14
+ * @returns A random uppercase base32 string.
15
+ */
16
+ export function generateRandomRecoveryCode() {
17
+ const recoveryCodeBytes = new Uint8Array(10);
18
+ crypto.getRandomValues(recoveryCodeBytes);
19
+ return encodeBase32UpperCaseNoPadding(recoveryCodeBytes);
20
+ }