myaidev-method 0.2.8 → 0.2.10

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 (158) 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 +70 -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/coolify-deploy.md +50 -50
  144. package/src/templates/claude/agents/payloadcms-publish.md +46 -18
  145. package/src/templates/codex/commands/myai-astro-publish.md +8 -2
  146. package/src/templates/codex/commands/myai-content-writer.md +8 -2
  147. package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
  148. package/src/templates/codex/commands/myai-dev-architect.md +8 -2
  149. package/src/templates/codex/commands/myai-dev-code.md +8 -2
  150. package/src/templates/codex/commands/myai-dev-docs.md +8 -2
  151. package/src/templates/codex/commands/myai-dev-review.md +8 -2
  152. package/src/templates/codex/commands/myai-dev-test.md +8 -2
  153. package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
  154. package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
  155. package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
  156. package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
  157. package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
  158. package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
@@ -0,0 +1,408 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import {
3
+ AuthError,
4
+ DatabaseError,
5
+ Session,
6
+ User,
7
+ ValidationError,
8
+ } from "../../../shared/types.js";
9
+ import { PasswordService } from "./PasswordService.js";
10
+ import { TokenService } from "./TokenService.js";
11
+ import { UserRepository } from "./UserRepository.js";
12
+ import { SessionRepository } from "./SessionRepository.js";
13
+ import { AuditLogService } from "./AuditLogService.js";
14
+ import { UserManagementService } from "../../user-management/UserManagementService.js";
15
+
16
+ const MAX_FAILED_ATTEMPTS = 5;
17
+ const LOCKOUT_DURATION_MS = 15 * 60 * 1000; // 15 minutes
18
+
19
+ export interface AuthServiceDeps {
20
+ readonly register: (
21
+ username: string,
22
+ email: string,
23
+ password: string,
24
+ ipAddress?: string | null,
25
+ userAgent?: string | null
26
+ ) => Effect.Effect<User, AuthError | ValidationError | DatabaseError>;
27
+ readonly login: (
28
+ email: string,
29
+ password: string,
30
+ ipAddress?: string | null,
31
+ userAgent?: string | null
32
+ ) => Effect.Effect<
33
+ { user: User; token: string; session: Session },
34
+ AuthError | DatabaseError
35
+ >;
36
+ readonly logout: (
37
+ sessionId: string,
38
+ userId: string
39
+ ) => Effect.Effect<void, DatabaseError>;
40
+ readonly verifyToken: (
41
+ token: string
42
+ ) => Effect.Effect<{ user: User; session: Session }, AuthError | DatabaseError>;
43
+ }
44
+
45
+ export class AuthService extends Context.Tag("AuthService")<
46
+ AuthService,
47
+ AuthServiceDeps
48
+ >() {
49
+ static Live = Layer.effect(
50
+ this,
51
+ Effect.gen(function* () {
52
+ const passwordService = yield* PasswordService;
53
+ const tokenService = yield* TokenService;
54
+ const userRepo = yield* UserRepository;
55
+ const sessionRepo = yield* SessionRepository;
56
+ const auditLog = yield* AuditLogService;
57
+ const userManagement = yield* UserManagementService;
58
+
59
+ const sanitizeLinuxUsername = (username: string): string => {
60
+ // Convert to lowercase, replace non-alphanumeric with underscore
61
+ let sanitized = username
62
+ .toLowerCase()
63
+ .replace(/[^a-z0-9_]/g, "_")
64
+ .replace(/^[0-9_]+/, "") // Cannot start with number or underscore
65
+ .slice(0, 32); // Max length for Linux usernames
66
+
67
+ // Ensure it starts with a letter
68
+ if (!/^[a-z]/.test(sanitized)) {
69
+ sanitized = "user_" + sanitized;
70
+ }
71
+
72
+ return sanitized;
73
+ };
74
+
75
+ const generateUniqueLinuxUsername = (
76
+ baseUsername: string
77
+ ): Effect.Effect<string, DatabaseError> =>
78
+ Effect.gen(function* () {
79
+ let linuxUsername = sanitizeLinuxUsername(baseUsername);
80
+ let counter = 0;
81
+
82
+ // Keep trying until we find a unique username
83
+ while (true) {
84
+ const existingUser = yield* userRepo.findByUsername(linuxUsername);
85
+ if (!existingUser) {
86
+ return linuxUsername;
87
+ }
88
+
89
+ counter++;
90
+ const suffix = `_${counter}`;
91
+ const maxBase = 32 - suffix.length;
92
+ linuxUsername =
93
+ sanitizeLinuxUsername(baseUsername).slice(0, maxBase) + suffix;
94
+ }
95
+ });
96
+
97
+ const validateEmail = (email: string): Effect.Effect<void, ValidationError> =>
98
+ Effect.gen(function* () {
99
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
100
+ if (!emailRegex.test(email)) {
101
+ return yield* Effect.fail(
102
+ new ValidationError("email", "Invalid email format")
103
+ );
104
+ }
105
+ });
106
+
107
+ const validateUsername = (
108
+ username: string
109
+ ): Effect.Effect<void, ValidationError> =>
110
+ Effect.gen(function* () {
111
+ if (username.length < 3) {
112
+ return yield* Effect.fail(
113
+ new ValidationError(
114
+ "username",
115
+ "Username must be at least 3 characters long"
116
+ )
117
+ );
118
+ }
119
+
120
+ if (username.length > 32) {
121
+ return yield* Effect.fail(
122
+ new ValidationError(
123
+ "username",
124
+ "Username must be at most 32 characters long"
125
+ )
126
+ );
127
+ }
128
+
129
+ if (!/^[a-zA-Z0-9_]+$/.test(username)) {
130
+ return yield* Effect.fail(
131
+ new ValidationError(
132
+ "username",
133
+ "Username can only contain letters, numbers, and underscores"
134
+ )
135
+ );
136
+ }
137
+ });
138
+
139
+ const isAccountLocked = (user: User): boolean => {
140
+ if (user.failedLoginAttempts < MAX_FAILED_ATTEMPTS) {
141
+ return false;
142
+ }
143
+
144
+ // Check if lockout period has expired
145
+ const now = Date.now();
146
+ const lockoutExpiry = user.updatedAt + LOCKOUT_DURATION_MS;
147
+ return now < lockoutExpiry;
148
+ };
149
+
150
+ const register: AuthServiceDeps["register"] = (
151
+ username,
152
+ email,
153
+ password,
154
+ ipAddress = null,
155
+ userAgent = null
156
+ ) =>
157
+ Effect.gen(function* () {
158
+ // Validate inputs
159
+ yield* validateUsername(username);
160
+ yield* validateEmail(email);
161
+ yield* passwordService.validatePasswordStrength(password);
162
+
163
+ // Check if user already exists
164
+ const existingEmail = yield* userRepo.findByEmail(email);
165
+ if (existingEmail) {
166
+ return yield* Effect.fail(
167
+ new ValidationError("email", "Email already registered")
168
+ );
169
+ }
170
+
171
+ const existingUsername = yield* userRepo.findByUsername(username);
172
+ if (existingUsername) {
173
+ return yield* Effect.fail(
174
+ new ValidationError("username", "Username already taken")
175
+ );
176
+ }
177
+
178
+ // Hash password
179
+ const passwordHash = yield* passwordService.hash(password);
180
+
181
+ // Generate unique Linux username
182
+ const linuxUsername = yield* generateUniqueLinuxUsername(username);
183
+
184
+ // Create user in database
185
+ const user = yield* userRepo.create({
186
+ username,
187
+ email,
188
+ passwordHash,
189
+ linuxUsername,
190
+ });
191
+
192
+ // Create Linux user with home directory and Claude config
193
+ // This runs in the background and doesn't block registration
194
+ // If it fails, user can still authenticate but won't have Linux access
195
+ yield* userManagement
196
+ .createUser({
197
+ username: linuxUsername,
198
+ email,
199
+ shell: "/bin/rbash", // Restricted bash for security
200
+ diskQuotaMB: 2048, // 2GB default quota
201
+ })
202
+ .pipe(
203
+ Effect.catchAll((error) => {
204
+ // Log the error but don't fail registration
205
+ console.error(
206
+ `Failed to create Linux user for ${linuxUsername}:`,
207
+ error
208
+ );
209
+ return Effect.succeed(void 0);
210
+ })
211
+ );
212
+
213
+ // Log audit event
214
+ yield* auditLog.log({
215
+ userId: user.id,
216
+ action: "USER_REGISTERED",
217
+ resourceType: "user",
218
+ resourceId: user.id,
219
+ ipAddress: ipAddress ?? null,
220
+ userAgent: userAgent ?? null,
221
+ });
222
+
223
+ return user;
224
+ });
225
+
226
+ const login: AuthServiceDeps["login"] = (
227
+ email,
228
+ password,
229
+ ipAddress = null,
230
+ userAgent = null
231
+ ) =>
232
+ Effect.gen(function* () {
233
+ // Find user by email
234
+ const user = yield* userRepo.findByEmail(email);
235
+ if (!user) {
236
+ return yield* Effect.fail(
237
+ new AuthError("INVALID_CREDENTIALS", "Invalid email or password")
238
+ );
239
+ }
240
+
241
+ // Check if account is locked
242
+ if (isAccountLocked(user)) {
243
+ yield* auditLog.log({
244
+ userId: user.id,
245
+ action: "LOGIN_FAILED",
246
+ resourceType: "user",
247
+ resourceId: user.id,
248
+ ipAddress: ipAddress ?? null,
249
+ userAgent: userAgent ?? null,
250
+ details: "Account locked due to too many failed attempts",
251
+ });
252
+
253
+ return yield* Effect.fail(
254
+ new AuthError(
255
+ "ACCOUNT_LOCKED",
256
+ "Account is locked due to too many failed login attempts. Please try again in 15 minutes."
257
+ )
258
+ );
259
+ }
260
+
261
+ // Verify password
262
+ if (!user.passwordHash) {
263
+ return yield* Effect.fail(
264
+ new AuthError(
265
+ "INVALID_CREDENTIALS",
266
+ "Invalid email or password"
267
+ )
268
+ );
269
+ }
270
+
271
+ const isPasswordValid = yield* passwordService.verify(
272
+ password,
273
+ user.passwordHash
274
+ );
275
+
276
+ if (!isPasswordValid) {
277
+ // Increment failed login attempts
278
+ yield* userRepo.incrementFailedLogins(user.id);
279
+
280
+ yield* auditLog.log({
281
+ userId: user.id,
282
+ action: "LOGIN_FAILED",
283
+ resourceType: "user",
284
+ resourceId: user.id,
285
+ ipAddress: ipAddress ?? null,
286
+ userAgent: userAgent ?? null,
287
+ details: "Invalid password",
288
+ });
289
+
290
+ return yield* Effect.fail(
291
+ new AuthError("INVALID_CREDENTIALS", "Invalid email or password")
292
+ );
293
+ }
294
+
295
+ // Reset failed login attempts on successful login
296
+ yield* userRepo.resetFailedLogins(user.id);
297
+
298
+ // Update last login time
299
+ const updatedUser = yield* userRepo.update(user.id, {
300
+ lastLoginAt: Date.now(),
301
+ });
302
+
303
+ // Generate a unique placeholder token to avoid constraint violations
304
+ const expiresAt = Date.now() + 7 * 24 * 60 * 60 * 1000; // 7 days
305
+ const placeholderToken = `placeholder_${user.id}_${Date.now()}_${Math.random()}`;
306
+ const placeholderHash = yield* tokenService.hashToken(placeholderToken);
307
+
308
+ // Create session with placeholder hash
309
+ const session = yield* sessionRepo.create({
310
+ userId: user.id,
311
+ tokenHash: placeholderHash,
312
+ ipAddress: ipAddress ?? null,
313
+ userAgent: userAgent ?? null,
314
+ expiresAt,
315
+ });
316
+
317
+ // Generate JWT token with the session ID
318
+ const token = yield* tokenService.generateToken({
319
+ sub: user.id,
320
+ username: user.username,
321
+ email: user.email,
322
+ jti: session.id,
323
+ });
324
+
325
+ // Update the session with the actual token hash
326
+ const actualTokenHash = yield* tokenService.hashToken(token);
327
+ const finalSession = yield* sessionRepo.updateTokenHash(session.id, actualTokenHash);
328
+
329
+ // Log audit event
330
+ yield* auditLog.log({
331
+ userId: user.id,
332
+ action: "USER_LOGIN",
333
+ resourceType: "session",
334
+ resourceId: finalSession.id,
335
+ ipAddress: ipAddress ?? null,
336
+ userAgent: userAgent ?? null,
337
+ });
338
+
339
+ return { user: updatedUser, token, session: finalSession };
340
+ });
341
+
342
+ const logout: AuthServiceDeps["logout"] = (sessionId, userId) =>
343
+ Effect.gen(function* () {
344
+ // Revoke session
345
+ yield* sessionRepo.revoke(sessionId);
346
+
347
+ // Log audit event
348
+ yield* auditLog.log({
349
+ userId,
350
+ action: "USER_LOGOUT",
351
+ resourceType: "session",
352
+ resourceId: sessionId,
353
+ ipAddress: null,
354
+ userAgent: null,
355
+ });
356
+ });
357
+
358
+ const verifyToken: AuthServiceDeps["verifyToken"] = (token) =>
359
+ Effect.gen(function* () {
360
+ // Verify JWT
361
+ const payload = yield* tokenService.verifyToken(token);
362
+
363
+ // Hash token to find session
364
+ const tokenHash = yield* tokenService.hashToken(token);
365
+ const session = yield* sessionRepo.findByTokenHash(tokenHash);
366
+
367
+ if (!session) {
368
+ return yield* Effect.fail(
369
+ new AuthError("INVALID_TOKEN", "Session not found")
370
+ );
371
+ }
372
+
373
+ // Check if session is expired
374
+ if (session.expiresAt < Date.now()) {
375
+ return yield* Effect.fail(
376
+ new AuthError("TOKEN_EXPIRED", "Session has expired")
377
+ );
378
+ }
379
+
380
+ // Check if session is revoked
381
+ if (session.isRevoked) {
382
+ return yield* Effect.fail(
383
+ new AuthError("SESSION_REVOKED", "Session has been revoked")
384
+ );
385
+ }
386
+
387
+ // Find user
388
+ const user = yield* userRepo.findById(payload.sub);
389
+ if (!user) {
390
+ return yield* Effect.fail(
391
+ new AuthError("USER_NOT_FOUND", "User not found")
392
+ );
393
+ }
394
+
395
+ // Check if user is active
396
+ if (!user.isActive) {
397
+ return yield* Effect.fail(
398
+ new AuthError("USER_INACTIVE", "User account is inactive")
399
+ );
400
+ }
401
+
402
+ return { user, session };
403
+ });
404
+
405
+ return { register, login, logout, verifyToken };
406
+ })
407
+ );
408
+ }