myaidev-method 0.2.8 → 0.2.9

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 (157) hide show
  1. package/.claude/agents/wordpress-admin.md +271 -0
  2. package/.env.example +0 -1
  3. package/PACKAGE_FIXES_SUMMARY.md +319 -0
  4. package/PAYLOADCMS_AUTH_UPDATE.md +248 -0
  5. package/USER_GUIDE.md +260 -0
  6. package/bin/cli.js +36 -0
  7. package/dist/server/.tsbuildinfo +1 -0
  8. package/dist/server/auth/controllers/AuthController.d.ts +34 -0
  9. package/dist/server/auth/controllers/AuthController.d.ts.map +1 -0
  10. package/dist/server/auth/controllers/AuthController.js +43 -0
  11. package/dist/server/auth/controllers/AuthController.js.map +1 -0
  12. package/dist/server/auth/example-usage.d.ts +53 -0
  13. package/dist/server/auth/example-usage.d.ts.map +1 -0
  14. package/dist/server/auth/example-usage.js +129 -0
  15. package/dist/server/auth/example-usage.js.map +1 -0
  16. package/dist/server/auth/index.d.ts +11 -0
  17. package/dist/server/auth/index.d.ts.map +1 -0
  18. package/dist/server/auth/index.js +15 -0
  19. package/dist/server/auth/index.js.map +1 -0
  20. package/dist/server/auth/layers.d.ts +19 -0
  21. package/dist/server/auth/layers.d.ts.map +1 -0
  22. package/dist/server/auth/layers.js +33 -0
  23. package/dist/server/auth/layers.js.map +1 -0
  24. package/dist/server/auth/middleware/authMiddleware.d.ts +24 -0
  25. package/dist/server/auth/middleware/authMiddleware.d.ts.map +1 -0
  26. package/dist/server/auth/middleware/authMiddleware.js +65 -0
  27. package/dist/server/auth/middleware/authMiddleware.js.map +1 -0
  28. package/dist/server/auth/routes/authRoutes.d.ts +11 -0
  29. package/dist/server/auth/routes/authRoutes.d.ts.map +1 -0
  30. package/dist/server/auth/routes/authRoutes.js +213 -0
  31. package/dist/server/auth/routes/authRoutes.js.map +1 -0
  32. package/dist/server/auth/services/AuditLogService.d.ts +21 -0
  33. package/dist/server/auth/services/AuditLogService.d.ts.map +1 -0
  34. package/dist/server/auth/services/AuditLogService.js +28 -0
  35. package/dist/server/auth/services/AuditLogService.js.map +1 -0
  36. package/dist/server/auth/services/AuthService.d.ts +27 -0
  37. package/dist/server/auth/services/AuthService.d.ts.map +1 -0
  38. package/dist/server/auth/services/AuthService.js +246 -0
  39. package/dist/server/auth/services/AuthService.js.map +1 -0
  40. package/dist/server/auth/services/PasswordService.d.ts +12 -0
  41. package/dist/server/auth/services/PasswordService.d.ts.map +1 -0
  42. package/dist/server/auth/services/PasswordService.js +31 -0
  43. package/dist/server/auth/services/PasswordService.js.map +1 -0
  44. package/dist/server/auth/services/SessionRepository.d.ts +24 -0
  45. package/dist/server/auth/services/SessionRepository.d.ts.map +1 -0
  46. package/dist/server/auth/services/SessionRepository.js +101 -0
  47. package/dist/server/auth/services/SessionRepository.js.map +1 -0
  48. package/dist/server/auth/services/TokenService.d.ts +12 -0
  49. package/dist/server/auth/services/TokenService.d.ts.map +1 -0
  50. package/dist/server/auth/services/TokenService.js +86 -0
  51. package/dist/server/auth/services/TokenService.js.map +1 -0
  52. package/dist/server/auth/services/UserRepository.d.ts +23 -0
  53. package/dist/server/auth/services/UserRepository.d.ts.map +1 -0
  54. package/dist/server/auth/services/UserRepository.js +168 -0
  55. package/dist/server/auth/services/UserRepository.js.map +1 -0
  56. package/dist/server/auth/services/example.d.ts +26 -0
  57. package/dist/server/auth/services/example.d.ts.map +1 -0
  58. package/dist/server/auth/services/example.js +221 -0
  59. package/dist/server/auth/services/example.js.map +1 -0
  60. package/dist/server/auth/services/index.d.ts +6 -0
  61. package/dist/server/auth/services/index.d.ts.map +1 -0
  62. package/dist/server/auth/services/index.js +7 -0
  63. package/dist/server/auth/services/index.js.map +1 -0
  64. package/dist/server/database/db.d.ts +28 -0
  65. package/dist/server/database/db.d.ts.map +1 -0
  66. package/dist/server/database/db.js +91 -0
  67. package/dist/server/database/db.js.map +1 -0
  68. package/dist/server/database/schema.sql +95 -0
  69. package/dist/server/hono/app.d.ts +10 -0
  70. package/dist/server/hono/app.d.ts.map +1 -0
  71. package/dist/server/hono/app.js +26 -0
  72. package/dist/server/hono/app.js.map +1 -0
  73. package/dist/server/hono/routes.d.ts +12 -0
  74. package/dist/server/hono/routes.d.ts.map +1 -0
  75. package/dist/server/hono/routes.js +40 -0
  76. package/dist/server/hono/routes.js.map +1 -0
  77. package/dist/server/main.d.ts +2 -0
  78. package/dist/server/main.d.ts.map +1 -0
  79. package/dist/server/main.js +94 -0
  80. package/dist/server/main.js.map +1 -0
  81. package/dist/server/user-management/DirectoryService.d.ts +62 -0
  82. package/dist/server/user-management/DirectoryService.d.ts.map +1 -0
  83. package/dist/server/user-management/DirectoryService.js +201 -0
  84. package/dist/server/user-management/DirectoryService.js.map +1 -0
  85. package/dist/server/user-management/LinuxUserService.d.ts +71 -0
  86. package/dist/server/user-management/LinuxUserService.d.ts.map +1 -0
  87. package/dist/server/user-management/LinuxUserService.js +192 -0
  88. package/dist/server/user-management/LinuxUserService.js.map +1 -0
  89. package/dist/server/user-management/QuotaService.d.ts +59 -0
  90. package/dist/server/user-management/QuotaService.d.ts.map +1 -0
  91. package/dist/server/user-management/QuotaService.js +148 -0
  92. package/dist/server/user-management/QuotaService.js.map +1 -0
  93. package/dist/server/user-management/UserManagementService.d.ts +74 -0
  94. package/dist/server/user-management/UserManagementService.d.ts.map +1 -0
  95. package/dist/server/user-management/UserManagementService.js +122 -0
  96. package/dist/server/user-management/UserManagementService.js.map +1 -0
  97. package/dist/server/user-management/index.d.ts +26 -0
  98. package/dist/server/user-management/index.d.ts.map +1 -0
  99. package/dist/server/user-management/index.js +26 -0
  100. package/dist/server/user-management/index.js.map +1 -0
  101. package/dist/server/user-management/layers.d.ts +27 -0
  102. package/dist/server/user-management/layers.d.ts.map +1 -0
  103. package/dist/server/user-management/layers.js +37 -0
  104. package/dist/server/user-management/layers.js.map +1 -0
  105. package/dist/shared/types.d.ts +94 -0
  106. package/dist/shared/types.d.ts.map +1 -0
  107. package/dist/shared/types.js +32 -0
  108. package/dist/shared/types.js.map +1 -0
  109. package/package.json +25 -5
  110. package/src/lib/payloadcms-utils.js +5 -12
  111. package/src/server/auth/ARCHITECTURE.md +575 -0
  112. package/src/server/auth/IMPLEMENTATION_SUMMARY.md +287 -0
  113. package/src/server/auth/QUICK_START.md +283 -0
  114. package/src/server/auth/README.md +290 -0
  115. package/src/server/auth/controllers/AuthController.ts +129 -0
  116. package/src/server/auth/example-usage.ts +159 -0
  117. package/src/server/auth/index.ts +19 -0
  118. package/src/server/auth/layers.ts +57 -0
  119. package/src/server/auth/middleware/authMiddleware.ts +118 -0
  120. package/src/server/auth/routes/authRoutes.ts +319 -0
  121. package/src/server/auth/services/AuditLogService.ts +81 -0
  122. package/src/server/auth/services/AuthService.ts +408 -0
  123. package/src/server/auth/services/IMPLEMENTATION_SUMMARY.md +404 -0
  124. package/src/server/auth/services/PasswordService.ts +85 -0
  125. package/src/server/auth/services/README.md +361 -0
  126. package/src/server/auth/services/SessionRepository.ts +227 -0
  127. package/src/server/auth/services/TokenService.ts +174 -0
  128. package/src/server/auth/services/UserRepository.ts +318 -0
  129. package/src/server/auth/services/example.ts +346 -0
  130. package/src/server/auth/services/index.ts +6 -0
  131. package/src/server/database/db.ts +161 -0
  132. package/src/server/database/schema.sql +95 -0
  133. package/src/server/hono/app.ts +41 -0
  134. package/src/server/main.ts +115 -0
  135. package/src/server/user-management/DirectoryService.ts +348 -0
  136. package/src/server/user-management/LinuxUserService.ts +338 -0
  137. package/src/server/user-management/QuotaService.ts +256 -0
  138. package/src/server/user-management/README.md +333 -0
  139. package/src/server/user-management/UserManagementService.ts +335 -0
  140. package/src/server/user-management/index.ts +26 -0
  141. package/src/server/user-management/layers.ts +51 -0
  142. package/src/shared/types.ts +111 -0
  143. package/src/templates/claude/agents/payloadcms-publish.md +34 -14
  144. package/src/templates/codex/commands/myai-astro-publish.md +8 -2
  145. package/src/templates/codex/commands/myai-content-writer.md +8 -2
  146. package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
  147. package/src/templates/codex/commands/myai-dev-architect.md +8 -2
  148. package/src/templates/codex/commands/myai-dev-code.md +8 -2
  149. package/src/templates/codex/commands/myai-dev-docs.md +8 -2
  150. package/src/templates/codex/commands/myai-dev-review.md +8 -2
  151. package/src/templates/codex/commands/myai-dev-test.md +8 -2
  152. package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
  153. package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
  154. package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
  155. package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
  156. package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
  157. package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuditLogService.js","sourceRoot":"","sources":["../../../../src/server/auth/services/AuditLogService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAkCvD,MAAM,OAAO,eAAgB,SAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAOhE;IACD,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CACxB,IAAI,EACJ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QAErC,MAAM,GAAG,GAAG,CACV,IAAwB,EACY,EAAE,CACtC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAEvB,KAAK,CAAC,CAAC,CAAC,CACN,EAAE,CAAC,GAAG,CACJ;;;mDAGqC,EACrC;gBACE,EAAE;gBACF,IAAI,CAAC,MAAM;gBACX,IAAI,CAAC,MAAM;gBACX,IAAI,CAAC,YAAY,IAAI,IAAI;gBACzB,IAAI,CAAC,UAAU,IAAI,IAAI;gBACvB,IAAI,CAAC,SAAS,IAAI,IAAI;gBACtB,IAAI,CAAC,SAAS,IAAI,IAAI;gBACtB,IAAI,CAAC,OAAO,IAAI,IAAI;gBACpB,GAAG;aACJ,CACF,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEL,OAAO,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC,CAAC,CACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { AuthError, DatabaseError, Session, User, ValidationError } from "../../../shared/types.js";
