@veap/auth 0.0.0-stage → 0.1.1

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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -2
  3. package/dist/actions/auth.actions.d.ts +23 -0
  4. package/dist/actions/auth.actions.js +40 -0
  5. package/dist/actions/index.d.ts +1 -0
  6. package/dist/actions/index.js +1 -0
  7. package/dist/client/auth.context.d.ts +29 -0
  8. package/dist/client/auth.context.js +64 -0
  9. package/dist/client/index.d.ts +1 -0
  10. package/dist/client/index.js +1 -0
  11. package/dist/common/augment.d.ts +26 -0
  12. package/dist/common/augment.js +63 -0
  13. package/dist/common/context.d.ts +30 -0
  14. package/dist/common/context.js +36 -0
  15. package/dist/common/index.d.ts +7 -0
  16. package/dist/common/index.js +7 -0
  17. package/dist/common/logic.d.ts +64 -0
  18. package/dist/common/logic.js +133 -0
  19. package/dist/common/registry.d.ts +28 -0
  20. package/dist/common/registry.js +76 -0
  21. package/dist/common/routes.d.ts +3 -0
  22. package/dist/common/routes.js +2 -0
  23. package/dist/common/tokens.d.ts +10 -0
  24. package/dist/common/tokens.js +8 -0
  25. package/dist/common/types.d.ts +178 -0
  26. package/dist/common/types.js +9 -0
  27. package/dist/crypto/aes-cipher.d.ts +8 -0
  28. package/dist/crypto/aes-cipher.js +12 -0
  29. package/dist/crypto/bcrypt-hasher.d.ts +12 -0
  30. package/dist/crypto/bcrypt-hasher.js +37 -0
  31. package/dist/crypto/contracts.d.ts +36 -0
  32. package/dist/crypto/contracts.js +3 -0
  33. package/dist/crypto/encryption.d.ts +18 -0
  34. package/dist/crypto/encryption.js +69 -0
  35. package/dist/crypto/index.d.ts +6 -0
  36. package/dist/crypto/index.js +6 -0
  37. package/dist/crypto/password.d.ts +19 -0
  38. package/dist/crypto/password.js +27 -0
  39. package/dist/crypto/token-generator.d.ts +10 -0
  40. package/dist/crypto/token-generator.js +25 -0
  41. package/dist/index.d.ts +11 -0
  42. package/dist/index.js +11 -0
  43. package/dist/migrations/0001_create_auth_tables.d.ts +4 -0
  44. package/dist/migrations/0001_create_auth_tables.js +122 -0
  45. package/dist/migrations/index.d.ts +4 -0
  46. package/dist/migrations/index.js +7 -0
  47. package/dist/models/EmailVerification.d.ts +23 -0
  48. package/dist/models/EmailVerification.js +19 -0
  49. package/dist/models/PasswordResetSession.d.ts +25 -0
  50. package/dist/models/PasswordResetSession.js +21 -0
  51. package/dist/models/Permission.d.ts +20 -0
  52. package/dist/models/Permission.js +19 -0
  53. package/dist/models/Role.d.ts +20 -0
  54. package/dist/models/Role.js +19 -0
  55. package/dist/models/Session.d.ts +22 -0
  56. package/dist/models/Session.js +21 -0
  57. package/dist/models/User.d.ts +32 -0
  58. package/dist/models/User.js +31 -0
  59. package/dist/models/index.d.ts +6 -0
  60. package/dist/models/index.js +6 -0
  61. package/dist/models.d.ts +1 -0
  62. package/dist/models.js +1 -0
  63. package/dist/passwords/contracts.d.ts +28 -0
  64. package/dist/passwords/contracts.js +2 -0
  65. package/dist/passwords/index.d.ts +4 -0
  66. package/dist/passwords/index.js +4 -0
  67. package/dist/passwords/password-reset.facade.d.ts +14 -0
  68. package/dist/passwords/password-reset.facade.js +57 -0
  69. package/dist/passwords/password-reset.repository.d.ts +10 -0
  70. package/dist/passwords/password-reset.repository.js +39 -0
  71. package/dist/passwords/password-reset.service.d.ts +38 -0
  72. package/dist/passwords/password-reset.service.js +242 -0
  73. package/dist/providers/auth.provider.d.ts +9 -0
  74. package/dist/providers/auth.provider.js +199 -0
  75. package/dist/providers/index.d.ts +2 -0
  76. package/dist/providers/index.js +2 -0
  77. package/dist/providers/setup.d.ts +7 -0
  78. package/dist/providers/setup.js +29 -0
  79. package/dist/rbac/contracts.d.ts +75 -0
  80. package/dist/rbac/contracts.js +4 -0
  81. package/dist/rbac/index.d.ts +4 -0
  82. package/dist/rbac/index.js +4 -0
  83. package/dist/rbac/rbac.facade.d.ts +23 -0
  84. package/dist/rbac/rbac.facade.js +85 -0
  85. package/dist/rbac/rbac.repository.d.ts +30 -0
  86. package/dist/rbac/rbac.repository.js +137 -0
  87. package/dist/rbac/rbac.service.d.ts +35 -0
  88. package/dist/rbac/rbac.service.js +143 -0
  89. package/dist/server.d.ts +3 -0
  90. package/dist/server.js +3 -0
  91. package/dist/session/auth.service.d.ts +35 -0
  92. package/dist/session/auth.service.js +207 -0
  93. package/dist/session/contracts.d.ts +33 -0
  94. package/dist/session/contracts.js +2 -0
  95. package/dist/session/index.d.ts +5 -0
  96. package/dist/session/index.js +5 -0
  97. package/dist/session/session.facade.d.ts +14 -0
  98. package/dist/session/session.facade.js +75 -0
  99. package/dist/session/session.repository.d.ts +15 -0
  100. package/dist/session/session.repository.js +55 -0
  101. package/dist/session/session.service.d.ts +63 -0
  102. package/dist/session/session.service.js +221 -0
  103. package/dist/users/contracts.d.ts +33 -0
  104. package/dist/users/contracts.js +2 -0
  105. package/dist/users/index.d.ts +4 -0
  106. package/dist/users/index.js +4 -0
  107. package/dist/users/user.facade.d.ts +28 -0
  108. package/dist/users/user.facade.js +92 -0
  109. package/dist/users/user.repository.d.ts +16 -0
  110. package/dist/users/user.repository.js +60 -0
  111. package/dist/users/user.service.d.ts +80 -0
  112. package/dist/users/user.service.js +201 -0
  113. package/dist/validation/index.d.ts +90 -0
  114. package/dist/validation/index.js +46 -0
  115. package/dist/verification/contracts.d.ts +25 -0
  116. package/dist/verification/contracts.js +2 -0
  117. package/dist/verification/index.d.ts +4 -0
  118. package/dist/verification/index.js +4 -0
  119. package/dist/verification/verification.facade.d.ts +10 -0
  120. package/dist/verification/verification.facade.js +49 -0
  121. package/dist/verification/verification.repository.d.ts +7 -0
  122. package/dist/verification/verification.repository.js +23 -0
  123. package/dist/verification/verification.service.d.ts +44 -0
  124. package/dist/verification/verification.service.js +122 -0
  125. package/package.json +84 -4
