myaidev-method 0.2.7 → 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.
- package/.claude/agents/wordpress-admin.md +271 -0
- package/.env.example +0 -1
- package/COOLIFY_DEPLOYMENT.md +1 -1
- package/DEV_WORKFLOW_GUIDE.md +1 -1
- package/PACKAGE_FIXES_SUMMARY.md +319 -0
- package/PAYLOADCMS_AUTH_UPDATE.md +248 -0
- package/PUBLISHING_GUIDE.md +1 -1
- package/README.md +7 -7
- package/USER_GUIDE.md +261 -1
- package/WORDPRESS_ADMIN_SCRIPTS.md +1 -1
- package/bin/cli.js +36 -0
- package/dist/server/.tsbuildinfo +1 -0
- package/dist/server/auth/controllers/AuthController.d.ts +34 -0
- package/dist/server/auth/controllers/AuthController.d.ts.map +1 -0
- package/dist/server/auth/controllers/AuthController.js +43 -0
- package/dist/server/auth/controllers/AuthController.js.map +1 -0
- package/dist/server/auth/example-usage.d.ts +53 -0
- package/dist/server/auth/example-usage.d.ts.map +1 -0
- package/dist/server/auth/example-usage.js +129 -0
- package/dist/server/auth/example-usage.js.map +1 -0
- package/dist/server/auth/index.d.ts +11 -0
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/auth/index.js +15 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/layers.d.ts +19 -0
- package/dist/server/auth/layers.d.ts.map +1 -0
- package/dist/server/auth/layers.js +33 -0
- package/dist/server/auth/layers.js.map +1 -0
- package/dist/server/auth/middleware/authMiddleware.d.ts +24 -0
- package/dist/server/auth/middleware/authMiddleware.d.ts.map +1 -0
- package/dist/server/auth/middleware/authMiddleware.js +65 -0
- package/dist/server/auth/middleware/authMiddleware.js.map +1 -0
- package/dist/server/auth/routes/authRoutes.d.ts +11 -0
- package/dist/server/auth/routes/authRoutes.d.ts.map +1 -0
- package/dist/server/auth/routes/authRoutes.js +213 -0
- package/dist/server/auth/routes/authRoutes.js.map +1 -0
- package/dist/server/auth/services/AuditLogService.d.ts +21 -0
- package/dist/server/auth/services/AuditLogService.d.ts.map +1 -0
- package/dist/server/auth/services/AuditLogService.js +28 -0
- package/dist/server/auth/services/AuditLogService.js.map +1 -0
- package/dist/server/auth/services/AuthService.d.ts +27 -0
- package/dist/server/auth/services/AuthService.d.ts.map +1 -0
- package/dist/server/auth/services/AuthService.js +246 -0
- package/dist/server/auth/services/AuthService.js.map +1 -0
- package/dist/server/auth/services/PasswordService.d.ts +12 -0
- package/dist/server/auth/services/PasswordService.d.ts.map +1 -0
- package/dist/server/auth/services/PasswordService.js +31 -0
- package/dist/server/auth/services/PasswordService.js.map +1 -0
- package/dist/server/auth/services/SessionRepository.d.ts +24 -0
- package/dist/server/auth/services/SessionRepository.d.ts.map +1 -0
- package/dist/server/auth/services/SessionRepository.js +101 -0
- package/dist/server/auth/services/SessionRepository.js.map +1 -0
- package/dist/server/auth/services/TokenService.d.ts +12 -0
- package/dist/server/auth/services/TokenService.d.ts.map +1 -0
- package/dist/server/auth/services/TokenService.js +86 -0
- package/dist/server/auth/services/TokenService.js.map +1 -0
- package/dist/server/auth/services/UserRepository.d.ts +23 -0
- package/dist/server/auth/services/UserRepository.d.ts.map +1 -0
- package/dist/server/auth/services/UserRepository.js +168 -0
- package/dist/server/auth/services/UserRepository.js.map +1 -0
- package/dist/server/auth/services/example.d.ts +26 -0
- package/dist/server/auth/services/example.d.ts.map +1 -0
- package/dist/server/auth/services/example.js +221 -0
- package/dist/server/auth/services/example.js.map +1 -0
- package/dist/server/auth/services/index.d.ts +6 -0
- package/dist/server/auth/services/index.d.ts.map +1 -0
- package/dist/server/auth/services/index.js +7 -0
- package/dist/server/auth/services/index.js.map +1 -0
- package/dist/server/database/db.d.ts +28 -0
- package/dist/server/database/db.d.ts.map +1 -0
- package/dist/server/database/db.js +91 -0
- package/dist/server/database/db.js.map +1 -0
- package/dist/server/database/schema.sql +95 -0
- package/dist/server/hono/app.d.ts +10 -0
- package/dist/server/hono/app.d.ts.map +1 -0
- package/dist/server/hono/app.js +26 -0
- package/dist/server/hono/app.js.map +1 -0
- package/dist/server/hono/routes.d.ts +12 -0
- package/dist/server/hono/routes.d.ts.map +1 -0
- package/dist/server/hono/routes.js +40 -0
- package/dist/server/hono/routes.js.map +1 -0
- package/dist/server/main.d.ts +2 -0
- package/dist/server/main.d.ts.map +1 -0
- package/dist/server/main.js +94 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/user-management/DirectoryService.d.ts +62 -0
- package/dist/server/user-management/DirectoryService.d.ts.map +1 -0
- package/dist/server/user-management/DirectoryService.js +201 -0
- package/dist/server/user-management/DirectoryService.js.map +1 -0
- package/dist/server/user-management/LinuxUserService.d.ts +71 -0
- package/dist/server/user-management/LinuxUserService.d.ts.map +1 -0
- package/dist/server/user-management/LinuxUserService.js +192 -0
- package/dist/server/user-management/LinuxUserService.js.map +1 -0
- package/dist/server/user-management/QuotaService.d.ts +59 -0
- package/dist/server/user-management/QuotaService.d.ts.map +1 -0
- package/dist/server/user-management/QuotaService.js +148 -0
- package/dist/server/user-management/QuotaService.js.map +1 -0
- package/dist/server/user-management/UserManagementService.d.ts +74 -0
- package/dist/server/user-management/UserManagementService.d.ts.map +1 -0
- package/dist/server/user-management/UserManagementService.js +122 -0
- package/dist/server/user-management/UserManagementService.js.map +1 -0
- package/dist/server/user-management/index.d.ts +26 -0
- package/dist/server/user-management/index.d.ts.map +1 -0
- package/dist/server/user-management/index.js +26 -0
- package/dist/server/user-management/index.js.map +1 -0
- package/dist/server/user-management/layers.d.ts +27 -0
- package/dist/server/user-management/layers.d.ts.map +1 -0
- package/dist/server/user-management/layers.js +37 -0
- package/dist/server/user-management/layers.js.map +1 -0
- package/dist/shared/types.d.ts +94 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +32 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +26 -6
- package/src/lib/payloadcms-utils.js +5 -12
- package/src/server/auth/ARCHITECTURE.md +575 -0
- package/src/server/auth/IMPLEMENTATION_SUMMARY.md +287 -0
- package/src/server/auth/QUICK_START.md +283 -0
- package/src/server/auth/README.md +290 -0
- package/src/server/auth/controllers/AuthController.ts +129 -0
- package/src/server/auth/example-usage.ts +159 -0
- package/src/server/auth/index.ts +19 -0
- package/src/server/auth/layers.ts +57 -0
- package/src/server/auth/middleware/authMiddleware.ts +118 -0
- package/src/server/auth/routes/authRoutes.ts +319 -0
- package/src/server/auth/services/AuditLogService.ts +81 -0
- package/src/server/auth/services/AuthService.ts +408 -0
- package/src/server/auth/services/IMPLEMENTATION_SUMMARY.md +404 -0
- package/src/server/auth/services/PasswordService.ts +85 -0
- package/src/server/auth/services/README.md +361 -0
- package/src/server/auth/services/SessionRepository.ts +227 -0
- package/src/server/auth/services/TokenService.ts +174 -0
- package/src/server/auth/services/UserRepository.ts +318 -0
- package/src/server/auth/services/example.ts +346 -0
- package/src/server/auth/services/index.ts +6 -0
- package/src/server/database/db.ts +161 -0
- package/src/server/database/schema.sql +95 -0
- package/src/server/hono/app.ts +41 -0
- package/src/server/main.ts +115 -0
- package/src/server/user-management/DirectoryService.ts +348 -0
- package/src/server/user-management/LinuxUserService.ts +338 -0
- package/src/server/user-management/QuotaService.ts +256 -0
- package/src/server/user-management/README.md +333 -0
- package/src/server/user-management/UserManagementService.ts +335 -0
- package/src/server/user-management/index.ts +26 -0
- package/src/server/user-management/layers.ts +51 -0
- package/src/shared/types.ts +111 -0
- package/src/templates/claude/agents/payloadcms-publish.md +34 -14
- package/src/templates/codex/commands/myai-astro-publish.md +8 -2
- package/src/templates/codex/commands/myai-content-writer.md +8 -2
- package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
- package/src/templates/codex/commands/myai-dev-architect.md +8 -2
- package/src/templates/codex/commands/myai-dev-code.md +8 -2
- package/src/templates/codex/commands/myai-dev-docs.md +8 -2
- package/src/templates/codex/commands/myai-dev-review.md +8 -2
- package/src/templates/codex/commands/myai-dev-test.md +8 -2
- package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
- package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
- package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
- package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
- package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
- package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
- package/src/templates/docs/wordpress-troubleshoot.js +2 -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":"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"}
|
|
@@ -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
|