3
+ import { PasswordService } from "./PasswordService.js";
4
+ import { TokenService } from "./TokenService.js";
5
+ import { UserRepository } from "./UserRepository.js";
6
+ import { SessionRepository } from "./SessionRepository.js";
7
+ import { AuditLogService } from "./AuditLogService.js";
8
+ import { UserManagementService } from "../../user-management/UserManagementService.js";
9
+ export interface AuthServiceDeps {
10
+ readonly register: (username: string, email: string, password: string, ipAddress?: string | null, userAgent?: string | null) => Effect.Effect<User, AuthError | ValidationError | DatabaseError>;
11
+ readonly login: (email: string, password: string, ipAddress?: string | null, userAgent?: string | null) => Effect.Effect<{
12
+ user: User;
13
+ token: string;
14
+ session: Session;
15
+ }, AuthError | DatabaseError>;
16
+ readonly logout: (sessionId: string, userId: string) => Effect.Effect<void, DatabaseError>;
17
+ readonly verifyToken: (token: string) => Effect.Effect<{
18
+ user: User;
19
+ session: Session;
20
+ }, AuthError | DatabaseError>;
21
+ }
22
+ declare const AuthService_base: Context.TagClass<AuthService, "AuthService", AuthServiceDeps>;
23
+ export declare class AuthService extends AuthService_base {
24
+ static Live: Layer.Layer<AuthService, never, PasswordService | TokenService | AuditLogService | UserRepository | SessionRepository | UserManagementService>;
25
+ }
26
+ export {};
27
+ //# sourceMappingURL=AuthService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthService.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/services/AuthService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EACL,SAAS,EACT,aAAa,EACb,OAAO,EACP,IAAI,EACJ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAC;AAKvF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,CACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,KACtB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,eAAe,GAAG,aAAa,CAAC,CAAC;IACtE,QAAQ,CAAC,KAAK,EAAE,CACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,EACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,KACtB,MAAM,CAAC,MAAM,CAChB;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAC/C,SAAS,GAAG,aAAa,CAC1B,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,CACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,CACpB,KAAK,EAAE,MAAM,KACV,MAAM,CAAC,MAAM,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,EAAE,SAAS,GAAG,aAAa,CAAC,CAAC;CACjF;;AAED,qBAAa,WAAY,SAAQ,gBAG9B;IACD,MAAM,CAAC,IAAI,iJAsWT;CACH"}
@@ -0,0 +1,246 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { AuthError, ValidationError, } from "../../../shared/types.js";
3
+ import { PasswordService } from "./PasswordService.js";
4
+ import { TokenService } from "./TokenService.js";
5
+ import { UserRepository } from "./UserRepository.js";
6
+ import { SessionRepository } from "./SessionRepository.js";
7
+ import { AuditLogService } from "./AuditLogService.js";
8
+ import { UserManagementService } from "../../user-management/UserManagementService.js";
9
+ const MAX_FAILED_ATTEMPTS = 5;
10
+ const LOCKOUT_DURATION_MS = 15 * 60 * 1000; // 15 minutes
11
+ export class AuthService extends Context.Tag("AuthService")() {
12
+ static Live = Layer.effect(this, Effect.gen(function* () {
13
+ const passwordService = yield* PasswordService;
14
+ const tokenService = yield* TokenService;
15
+ const userRepo = yield* UserRepository;
16
+ const sessionRepo = yield* SessionRepository;
17
+ const auditLog = yield* AuditLogService;
18
+ const userManagement = yield* UserManagementService;
19
+ const sanitizeLinuxUsername = (username) => {
20
+ // Convert to lowercase, replace non-alphanumeric with underscore
21
+ let sanitized = username
22
+ .toLowerCase()
23
+ .replace(/[^a-z0-9_]/g, "_")
24
+ .replace(/^[0-9_]+/, "") // Cannot start with number or underscore
25
+ .slice(0, 32); // Max length for Linux usernames
26
+ // Ensure it starts with a letter
27
+ if (!/^[a-z]/.test(sanitized)) {
28
+ sanitized = "user_" + sanitized;
29
+ }
30
+ return sanitized;
31
+ };
32
+ const generateUniqueLinuxUsername = (baseUsername) => Effect.gen(function* () {
33
+ let linuxUsername = sanitizeLinuxUsername(baseUsername);
34
+ let counter = 0;
35
+ // Keep trying until we find a unique username
36
+ while (true) {
37
+ const existingUser = yield* userRepo.findByUsername(linuxUsername);
38
+ if (!existingUser) {
39
+ return linuxUsername;
40
+ }
41
+ counter++;
42
+ const suffix = `_${counter}`;
43
+ const maxBase = 32 - suffix.length;
44
+ linuxUsername =
45
+ sanitizeLinuxUsername(baseUsername).slice(0, maxBase) + suffix;
46
+ }
47
+ });
48
+ const validateEmail = (email) => Effect.gen(function* () {
49
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
50
+ if (!emailRegex.test(email)) {
51
+ return yield* Effect.fail(new ValidationError("email", "Invalid email format"));
52
+ }
53
+ });
54
+ const validateUsername = (username) => Effect.gen(function* () {
55
+ if (username.length < 3) {
56
+ return yield* Effect.fail(new ValidationError("username", "Username must be at least 3 characters long"));
57
+ }
58
+ if (username.length > 32) {
59
+ return yield* Effect.fail(new ValidationError("username", "Username must be at most 32 characters long"));
60
+ }
61
+ if (!/^[a-zA-Z0-9_]+$/.test(username)) {
62
+ return yield* Effect.fail(new ValidationError("username", "Username can only contain letters, numbers, and underscores"));
63
+ }
64
+ });
65
+ const isAccountLocked = (user) => {
66
+ if (user.failedLoginAttempts < MAX_FAILED_ATTEMPTS) {
67
+ return false;
68
+ }
69
+ // Check if lockout period has expired
70
+ const now = Date.now();
71
+ const lockoutExpiry = user.updatedAt + LOCKOUT_DURATION_MS;
72
+ return now < lockoutExpiry;
73
+ };
74
+ const register = (username, email, password, ipAddress = null, userAgent = null) => Effect.gen(function* () {
75
+ // Validate inputs
76
+ yield* validateUsername(username);
77
+ yield* validateEmail(email);
78
+ yield* passwordService.validatePasswordStrength(password);
79
+ // Check if user already exists
80
+ const existingEmail = yield* userRepo.findByEmail(email);
81
+ if (existingEmail) {
82
+ return yield* Effect.fail(new ValidationError("email", "Email already registered"));
83
+ }
84
+ const existingUsername = yield* userRepo.findByUsername(username);
85
+ if (existingUsername) {
86
+ return yield* Effect.fail(new ValidationError("username", "Username already taken"));
87
+ }
88
+ // Hash password
89
+ const passwordHash = yield* passwordService.hash(password);
90
+ // Generate unique Linux username
91
+ const linuxUsername = yield* generateUniqueLinuxUsername(username);
92
+ // Create user in database
93
+ const user = yield* userRepo.create({
94
+ username,
95
+ email,
96
+ passwordHash,
97
+ linuxUsername,
98
+ });
99
+ // Create Linux user with home directory and Claude config
100
+ // This runs in the background and doesn't block registration
101
+ // If it fails, user can still authenticate but won't have Linux access
102
+ yield* userManagement
103
+ .createUser({
104
+ username: linuxUsername,
105
+ email,
106
+ shell: "/bin/rbash", // Restricted bash for security
107
+ diskQuotaMB: 2048, // 2GB default quota
108
+ })
109
+ .pipe(Effect.catchAll((error) => {
110
+ // Log the error but don't fail registration
111
+ console.error(`Failed to create Linux user for ${linuxUsername}:`, error);
112
+ return Effect.succeed(void 0);
113
+ }));
114
+ // Log audit event
115
+ yield* auditLog.log({
116
+ userId: user.id,
117
+ action: "USER_REGISTERED",
118
+ resourceType: "user",
119
+ resourceId: user.id,
120
+ ipAddress: ipAddress ?? null,
121
+ userAgent: userAgent ?? null,
122
+ });
123
+ return user;
124
+ });
125
+ const login = (email, password, ipAddress = null, userAgent = null) => Effect.gen(function* () {
126
+ // Find user by email
127
+ const user = yield* userRepo.findByEmail(email);
128
+ if (!user) {
129
+ return yield* Effect.fail(new AuthError("INVALID_CREDENTIALS", "Invalid email or password"));
130
+ }
131
+ // Check if account is locked
132
+ if (isAccountLocked(user)) {
133
+ yield* auditLog.log({
134
+ userId: user.id,
135
+ action: "LOGIN_FAILED",
136
+ resourceType: "user",
137
+ resourceId: user.id,
138
+ ipAddress: ipAddress ?? null,
139
+ userAgent: userAgent ?? null,
140
+ details: "Account locked due to too many failed attempts",
141
+ });
142
+ return yield* Effect.fail(new AuthError("ACCOUNT_LOCKED", "Account is locked due to too many failed login attempts. Please try again in 15 minutes."));
143
+ }
144
+ // Verify password
145
+ if (!user.passwordHash) {
146
+ return yield* Effect.fail(new AuthError("INVALID_CREDENTIALS", "Invalid email or password"));
147
+ }
148
+ const isPasswordValid = yield* passwordService.verify(password, user.passwordHash);
149
+ if (!isPasswordValid) {
150
+ // Increment failed login attempts
151
+ yield* userRepo.incrementFailedLogins(user.id);
152
+ yield* auditLog.log({
153
+ userId: user.id,
154
+ action: "LOGIN_FAILED",
155
+ resourceType: "user",
156
+ resourceId: user.id,
157
+ ipAddress: ipAddress ?? null,
158
+ userAgent: userAgent ?? null,
159
+ details: "Invalid password",
160
+ });
161
+ return yield* Effect.fail(new AuthError("INVALID_CREDENTIALS", "Invalid email or password"));
162
+ }
163
+ // Reset failed login attempts on successful login
164
+ yield* userRepo.resetFailedLogins(user.id);
165
+ // Update last login time
166
+ const updatedUser = yield* userRepo.update(user.id, {
167
+ lastLoginAt: Date.now(),
168
+ });
169
+ // Generate a unique placeholder token to avoid constraint violations
170
+ const expiresAt = Date.now() + 7 * 24 * 60 * 60 * 1000; // 7 days
171
+ const placeholderToken = `placeholder_${user.id}_${Date.now()}_${Math.random()}`;
172
+ const placeholderHash = yield* tokenService.hashToken(placeholderToken);
173
+ // Create session with placeholder hash
174
+ const session = yield* sessionRepo.create({
175
+ userId: user.id,
176
+ tokenHash: placeholderHash,
177
+ ipAddress: ipAddress ?? null,
178
+ userAgent: userAgent ?? null,
179
+ expiresAt,
180
+ });
181
+ // Generate JWT token with the session ID
182
+ const token = yield* tokenService.generateToken({
183
+ sub: user.id,
184
+ username: user.username,
185
+ email: user.email,
186
+ jti: session.id,
187
+ });
188
+ // Update the session with the actual token hash
189
+ const actualTokenHash = yield* tokenService.hashToken(token);
190
+ const finalSession = yield* sessionRepo.updateTokenHash(session.id, actualTokenHash);
191
+ // Log audit event
192
+ yield* auditLog.log({
193
+ userId: user.id,
194
+ action: "USER_LOGIN",
195
+ resourceType: "session",
196
+ resourceId: finalSession.id,
197
+ ipAddress: ipAddress ?? null,
198
+ userAgent: userAgent ?? null,
199
+ });
200
+ return { user: updatedUser, token, session: finalSession };
201
+ });
202
+ const logout = (sessionId, userId) => Effect.gen(function* () {
203
+ // Revoke session
204
+ yield* sessionRepo.revoke(sessionId);
205
+ // Log audit event
206
+ yield* auditLog.log({
207
+ userId,
208
+ action: "USER_LOGOUT",
209
+ resourceType: "session",
210
+ resourceId: sessionId,
211
+ ipAddress: null,
212
+ userAgent: null,
213
+ });
214
+ });
215
+ const verifyToken = (token) => Effect.gen(function* () {
216
+ // Verify JWT
217
+ const payload = yield* tokenService.verifyToken(token);
218
+ // Hash token to find session
219
+ const tokenHash = yield* tokenService.hashToken(token);
220
+ const session = yield* sessionRepo.findByTokenHash(tokenHash);
221
+ if (!session) {
222
+ return yield* Effect.fail(new AuthError("INVALID_TOKEN", "Session not found"));
223
+ }
224
+ // Check if session is expired
225
+ if (session.expiresAt < Date.now()) {
226
+ return yield* Effect.fail(new AuthError("TOKEN_EXPIRED", "Session has expired"));
227
+ }
228
+ // Check if session is revoked
229
+ if (session.isRevoked) {
230
+ return yield* Effect.fail(new AuthError("SESSION_REVOKED", "Session has been revoked"));
231
+ }
232
+ // Find user
233
+ const user = yield* userRepo.findById(payload.sub);
234
+ if (!user) {
235
+ return yield* Effect.fail(new AuthError("USER_NOT_FOUND", "User not found"));
236
+ }
237
+ // Check if user is active
238
+ if (!user.isActive) {
239
+ return yield* Effect.fail(new AuthError("USER_INACTIVE", "User account is inactive"));
240
+ }
241
+ return { user, session };
242
+ });
243
+ return { register, login, logout, verifyToken };
244
+ }));
245
+ }
246
+ //# sourceMappingURL=AuthService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthService.js","sourceRoot":"","sources":["../../../../src/server/auth/services/AuthService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { AuthError, ValidationError } from "../../../shared/types.js";
3
+ declare const PasswordService_base: Context.TagClass<PasswordService, "PasswordService", {
4
+ readonly hash: (password: string) => Effect.Effect<string, AuthError>;
5
+ readonly verify: (password: string, hash: string) => Effect.Effect<boolean, AuthError>;
6
+ readonly validatePasswordStrength: (password: string) => Effect.Effect<void, ValidationError>;
7
+ }>;
8
+ export declare class PasswordService extends PasswordService_base {
9
+ static Live: Layer.Layer<PasswordService, never, never>;
10
+ }
11
+ export {};
12
+ //# sourceMappingURL=PasswordService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasswordService.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/services/PasswordService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;;mBAOnD,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;qBACpD,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,KACT,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC;uCACH,CACjC,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC;;AAV7C,qBAAa,eAAgB,SAAQ,oBAYlC;IACD,MAAM,CAAC,IAAI,6CAgET;CACH"}
@@ -0,0 +1,31 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import * as bcrypt from "bcrypt";
3
+ import { AuthError, ValidationError } from "../../../shared/types.js";
4
+ const SALT_ROUNDS = 12;
5
+ export class PasswordService extends Context.Tag("PasswordService")() {
6
+ static Live = Layer.succeed(this, {
7
+ hash: (password) => Effect.tryPromise({
8
+ try: () => bcrypt.hash(password, SALT_ROUNDS),
9
+ catch: (error) => new AuthError("HASH_FAILED", "Failed to hash password", error),
10
+ }),
11
+ verify: (password, hash) => Effect.tryPromise({
12
+ try: () => bcrypt.compare(password, hash),
13
+ catch: (error) => new AuthError("VERIFY_FAILED", "Failed to verify password", error),
14
+ }),
15
+ validatePasswordStrength: (password) => Effect.gen(function* () {
16
+ if (password.length < 8) {
17
+ return yield* Effect.fail(new ValidationError("password", "Password must be at least 8 characters long"));
18
+ }
19
+ if (!/[A-Z]/.test(password)) {
20
+ return yield* Effect.fail(new ValidationError("password", "Password must contain at least one uppercase letter"));
21
+ }
22
+ if (!/[a-z]/.test(password)) {
23
+ return yield* Effect.fail(new ValidationError("password", "Password must contain at least one lowercase letter"));
24
+ }
25
+ if (!/[0-9]/.test(password)) {
26
+ return yield* Effect.fail(new ValidationError("password", "Password must contain at least one number"));
27
+ }
28
+ }),
29
+ });
30
+ }
31
+ //# sourceMappingURL=PasswordService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PasswordService.js","sourceRoot":"","sources":["../../../../src/server/auth/services/PasswordService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEtE,MAAM,WAAW,GAAG,EAAE,CAAC;AAEvB,MAAM,OAAO,eAAgB,SAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAYhE;IACD,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,OAAO,CACzB,IAAI,EACJ;QACE,IAAI,EAAE,CAAC,QAAgB,EAAE,EAAE,CACzB,MAAM,CAAC,UAAU,CAAC;YAChB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC;YAC7C,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,SAAS,CACX,aAAa,EACb,yBAAyB,EACzB,KAAK,CACN;SACJ,CAAC;QAEJ,MAAM,EAAE,CAAC,QAAgB,EAAE,IAAY,EAAE,EAAE,CACzC,MAAM,CAAC,UAAU,CAAC;YAChB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC;YACzC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,SAAS,CACX,eAAe,EACf,2BAA2B,EAC3B,KAAK,CACN;SACJ,CAAC;QAEJ,wBAAwB,EAAE,CAAC,QAAgB,EAAE,EAAE,CAC7C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,eAAe,CACjB,UAAU,EACV,6CAA6C,CAC9C,CACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,eAAe,CACjB,UAAU,EACV,qDAAqD,CACtD,CACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,eAAe,CACjB,UAAU,EACV,qDAAqD,CACtD,CACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,IAAI,eAAe,CACjB,UAAU,EACV,2CAA2C,CAC5C,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;KACL,CACF,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { DatabaseService } from "../../database/db.js";
3
+ import { DatabaseError, Session } from "../../../shared/types.js";
4
+ export interface CreateSessionData {
5
+ userId: string;
6
+ tokenHash: string;
7
+ ipAddress: string | null;
8
+ userAgent: string | null;
9
+ expiresAt: number;
10
+ }
11
+ declare const SessionRepository_base: Context.TagClass<SessionRepository, "SessionRepository", {
12
+ readonly create: (data: CreateSessionData) => Effect.Effect<Session, DatabaseError>;
13
+ readonly findById: (id: string) => Effect.Effect<Session | undefined, DatabaseError>;
14
+ readonly findByTokenHash: (tokenHash: string) => Effect.Effect<Session | undefined, DatabaseError>;
15
+ readonly updateTokenHash: (id: string, tokenHash: string) => Effect.Effect<Session, DatabaseError>;
16
+ readonly revoke: (id: string) => Effect.Effect<void, DatabaseError>;
17
+ readonly revokeAllForUser: (userId: string) => Effect.Effect<void, DatabaseError>;
18
+ readonly deleteExpired: () => Effect.Effect<number, DatabaseError>;
19
+ }>;
20
+ export declare class SessionRepository extends SessionRepository_base {
21
+ static Live: Layer.Layer<SessionRepository, never, DatabaseService>;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=SessionRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionRepository.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/services/SessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;;qBAKoB,CACf,IAAI,EAAE,iBAAiB,KACpB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC;uBACvB,CACjB,EAAE,EAAE,MAAM,KACP,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,SAAS,EAAE,aAAa,CAAC;8BAC5B,CACxB,SAAS,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,OAAO,GAAG,SAAS,EAAE,aAAa,CAAC;8BAC5B,CACxB,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC;qBACzB,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;+BACxC,CACzB,MAAM,EAAE,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;4BACf,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;;AApBtE,qBAAa,iBAAkB,SAAQ,sBAsBpC;IACD,MAAM,CAAC,IAAI,yDA6LT;CACH"}
@@ -0,0 +1,101 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { randomUUID } from "node:crypto";
3
+ import { DatabaseService } from "../../database/db.js";
4
+ import { DatabaseError } from "../../../shared/types.js";
5
+ export class SessionRepository extends Context.Tag("SessionRepository")() {
6
+ static Live = Layer.effect(this, Effect.gen(function* (_) {
7
+ const db = yield* _(DatabaseService);
8
+ const create = (data) => Effect.gen(function* (_) {
9
+ const id = randomUUID();
10
+ const now = Date.now();
11
+ yield* _(db.run(`INSERT INTO sessions (
12
+ id, user_id, token_hash, ip_address, user_agent,
13
+ created_at, expires_at, is_revoked, revoked_at
14
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
15
+ id,
16
+ data.userId,
17
+ data.tokenHash,
18
+ data.ipAddress,
19
+ data.userAgent,
20
+ now,
21
+ data.expiresAt,
22
+ 0, // is_revoked = false
23
+ null, // revoked_at = null
24
+ ]));
25
+ const session = yield* _(findById(id));
26
+ if (!session) {
27
+ return yield* _(Effect.fail(new DatabaseError("Failed to retrieve created session")));
28
+ }
29
+ return session;
30
+ });
31
+ const findById = (id) => Effect.gen(function* (_) {
32
+ const row = yield* _(db.get("SELECT * FROM sessions WHERE id = ?", [id]));
33
+ if (!row) {
34
+ return undefined;
35
+ }
36
+ return {
37
+ id: row.id,
38
+ userId: row.user_id,
39
+ tokenHash: row.token_hash,
40
+ ipAddress: row.ip_address,
41
+ userAgent: row.user_agent,
42
+ createdAt: row.created_at,
43
+ expiresAt: row.expires_at,
44
+ isRevoked: row.is_revoked === 1,
45
+ revokedAt: row.revoked_at,
46
+ };
47
+ });
48
+ const findByTokenHash = (tokenHash) => Effect.gen(function* (_) {
49
+ const row = yield* _(db.get("SELECT * FROM sessions WHERE token_hash = ?", [tokenHash]));
50
+ if (!row) {
51
+ return undefined;
52
+ }
53
+ return {
54
+ id: row.id,
55
+ userId: row.user_id,
56
+ tokenHash: row.token_hash,
57
+ ipAddress: row.ip_address,
58
+ userAgent: row.user_agent,
59
+ createdAt: row.created_at,
60
+ expiresAt: row.expires_at,
61
+ isRevoked: row.is_revoked === 1,
62
+ revokedAt: row.revoked_at,
63
+ };
64
+ });
65
+ const updateTokenHash = (id, tokenHash) => Effect.gen(function* (_) {
66
+ yield* _(db.run(`UPDATE sessions
67
+ SET token_hash = ?
68
+ WHERE id = ?`, [tokenHash, id]));
69
+ const session = yield* _(findById(id));
70
+ if (!session) {
71
+ return yield* _(Effect.fail(new DatabaseError("Failed to retrieve updated session")));
72
+ }
73
+ return session;
74
+ });
75
+ const revoke = (id) => Effect.gen(function* (_) {
76
+ yield* _(db.run(`UPDATE sessions
77
+ SET is_revoked = 1, revoked_at = ?
78
+ WHERE id = ?`, [Date.now(), id]));
79
+ });
80
+ const revokeAllForUser = (userId) => Effect.gen(function* (_) {
81
+ yield* _(db.run(`UPDATE sessions
82
+ SET is_revoked = 1, revoked_at = ?
83
+ WHERE user_id = ? AND is_revoked = 0`, [Date.now(), userId]));
84
+ });
85
+ const deleteExpired = () => Effect.gen(function* (_) {
86
+ const now = Date.now();
87
+ const result = yield* _(db.run(`DELETE FROM sessions WHERE expires_at < ?`, [now]));
88
+ return result.changes ?? 0;
89
+ });
90
+ return {
91
+ create,
92
+ findById,
93
+ findByTokenHash,
94
+ updateTokenHash,
95
+ revoke,
96
+ revokeAllForUser,
97
+ deleteExpired,
98
+ };
99
+ }));
100
+ }
101
+ //# sourceMappingURL=SessionRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionRepository.js","sourceRoot":"","sources":["../../../../src/server/auth/services/SessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAW,MAAM,0BAA0B,CAAC;AAUlE,MAAM,OAAO,iBAAkB,SAAQ,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAsBpE;IACD,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CACxB,IAAI,EACJ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;QAErC,MAAM,MAAM,GAAG,CACb,IAAuB,EACgB,EAAE,CACzC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAEvB,KAAK,CAAC,CAAC,CAAC,CACN,EAAE,CAAC,GAAG,CACJ;;;mDAGqC,EACrC;gBACE,EAAE;gBACF,IAAI,CAAC,MAAM;gBACX,IAAI,CAAC,SAAS;gBACd,IAAI,CAAC,SAAS;gBACd,IAAI,CAAC,SAAS;gBACd,GAAG;gBACH,IAAI,CAAC,SAAS;gBACd,CAAC,EAAE,qBAAqB;gBACxB,IAAI,EAAE,oBAAoB;aAC3B,CACF,CACF,CAAC;YAEF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC,CAAC,CAAC,CACb,MAAM,CAAC,IAAI,CACT,IAAI,aAAa,CAAC,oCAAoC,CAAC,CACxD,CACF,CAAC;YACJ,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;QAEL,MAAM,QAAQ,GAAG,CACf,EAAU,EACyC,EAAE,CACrD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAClB,EAAE,CAAC,GAAG,CAUH,qCAAqC,EAAE,CAAC,EAAE,CAAC,CAAC,CAChD,CAAC;YAEF,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,OAAO;gBACL,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,MAAM,EAAE,GAAG,CAAC,OAAO;gBACnB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU,KAAK,CAAC;gBAC/B,SAAS,EAAE,GAAG,CAAC,UAAU;aAC1B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEL,MAAM,eAAe,GAAG,CACtB,SAAiB,EACkC,EAAE,CACrD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAClB,EAAE,CAAC,GAAG,CAUH,6CAA6C,EAAE,CAAC,SAAS,CAAC,CAAC,CAC/D,CAAC;YAEF,IAAI,CAAC,GAAG,EAAE,CAAC;gBACT,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,OAAO;gBACL,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,MAAM,EAAE,GAAG,CAAC,OAAO;gBACnB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU;gBACzB,SAAS,EAAE,GAAG,CAAC,UAAU,KAAK,CAAC;gBAC/B,SAAS,EAAE,GAAG,CAAC,UAAU;aAC1B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEL,MAAM,eAAe,GAAG,CACtB,EAAU,EACV,SAAiB,EACsB,EAAE,CACzC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,CAAC,CAAC,CACN,EAAE,CAAC,GAAG,CACJ;;4BAEc,EACd,CAAC,SAAS,EAAE,EAAE,CAAC,CAChB,CACF,CAAC;YAEF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,KAAK,CAAC,CAAC,CAAC,CACb,MAAM,CAAC,IAAI,CACT,IAAI,aAAa,CAAC,oCAAoC,CAAC,CACxD,CACF,CAAC;YACJ,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;QAEL,MAAM,MAAM,GAAG,CAAC,EAAU,EAAsC,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,CAAC,CAAC,CACN,EAAE,CAAC,GAAG,CACJ;;4BAEc,EACd,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,CACjB,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEL,MAAM,gBAAgB,GAAG,CACvB,MAAc,EACsB,EAAE,CACtC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,KAAK,CAAC,CAAC,CAAC,CACN,EAAE,CAAC,GAAG,CACJ;;oDAEsC,EACtC,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CACrB,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEL,MAAM,aAAa,GAAG,GAAyC,EAAE,CAC/D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CACrB,EAAE,CAAC,GAAG,CACJ,2CAA2C,EAC3C,CAAC,GAAG,CAAC,CACN,CACF,CAAC;YAEF,OAAO,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEL,OAAO;YACL,MAAM;YACN,QAAQ;YACR,eAAe;YACf,eAAe;YACf,MAAM;YACN,gBAAgB;YAChB,aAAa;SACd,CAAC;IACJ,CAAC,CAAC,CACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { AuthError, JWTPayload } from "../../../shared/types.js";
3
+ declare const TokenService_base: Context.TagClass<TokenService, "TokenService", {
4
+ readonly generateToken: (payload: Omit<JWTPayload, "iat" | "exp">) => Effect.Effect<string, AuthError>;
5
+ readonly verifyToken: (token: string) => Effect.Effect<JWTPayload, AuthError>;
6
+ readonly hashToken: (token: string) => Effect.Effect<string, never>;
7
+ }>;
8
+ export declare class TokenService extends TokenService_base {
9
+ static Live: Layer.Layer<TokenService, AuthError, never>;
10
+ }
11
+ export {};
12
+ //# sourceMappingURL=TokenService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenService.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/services/TokenService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAGhD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;;4BAUrC,CACtB,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,KAAK,CAAC,KACrC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC;0BACf,CACpB,KAAK,EAAE,MAAM,KACV,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC;wBACrB,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;;AATvE,qBAAa,YAAa,SAAQ,iBAW/B;IACD,MAAM,CAAC,IAAI,8CAsJT;CACH"}
@@ -0,0 +1,86 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import * as jose from "jose";
3
+ import { createHash } from "node:crypto";
4
+ import { AuthError } from "../../../shared/types.js";
5
+ const TOKEN_EXPIRY_DAYS = 7;
6
+ const ALGORITHM = "RS256";
7
+ let keyPair = null;
8
+ export class TokenService extends Context.Tag("TokenService")() {
9
+ static Live = Layer.effect(this, Effect.gen(function* (_) {
10
+ // Generate key pair if not already generated
11
+ if (!keyPair) {
12
+ const { publicKey, privateKey } = yield* _(Effect.tryPromise({
13
+ try: () => jose.generateKeyPair(ALGORITHM),
14
+ catch: (error) => new AuthError("KEY_GENERATION_FAILED", "Failed to generate RSA key pair", error),
15
+ }));
16
+ keyPair = { publicKey, privateKey };
17
+ }
18
+ const generateToken = (payload) => Effect.gen(function* (_) {
19
+ const currentKeyPair = keyPair;
20
+ if (!currentKeyPair) {
21
+ return yield* _(Effect.fail(new AuthError("KEY_NOT_INITIALIZED", "Key pair not initialized")));
22
+ }
23
+ const now = Math.floor(Date.now() / 1000);
24
+ // Type assertion needed for index signature compatibility
25
+ const sub = payload["sub"];
26
+ const username = payload["username"];
27
+ const email = payload["email"];
28
+ const jti = payload["jti"];
29
+ if (typeof sub !== "string" ||
30
+ typeof username !== "string" ||
31
+ typeof email !== "string" ||
32
+ typeof jti !== "string") {
33
+ return yield* _(Effect.fail(new AuthError("INVALID_PAYLOAD", "Payload contains invalid field types")));
34
+ }
35
+ const fullPayload = {
36
+ sub,
37
+ username,
38
+ email,
39
+ jti,
40
+ iat: now,
41
+ exp: now + TOKEN_EXPIRY_DAYS * 24 * 60 * 60,
42
+ };
43
+ return yield* _(Effect.tryPromise({
44
+ try: () => new jose.SignJWT(fullPayload)
45
+ .setProtectedHeader({ alg: ALGORITHM })
46
+ .setIssuedAt(fullPayload.iat)
47
+ .setExpirationTime(fullPayload.exp)
48
+ .sign(currentKeyPair.privateKey),
49
+ catch: (error) => new AuthError("TOKEN_GENERATION_FAILED", "Failed to generate JWT token", error),
50
+ }));
51
+ });
52
+ const verifyToken = (token) => Effect.gen(function* (_) {
53
+ const currentKeyPair = keyPair;
54
+ if (!currentKeyPair) {
55
+ return yield* _(Effect.fail(new AuthError("KEY_NOT_INITIALIZED", "Key pair not initialized")));
56
+ }
57
+ const result = yield* _(Effect.tryPromise({
58
+ try: () => jose.jwtVerify(token, currentKeyPair.publicKey),
59
+ catch: (error) => new AuthError("TOKEN_VERIFICATION_FAILED", "Invalid or expired token", error),
60
+ }));
61
+ const payload = result.payload;
62
+ // Validate payload structure using bracket notation for index signature properties
63
+ if (typeof payload.sub !== "string" ||
64
+ typeof payload["username"] !== "string" ||
65
+ typeof payload["email"] !== "string" ||
66
+ typeof payload.iat !== "number" ||
67
+ typeof payload.exp !== "number" ||
68
+ typeof payload.jti !== "string") {
69
+ return yield* _(Effect.fail(new AuthError("INVALID_TOKEN_PAYLOAD", "Token payload is missing required fields")));
70
+ }
71
+ return {
72
+ sub: payload.sub,
73
+ username: payload["username"],
74
+ email: payload["email"],
75
+ iat: payload.iat,
76
+ exp: payload.exp,
77
+ jti: payload.jti,
78
+ };
79
+ });
80
+ const hashToken = (token) => Effect.sync(() => {
81
+ return createHash("sha256").update(token).digest("hex");
82
+ });
83
+ return { generateToken, verifyToken, hashToken };
84
+ }));
85
+ }
86
+ //# sourceMappingURL=TokenService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenService.js","sourceRoot":"","sources":["../../../../src/server/auth/services/TokenService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { DatabaseService } from "../../database/db.js";
3
+ import { DatabaseError, User } from "../../../shared/types.js";
4
+ export interface CreateUserData {
5
+ username: string;
6
+ email: string;
7
+ passwordHash: string | null;
8
+ linuxUsername: string;
9
+ }
10
+ declare const UserRepository_base: Context.TagClass<UserRepository, "UserRepository", {
11
+ readonly create: (data: CreateUserData) => Effect.Effect<User, DatabaseError>;
12
+ readonly findById: (id: string) => Effect.Effect<User | undefined, DatabaseError>;
13
+ readonly findByEmail: (email: string) => Effect.Effect<User | undefined, DatabaseError>;
14
+ readonly findByUsername: (username: string) => Effect.Effect<User | undefined, DatabaseError>;
15
+ readonly update: (id: string, data: Partial<User>) => Effect.Effect<User, DatabaseError>;
16
+ readonly incrementFailedLogins: (id: string) => Effect.Effect<void, DatabaseError>;
17
+ readonly resetFailedLogins: (id: string) => Effect.Effect<void, DatabaseError>;
18
+ }>;
19
+ export declare class UserRepository extends UserRepository_base {
20
+ static Live: Layer.Layer<UserRepository, never, DatabaseService>;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=UserRepository.d.ts.map