@@ -0,0 +1,133 @@
1
+ import { augmentSession, augmentUser, registerIdentityAugmenter, registerPasswordResetSessionAugmenter, registerSessionAugmenter, unregisterIdentityAugmenter, unregisterPasswordResetSessionAugmenter, unregisterSessionAugmenter, } from "./augment.js";
2
+ import { authContext, isAuthContextBound } from "./context.js";
3
+ import { AuthCallbackRegistry } from "./registry.js";
4
+ const globalForAuth = globalThis;
5
+ export const authValidators = globalForAuth.__VEAP_AUTH_VALIDATORS__ ??
6
+ new AuthCallbackRegistry();
7
+ export const securityRequirements = globalForAuth.__VEAP_SECURITY_REQUIREMENTS__ ??
8
+ new AuthCallbackRegistry();
9
+ export const passwordResetValidators = globalForAuth.__VEAP_PASSWORD_RESET_VALIDATORS__ ??
10
+ new AuthCallbackRegistry();
11
+ export const emailVerificationValidators = globalForAuth.__VEAP_EMAIL_VERIFICATION_VALIDATORS__ ??
12
+ new AuthCallbackRegistry();
13
+ globalForAuth.__VEAP_AUTH_VALIDATORS__ = authValidators;
14
+ globalForAuth.__VEAP_SECURITY_REQUIREMENTS__ = securityRequirements;
15
+ globalForAuth.__VEAP_PASSWORD_RESET_VALIDATORS__ = passwordResetValidators;
16
+ globalForAuth.__VEAP_EMAIL_VERIFICATION_VALIDATORS__ =
17
+ emailVerificationValidators;
18
+ export function registerAuthValidator(idOrValidator, validator) {
19
+ authValidators.register(idOrValidator, validator);
20
+ }
21
+ export function unregisterAuthValidator(idOrValidator) {
22
+ return authValidators.unregister(idOrValidator);
23
+ }
24
+ export function registerPasswordResetValidator(idOrValidator, validator) {
25
+ passwordResetValidators.register(idOrValidator, validator);
26
+ }
27
+ export function unregisterPasswordResetValidator(idOrValidator) {
28
+ return passwordResetValidators.unregister(idOrValidator);
29
+ }
30
+ export function registerEmailVerificationValidator(idOrValidator, validator) {
31
+ emailVerificationValidators.register(idOrValidator, validator);
32
+ }
33
+ export function unregisterEmailVerificationValidator(idOrValidator) {
34
+ return emailVerificationValidators.unregister(idOrValidator);
35
+ }
36
+ export { augmentSession, augmentUser, registerIdentityAugmenter, registerPasswordResetSessionAugmenter, registerSessionAugmenter, unregisterIdentityAugmenter, unregisterPasswordResetSessionAugmenter, unregisterSessionAugmenter, };
37
+ export function registerSecurityRequirement(idOrRequirement, requirement) {
38
+ securityRequirements.register(idOrRequirement, requirement);
39
+ }
40
+ export function unregisterSecurityRequirement(idOrRequirement) {
41
+ return securityRequirements.unregister(idOrRequirement);
42
+ }
43
+ export async function runPasswordResetValidators(userId) {
44
+ for (const validator of passwordResetValidators) {
45
+ const interception = await validator(userId);
46
+ if (interception)
47
+ return interception;
48
+ }
49
+ return null;
50
+ }
51
+ export async function runEmailVerificationValidators(userId) {
52
+ for (const validator of emailVerificationValidators) {
53
+ const interception = await validator(userId);
54
+ if (interception)
55
+ return interception;
56
+ }
57
+ return null;
58
+ }
59
+ /**
60
+ * Checks if the current session satisfies all registered security requirements.
61
+ */
62
+ export async function checkSecurity(session, user, requiredRoles, requiredPermissions, fallbackRedirect,
63
+ /** Request path this check runs for; forwarded to modular requirements. */
64
+ path) {
65
+ if (!user) {
66
+ console.warn("User is required for security check");
67
+ return { satisfied: false, redirect: fallbackRedirect ?? "/signin" };
68
+ }
69
+ const userRoles = Array.isArray(user.roles) ? user.roles : [];
70
+ const userPermissions = Array.isArray(user.permissions)
71
+ ? user.permissions
72
+ : [];
73
+ // 1. Core Role Check (At least one role must match)
74
+ if (requiredRoles && requiredRoles.length > 0) {
75
+ const hasRole = requiredRoles.some((role) => userRoles.includes(role));
76
+ if (!hasRole) {
77
+ console.warn(`User lacks required roles: ${requiredRoles.join(", ")}`);
78
+ return {
79
+ satisfied: false,
80
+ redirect: fallbackRedirect,
81
+ };
82
+ }
83
+ }
84
+ // 2. Core Permission Check (ALL permissions must match)
85
+ if (requiredPermissions && requiredPermissions.length > 0) {
86
+ const hasAllPermissions = requiredPermissions.every((perm) => userPermissions.includes(perm));
87
+ if (!hasAllPermissions) {
88
+ console.warn(`User lacks required permissions: ${requiredPermissions.join(", ")}`);
89
+ return {
90
+ satisfied: false,
91
+ redirect: fallbackRedirect,
92
+ };
93
+ }
94
+ }
95
+ // 3. Modular Requirements Check
96
+ if (securityRequirements) {
97
+ for (const requirement of securityRequirements) {
98
+ try {
99
+ const result = await requirement(session, user, path);
100
+ if (result && !result.satisfied) {
101
+ return {
102
+ ...result,
103
+ redirect: result.redirect ?? fallbackRedirect,
104
+ };
105
+ }
106
+ }
107
+ catch (error) {
108
+ console.error("[veap:security] Requirement failed:", error);
109
+ }
110
+ }
111
+ }
112
+ return { satisfied: true };
113
+ }
114
+ /**
115
+ * Performs full augmentation on a user by merging core RBAC roles/permissions
116
+ * and running all registered modular identity augmenters.
117
+ */
118
+ export async function performFullUserAugmentation(user, session) {
119
+ let rbacData = {};
120
+ if (isAuthContextBound()) {
121
+ try {
122
+ const ctx = authContext();
123
+ if (ctx.rbac &&
124
+ typeof ctx.rbac.coreRbacAugmenter === "function") {
125
+ rbacData = await ctx.rbac.coreRbacAugmenter(user);
126
+ }
127
+ }
128
+ catch {
129
+ // ignore
130
+ }
131
+ }
132
+ return await augmentUser(user, rbacData);
133
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Generic callback registry supporting both keyed (string ID) and anonymous registrations.
3
+ * Implements Iterable<T> so it can be looped directly in `for (const fn of registry)`.
4
+ * Also implements Set-like methods (.add, .delete, .has, .size) for backward compatibility.
5
+ */
6
+ export declare class AuthCallbackRegistry<T extends Function> implements Iterable<T> {
7
+ private keyed;
8
+ private anonymous;
9
+ /**
10
+ * Registers a callback with an optional string ID.
11
+ * If registered with a string ID, re-registering with the same ID replaces the previous handler.
12
+ */
13
+ register(idOrHandler: string | T, maybeHandler?: T): void;
14
+ /**
15
+ * Unregisters a callback by its string ID or function reference.
16
+ * Returns true if an entry was removed, false otherwise.
17
+ */
18
+ unregister(idOrHandler: string | T): boolean;
19
+ /**
20
+ * Iterable implementation so `for (const fn of registry)` works seamlessly.
21
+ */
22
+ [Symbol.iterator](): Iterator<T>;
23
+ add(handler: T): this;
24
+ delete(idOrHandler: string | T): boolean;
25
+ has(idOrHandler: string | T): boolean;
26
+ clear(): void;
27
+ get size(): number;
28
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Generic callback registry supporting both keyed (string ID) and anonymous registrations.
3
+ * Implements Iterable<T> so it can be looped directly in `for (const fn of registry)`.
4
+ * Also implements Set-like methods (.add, .delete, .has, .size) for backward compatibility.
5
+ */
6
+ export class AuthCallbackRegistry {
7
+ keyed = new Map();
8
+ anonymous = new Set();
9
+ /**
10
+ * Registers a callback with an optional string ID.
11
+ * If registered with a string ID, re-registering with the same ID replaces the previous handler.
12
+ */
13
+ register(idOrHandler, maybeHandler) {
14
+ if (typeof idOrHandler === "string") {
15
+ if (!maybeHandler) {
16
+ throw new Error(`[AuthCallbackRegistry] A handler function must be provided when registering with ID "${idOrHandler}".`);
17
+ }
18
+ this.keyed.set(idOrHandler, maybeHandler);
19
+ }
20
+ else {
21
+ this.anonymous.add(idOrHandler);
22
+ }
23
+ }
24
+ /**
25
+ * Unregisters a callback by its string ID or function reference.
26
+ * Returns true if an entry was removed, false otherwise.
27
+ */
28
+ unregister(idOrHandler) {
29
+ if (typeof idOrHandler === "string") {
30
+ return this.keyed.delete(idOrHandler);
31
+ }
32
+ let removed = this.anonymous.delete(idOrHandler);
33
+ for (const [key, handler] of this.keyed.entries()) {
34
+ if (handler === idOrHandler) {
35
+ this.keyed.delete(key);
36
+ removed = true;
37
+ }
38
+ }
39
+ return removed;
40
+ }
41
+ /**
42
+ * Iterable implementation so `for (const fn of registry)` works seamlessly.
43
+ */
44
+ *[Symbol.iterator]() {
45
+ yield* this.keyed.values();
46
+ yield* this.anonymous.values();
47
+ }
48
+ // Set-compatible interface for backward compatibility
49
+ add(handler) {
50
+ this.anonymous.add(handler);
51
+ return this;
52
+ }
53
+ delete(idOrHandler) {
54
+ return this.unregister(idOrHandler);
55
+ }
56
+ has(idOrHandler) {
57
+ if (typeof idOrHandler === "string") {
58
+ return this.keyed.has(idOrHandler);
59
+ }
60
+ if (this.anonymous.has(idOrHandler)) {
61
+ return true;
62
+ }
63
+ for (const handler of this.keyed.values()) {
64
+ if (handler === idOrHandler)
65
+ return true;
66
+ }
67
+ return false;
68
+ }
69
+ clear() {
70
+ this.keyed.clear();
71
+ this.anonymous.clear();
72
+ }
73
+ get size() {
74
+ return this.keyed.size + this.anonymous.size;
75
+ }
76
+ }
@@ -0,0 +1,3 @@
1
+ export { AUTH_ROUTES, ROUTER_SERVICE } from "./tokens.js";
2
+ export type { AuthConfig, AuthRoutesConfig } from "./types.js";
3
+ export { DEFAULT_AUTH_ROUTES } from "./types.js";
@@ -0,0 +1,2 @@
1
+ export { AUTH_ROUTES, ROUTER_SERVICE } from "./tokens.js";
2
+ export { DEFAULT_AUTH_ROUTES } from "./types.js";
@@ -0,0 +1,10 @@
1
+ import type { Token } from "@veap/kernel";
2
+ import type { AuthRoutesConfig } from "./types.js";
3
+ /**
4
+ * IoC Token for application authentication routes configuration.
5
+ */
6
+ export declare const AUTH_ROUTES: Token<AuthRoutesConfig>;
7
+ /**
8
+ * IoC Token for application router service (used for route rewrites).
9
+ */
10
+ export declare const ROUTER_SERVICE: Token<any>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * IoC Token for application authentication routes configuration.
3
+ */
4
+ export const AUTH_ROUTES = Symbol.for("veap:auth:routes");
5
+ /**
6
+ * IoC Token for application router service (used for route rewrites).
7
+ */
8
+ export const ROUTER_SERVICE = Symbol.for("veap:router:service");
@@ -0,0 +1,178 @@
1
+ export type UserRole = "user" | "admin" | string;
2
+ export type UserPermission = string;
3
+ export interface User {
4
+ id: string;
5
+ email: string;
6
+ name: string;
7
+ password: string | null;
8
+ image: string | null;
9
+ recovery_code: any;
10
+ emailVerifiedAt: Date | null;
11
+ createdAt: Date;
12
+ updatedAt: Date | null;
13
+ [key: string]: any;
14
+ }
15
+ export interface Session {
16
+ id: string;
17
+ userId?: string;
18
+ user_id?: string;
19
+ metadata?: Record<string, any> | null;
20
+ expiresAt: Date;
21
+ expires_at?: Date;
22
+ createdAt?: Date;
23
+ created_at?: Date;
24
+ updatedAt?: Date | null;
25
+ updated_at?: Date | null;
26
+ [key: string]: any;
27
+ }
28
+ export interface PasswordResetSession {
29
+ id: string;
30
+ email: string;
31
+ code: string;
32
+ emailVerified?: boolean | null;
33
+ email_verified?: boolean | null;
34
+ userId: string;
35
+ user_id?: string;
36
+ expiresAt: Date;
37
+ expires_at?: Date;
38
+ createdAt?: Date;
39
+ created_at?: Date;
40
+ updatedAt?: Date | null;
41
+ updated_at?: Date | null;
42
+ [key: string]: any;
43
+ }
44
+ /**
45
+ * Represents a user with all potential extensions.
46
+ * Use this type in UI components that require data added by modules.
47
+ */
48
+ export type FullUser = User & Record<string, any> & {
49
+ roles: UserRole[];
50
+ permissions: UserPermission[];
51
+ };
52
+ /**
53
+ * Basic session context.
54
+ */
55
+ export interface AuthSession {
56
+ session: Session | null;
57
+ user: FullUser | null;
58
+ }
59
+ export interface SessionFlags {
60
+ [key: string]: any;
61
+ }
62
+ export type UserSession = {
63
+ id: string;
64
+ createdAt: Date;
65
+ expiresAt: Date;
66
+ isCurrent: boolean;
67
+ [key: string]: any;
68
+ };
69
+ export type AuthResponse = {
70
+ status: "SUCCESS";
71
+ session: Session;
72
+ user: FullUser;
73
+ redirect?: string;
74
+ } | {
75
+ status: "CHALLENGE_REQUIRED";
76
+ type: string;
77
+ userId: string;
78
+ tempToken?: string;
79
+ redirect?: string;
80
+ } | {
81
+ status: "ERROR";
82
+ message: string;
83
+ redirect?: string;
84
+ };
85
+ export interface PasswordResetAuthSession {
86
+ session: PasswordResetSession | null;
87
+ user: FullUser | null;
88
+ }
89
+ /**
90
+ * Configuration of public and private authentication route URLs.
91
+ */
92
+ export interface AuthRoutesConfig {
93
+ /** Sign in / login page URL. Default: "/signin" */
94
+ signIn: string;
95
+ /** Sign up / registration page URL. Default: "/signup" */
96
+ signUp: string;
97
+ /** Forgot password page URL. Default: "/forgot-password" */
98
+ forgotPassword: string;
99
+ /** Reset password page URL. Default: "/reset-password" */
100
+ resetPassword: string;
101
+ /** Email verification page URL. Default: "/verify-email" */
102
+ verifyEmail: string;
103
+ /** Redirect target after successful login. Default: "/" */
104
+ afterLogin: string;
105
+ /** Redirect target after logout. Default: "/signin" */
106
+ afterLogout: string;
107
+ }
108
+ export declare const DEFAULT_AUTH_ROUTES: AuthRoutesConfig;
109
+ export interface AuthConfig {
110
+ routes?: Partial<AuthRoutesConfig>;
111
+ /**
112
+ * Whether to automatically run auth database migrations during AuthServiceProvider.boot().
113
+ * Defaults to `true` when database connection is initialized.
114
+ */
115
+ autoMigrate?: boolean;
116
+ }
117
+ export type AuthEventPayloads = {
118
+ /**
119
+ * Emitted after password/base credentials are verified,
120
+ * but BEFORE the final session is created.
121
+ * Listeners can return a "veto" to require additional factors.
122
+ */
123
+ "system:auth:validate-factors": {
124
+ userId: string;
125
+ email: string;
126
+ };
127
+ /**
128
+ * Emitted after a full session is successfully created.
129
+ */
130
+ "system:auth:session-created": {
131
+ session: Session;
132
+ user: User;
133
+ };
134
+ "system:auth:login": {
135
+ session: Session;
136
+ user: User;
137
+ };
138
+ /**
139
+ * Emitted after a new user successfully signs up.
140
+ */
141
+ "system:auth:signup": {
142
+ session: Session;
143
+ user: User;
144
+ };
145
+ /**
146
+ * Emitted after a user signs out.
147
+ */
148
+ "system:auth:signed-out": {
149
+ user: User;
150
+ };
151
+ /**
152
+ * Emitted when a password reset is requested.
153
+ */
154
+ "system:auth:password-reset:requested": {
155
+ userId: string;
156
+ email: string;
157
+ };
158
+ /**
159
+ * Emitted when a password has been successfully reset.
160
+ */
161
+ "system:auth:password-reset:completed": {
162
+ userId: string;
163
+ };
164
+ /**
165
+ * Emitted when a verification email is sent.
166
+ */
167
+ "system:auth:verification-requested": {
168
+ userId: string;
169
+ email: string;
170
+ };
171
+ /**
172
+ * Emitted when a user successfully verifies their email.
173
+ */
174
+ "system:auth:email-verified": {
175
+ userId: string;
176
+ email: string;
177
+ };
178
+ };
@@ -0,0 +1,9 @@
1
+ export const DEFAULT_AUTH_ROUTES = {
2
+ signIn: "/signin",
3
+ signUp: "/signup",
4
+ forgotPassword: "/forgot-password",
5
+ resetPassword: "/reset-password",
6
+ verifyEmail: "/verify-email",
7
+ afterLogin: "/",
8
+ afterLogout: "/signin",
9
+ };
@@ -0,0 +1,8 @@
1
+ import type { ISecretCipher } from "./contracts.js";
2
+ /**
3
+ * AES-GCM adapter for the ISecretCipher port.
4
+ */
5
+ export declare class AesSecretCipher implements ISecretCipher {
6
+ encrypt(secret: string): Uint8Array;
7
+ decryptToString(data: Uint8Array): string;
8
+ }
@@ -0,0 +1,12 @@
1
+ import { decryptToString, encryptString } from "./encryption.js";
2
+ /**
3
+ * AES-GCM adapter for the ISecretCipher port.
4
+ */
5
+ export class AesSecretCipher {
6
+ encrypt(secret) {
7
+ return encryptString(secret);
8
+ }
9
+ decryptToString(data) {
10
+ return decryptToString(data);
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { IPasswordHasher } from "./contracts.js";
2
+ /**
3
+ * Bcrypt adapter for the IPasswordHasher port.
4
+ */
5
+ export declare class BcryptPasswordHasher implements IPasswordHasher {
6
+ readonly rounds: number;
7
+ readonly minLength: number;
8
+ constructor(rounds?: number, minLength?: number);
9
+ hash(password: string): Promise<string>;
10
+ verify(hash: string, password: string): Promise<boolean>;
11
+ validateStrength(password: string): Promise<boolean>;
12
+ }
@@ -0,0 +1,37 @@
1
+ import bcrypt from "bcryptjs";
2
+ /**
3
+ * Bcrypt adapter for the IPasswordHasher port.
4
+ */
5
+ export class BcryptPasswordHasher {
6
+ rounds;
7
+ minLength;
8
+ constructor(rounds, minLength) {
9
+ const envRounds = process.env.AUTH_BCRYPT_ROUNDS
10
+ ? Number(process.env.AUTH_BCRYPT_ROUNDS)
11
+ : NaN;
12
+ const resolvedRounds = rounds !== undefined ? Number(rounds) : envRounds;
13
+ this.rounds =
14
+ !Number.isNaN(resolvedRounds) &&
15
+ resolvedRounds >= 4 &&
16
+ resolvedRounds <= 31
17
+ ? Math.floor(resolvedRounds)
18
+ : 10;
19
+ const envMinLength = process.env.AUTH_PASSWORD_MIN_LENGTH
20
+ ? Number(process.env.AUTH_PASSWORD_MIN_LENGTH)
21
+ : NaN;
22
+ const resolvedMinLength = minLength !== undefined ? Number(minLength) : envMinLength;
23
+ this.minLength =
24
+ !Number.isNaN(resolvedMinLength) && resolvedMinLength >= 1
25
+ ? Math.floor(resolvedMinLength)
26
+ : 8;
27
+ }
28
+ async hash(password) {
29
+ return await bcrypt.hash(password, this.rounds);
30
+ }
31
+ async verify(hash, password) {
32
+ return await bcrypt.compare(password, hash);
33
+ }
34
+ async validateStrength(password) {
35
+ return password.length >= this.minLength && password.length <= 255;
36
+ }
37
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Password hashing port.
3
+ */
4
+ export interface IPasswordHasher {
5
+ /** Creates a secure hash of a plaintext password. */
6
+ hash(password: string): Promise<string>;
7
+ /** Verifies a plaintext password against a stored hash. */
8
+ verify(hash: string, password: string): Promise<boolean>;
9
+ /** Validates password complexity/length requirements. */
10
+ validateStrength(password: string): Promise<boolean>;
11
+ }
12
+ export declare const PASSWORD_HASHER: unique symbol;
13
+ /**
14
+ * Secret-at-rest encryption port.
15
+ */
16
+ export interface ISecretCipher {
17
+ /** Encrypts a secret string into storable bytes. */
18
+ encrypt(secret: string): Uint8Array;
19
+ /** Decrypts stored bytes back into the secret string. */
20
+ decryptToString(data: Uint8Array): string;
21
+ }
22
+ export declare const SECRET_CIPHER: unique symbol;
23
+ /**
24
+ * Secret-token generation port.
25
+ */
26
+ export interface ITokenGenerator {
27
+ /** Generates a random one-time code (default 6 characters, uppercase base32). */
28
+ generateOtp(length?: number): string;
29
+ /** Generates a random recovery code (uppercase base32). */
30
+ generateRecoveryCode(): string;
31
+ /** Generates a random session token (lowercase base32). */
32
+ generateSessionToken(): string;
33
+ /** Hashes a token for storage/lookup (deterministic, keyed by content). */
34
+ hashToken(token: string): string;
35
+ }
36
+ export declare const TOKEN_GENERATOR: unique symbol;
@@ -0,0 +1,3 @@
1
+ export const PASSWORD_HASHER = Symbol.for("veap:auth:password-hasher");
2
+ export const SECRET_CIPHER = Symbol.for("veap:auth:secret-cipher");
3
+ export const TOKEN_GENERATOR = Symbol.for("veap:auth:token-generator");
@@ -0,0 +1,18 @@
1
+ export declare function getEncryptionKey(): Uint8Array;
2
+ export declare function getAlgorithm(key: Uint8Array): string;
3
+ /**
4
+ * Encrypts data using AES-GCM.
5
+ */
6
+ export declare function encrypt(data: Uint8Array): Uint8Array;
7
+ /**
8
+ * Encrypts a string.
9
+ */
10
+ export declare function encryptString(data: string): Uint8Array;
11
+ /**
12
+ * Decrypts data using AES-GCM.
13
+ */
14
+ export declare function decrypt(encrypted: Uint8Array): Uint8Array;
15
+ /**
16
+ * Decrypts data to a string.
17
+ */
18
+ export declare function decryptToString(data: Uint8Array): string;
@@ -0,0 +1,69 @@
1
+ import { createCipheriv, createDecipheriv } from "node:crypto";
2
+ import { DynamicBuffer } from "@oslojs/binary";
3
+ import { decodeBase64 } from "@oslojs/encoding";
4
+ export function getEncryptionKey() {
5
+ const envKey = process.env.ENCRYPTION_KEY;
6
+ if (!envKey) {
7
+ throw new Error("ENCRYPTION_KEY environment variable is not set. Generate one with: openssl rand -base64 16");
8
+ }
9
+ const key = decodeBase64(envKey);
10
+ if (key.length !== 16 && key.length !== 24 && key.length !== 32) {
11
+ throw new Error(`ENCRYPTION_KEY must decode to 16, 24 or 32 bytes (got ${key.length}). ` +
12
+ "Provide a base64-encoded key, e.g. from: openssl rand -base64 16");
13
+ }
14
+ return key;
15
+ }
16
+ export function getAlgorithm(key) {
17
+ return key.length === 16
18
+ ? "aes-128-gcm"
19
+ : key.length === 24
20
+ ? "aes-192-gcm"
21
+ : "aes-256-gcm";
22
+ }
23
+ // Configuration contract: validate key on module load
24
+ getEncryptionKey();
25
+ /**
26
+ * Encrypts data using AES-GCM.
27
+ */
28
+ export function encrypt(data) {
29
+ const key = getEncryptionKey();
30
+ const iv = new Uint8Array(16);
31
+ crypto.getRandomValues(iv);
32
+ const cipher = createCipheriv(getAlgorithm(key), key, iv);
33
+ const encrypted = new DynamicBuffer(0);
34
+ encrypted.write(iv);
35
+ encrypted.write(cipher.update(data));
36
+ encrypted.write(cipher.final());
37
+ encrypted.write(cipher.getAuthTag());
38
+ return encrypted.bytes();
39
+ }
40
+ /**
41
+ * Encrypts a string.
42
+ */
43
+ export function encryptString(data) {
44
+ return encrypt(new TextEncoder().encode(data));
45
+ }
46
+ /**
47
+ * Decrypts data using AES-GCM.
48
+ */
49
+ export function decrypt(encrypted) {
50
+ if (encrypted.byteLength < 33) {
51
+ throw new Error("Invalid encrypted data length");
52
+ }
53
+ const key = getEncryptionKey();
54
+ const iv = encrypted.slice(0, 16);
55
+ const authTag = encrypted.slice(encrypted.byteLength - 16);
56
+ const content = encrypted.slice(16, encrypted.byteLength - 16);
57
+ const decipher = createDecipheriv(getAlgorithm(key), key, iv);
58
+ decipher.setAuthTag(authTag);
59
+ const decrypted = new DynamicBuffer(0);
60
+ decrypted.write(decipher.update(content));
61
+ decrypted.write(decipher.final());
62
+ return decrypted.bytes();
63
+ }
64
+ /**
65
+ * Decrypts data to a string.
66
+ */
67
+ export function decryptToString(data) {
68
+ return new TextDecoder().decode(decrypt(data));
69
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./aes-cipher.js";
2
+ export * from "./bcrypt-hasher.js";
3
+ export * from "./contracts.js";
4
+ export * from "./encryption.js";
5
+ export * from "./password.js";
6
+ export * from "./token-generator.js";
@@ -0,0 +1,6 @@
1
+ export * from "./aes-cipher.js";
2
+ export * from "./bcrypt-hasher.js";
3
+ export * from "./contracts.js";
4
+ export * from "./encryption.js";
5
+ export * from "./password.js";
6
+ export * from "./token-generator.js";