@simple-auth-kit/cli 1.4.3 → 1.5.0

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 (133) hide show
  1. package/package.json +1 -1
  2. package/registry/combos/express-drizzle/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
  3. package/registry/combos/express-drizzle/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +23 -7
  4. package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/middleware}/response-envelope.middleware.ts +15 -3
  5. package/registry/combos/express-drizzle/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +32 -9
  6. package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
  7. package/registry/combos/express-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
  8. package/registry/combos/express-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +20 -6
  9. package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
  10. package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +395 -0
  11. package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +52 -19
  12. package/registry/combos/express-drizzle/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
  13. package/registry/combos/express-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +28 -10
  14. package/registry/combos/express-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +239 -48
  15. package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +497 -0
  16. package/registry/combos/express-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +480 -109
  17. package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +69 -23
  18. package/registry/combos/express-drizzle/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
  19. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +147 -28
  20. package/registry/combos/express-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +30 -11
  21. package/registry/combos/express-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +319 -70
  22. package/registry/combos/express-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +127 -30
  23. package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +534 -0
  24. package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +194 -0
  25. package/registry/combos/express-drizzle/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +544 -122
  26. package/registry/combos/express-prisma/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
  27. package/registry/combos/express-prisma/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +19 -6
  28. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/middleware}/response-envelope.middleware.ts +15 -3
  29. package/registry/combos/express-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +27 -9
  30. package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
  31. package/registry/combos/express-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
  32. package/registry/combos/express-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
  33. package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
  34. package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +398 -0
  35. package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +57 -22
  36. package/registry/combos/express-prisma/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
  37. package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +67 -19
  38. package/registry/combos/express-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  39. package/registry/combos/express-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +199 -48
  40. package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +508 -0
  41. package/registry/combos/express-prisma/variants/base/src/seed.ts +78 -20
  42. package/registry/combos/express-prisma/variants/base/src/{auth.service.ts → services/auth.service.ts} +454 -105
  43. package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +81 -26
  44. package/registry/combos/express-prisma/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
  45. package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +147 -28
  46. package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +73 -21
  47. package/registry/combos/express-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  48. package/registry/combos/express-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +272 -64
  49. package/registry/combos/express-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +86 -23
  50. package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +545 -0
  51. package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +194 -0
  52. package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +109 -29
  53. package/registry/combos/express-prisma/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +518 -119
  54. package/registry/combos/nestjs-drizzle/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
  55. package/registry/combos/nestjs-drizzle/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
  56. package/registry/combos/nestjs-drizzle/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
  57. package/registry/combos/nestjs-drizzle/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
  58. package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/interceptors}/response.interceptor.ts +10 -2
  59. package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +63 -0
  60. package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
  61. package/registry/combos/nestjs-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
  62. package/registry/combos/nestjs-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +21 -7
  63. package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
  64. package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1097 -307
  65. package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +44 -20
  66. package/registry/combos/nestjs-drizzle/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +228 -62
  67. package/registry/combos/nestjs-drizzle/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
  68. package/registry/combos/nestjs-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +27 -10
  69. package/registry/combos/nestjs-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +249 -51
  70. package/registry/combos/nestjs-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +499 -116
  71. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +48 -22
  72. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +269 -70
  73. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
  74. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
  75. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +29 -11
  76. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +323 -71
  77. package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +131 -30
  78. package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +988 -0
  79. package/registry/combos/nestjs-prisma/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
  80. package/registry/combos/nestjs-prisma/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
  81. package/registry/combos/nestjs-prisma/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
  82. package/registry/combos/nestjs-prisma/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
  83. package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/interceptors}/response.interceptor.ts +10 -2
  84. package/registry/combos/nestjs-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +25 -8
  85. package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
  86. package/registry/combos/nestjs-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
  87. package/registry/combos/nestjs-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
  88. package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
  89. package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1097 -307
  90. package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +59 -28
  91. package/registry/combos/nestjs-prisma/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +469 -110
  92. package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.gateway.ts → gateways/audit-log.gateway.ts} +22 -6
  93. package/registry/combos/nestjs-prisma/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
  94. package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +98 -27
  95. package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  96. package/registry/combos/nestjs-prisma/variants/base/src/{country.repository.ts → repositories/country.repository.ts} +98 -25
  97. package/registry/combos/nestjs-prisma/variants/base/src/{customer.repository.ts → repositories/customer.repository.ts} +106 -27
  98. package/registry/combos/nestjs-prisma/variants/base/src/{language.repository.ts → repositories/language.repository.ts} +93 -25
  99. package/registry/combos/nestjs-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +229 -55
  100. package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +78 -20
  101. package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +1064 -0
  102. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +55 -27
  103. package/registry/combos/nestjs-prisma/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +544 -128
  104. package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
  105. package/registry/combos/nestjs-prisma/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
  106. package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +104 -29
  107. package/registry/combos/nestjs-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
  108. package/registry/combos/nestjs-prisma/variants/workspaces/src/{country.repository.ts → repositories/country.repository.ts} +127 -26
  109. package/registry/combos/nestjs-prisma/variants/workspaces/src/{customer.repository.ts → repositories/customer.repository.ts} +137 -28
  110. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +243 -0
  111. package/registry/combos/nestjs-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +314 -74
  112. package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +94 -24
  113. package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +109 -29
  114. package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +961 -0
  115. package/registry.json +4 -4
  116. package/simple-auth-kit.ts +29 -2
  117. package/registry/combos/express-drizzle/shared/src/auth.router.ts +0 -266
  118. package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +0 -35
  119. package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +0 -307
  120. package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +0 -323
  121. package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +0 -123
  122. package/registry/combos/express-prisma/shared/src/auth.router.ts +0 -269
  123. package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +0 -29
  124. package/registry/combos/express-prisma/variants/base/src/admin.router.ts +0 -314
  125. package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +0 -330
  126. package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +0 -123
  127. package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +0 -36
  128. package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +0 -29
  129. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +0 -553
  130. package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +0 -29
  131. package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +0 -618
  132. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +0 -509
  133. package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +0 -143
@@ -1,6 +1,6 @@
1
1
  import type { NextFunction, Request, Response } from "express";
2
2
  import { AuthCoreError } from "@/lib/auth/core/types.js";
3
- import { HttpError } from "./http-error.js";
3
+ import { HttpError } from "../http-error.js";
4
4
 
5
5
  /**
6
6
  * Replaces the reference combo's `AuthCoreErrorFilter` (a Nest global `@Catch` exception
@@ -11,19 +11,32 @@ import { HttpError } from "./http-error.js";
11
11
  * maps this combo's own `HttpError` to its intended status code — the plain-Express stand-in
12
12
  * for the NestJS exception classes the reference throws.
13
13
  */
14
- export function authCoreErrorMiddleware(err: unknown, _req: Request, res: Response, next: NextFunction): void {
14
+ export function authCoreErrorMiddleware(
15
+ err: unknown,
16
+ _req: Request,
17
+ res: Response,
18
+ next: NextFunction,
19
+ ): void {
15
20
  if (res.headersSent) {
16
21
  next(err);
17
22
  return;
18
23
  }
19
24
 
20
25
  if (err instanceof AuthCoreError) {
21
- res.status(401).json({ statusCode: 401, code: err.code, message: err.message });
26
+ res
27
+ .status(401)
28
+ .json({ statusCode: 401, code: err.code, message: err.message });
22
29
  return;
23
30
  }
24
31
 
25
32
  if (err instanceof HttpError) {
26
- res.status(err.statusCode).json({ statusCode: err.statusCode, code: err.name, message: err.message });
33
+ res
34
+ .status(err.statusCode)
35
+ .json({
36
+ statusCode: err.statusCode,
37
+ code: err.name,
38
+ message: err.message,
39
+ });
27
40
  return;
28
41
  }
29
42
 
@@ -1,8 +1,8 @@
1
1
  import type { NextFunction, Request, Response } from "express";
2
2
  import { verifyAccessToken } from "@/lib/auth/core/token-service.js";
3
- import { KeyProviderService } from "./key-provider.js";
4
- import "./request-context.js";
5
- import { SessionRepository } from "./session.repository.js";
3
+ import { KeyProviderService } from "../key-provider.js";
4
+ import "../request-context.js";
5
+ import { SessionRepository } from "../repositories/session.repository.js";
6
6
 
7
7
  export interface AuthMiddlewareDeps {
8
8
  keys: KeyProviderService;
@@ -19,16 +19,29 @@ export interface AuthMiddlewareDeps {
19
19
  * is the idiomatic equivalent.
20
20
  */
21
21
  export function createAuthMiddleware(deps: AuthMiddlewareDeps) {
22
- return async function authMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
22
+ return async function authMiddleware(
23
+ req: Request,
24
+ res: Response,
25
+ next: NextFunction,
26
+ ): Promise<void> {
23
27
  const header = req.headers["authorization"];
24
28
  if (!header?.startsWith("Bearer ")) {
25
- res.status(401).json({ statusCode: 401, code: "UNAUTHORIZED", message: "missing bearer token" });
29
+ res
30
+ .status(401)
31
+ .json({
32
+ statusCode: 401,
33
+ code: "UNAUTHORIZED",
34
+ message: "missing bearer token",
35
+ });
26
36
  return;
27
37
  }
28
38
 
29
39
  try {
30
40
  req.auth = await verifyAccessToken(
31
- { secret: deps.keys.secret, isDenylisted: (jti) => deps.sessions.isDenylisted(jti) },
41
+ {
42
+ secret: deps.keys.secret,
43
+ isDenylisted: (jti) => deps.sessions.isDenylisted(jti),
44
+ },
32
45
  header.slice("Bearer ".length),
33
46
  );
34
47
  } catch (err) {
@@ -36,10 +36,22 @@ export function responseEnvelope(): RequestHandler {
36
36
  const originalJson = res.json.bind(res);
37
37
  res.json = ((body: unknown) => {
38
38
  if (res.statusCode >= 400) {
39
- const shaped = body && typeof body === "object" ? (body as Record<string, unknown>) : { message: body };
40
- return originalJson({ success: false, statusCode: res.statusCode, ...shaped });
39
+ const shaped =
40
+ body && typeof body === "object"
41
+ ? (body as Record<string, unknown>)
42
+ : { message: body };
43
+ return originalJson({
44
+ success: false,
45
+ statusCode: res.statusCode,
46
+ ...shaped,
47
+ });
41
48
  }
42
- return originalJson({ success: true, statusCode: res.statusCode, message: deriveMessage(req.method), data: body });
49
+ return originalJson({
50
+ success: true,
51
+ statusCode: res.statusCode,
52
+ message: deriveMessage(req.method),
53
+ data: body,
54
+ });
43
55
  }) as typeof res.json;
44
56
  next();
45
57
  };
@@ -1,22 +1,32 @@
1
1
  import type { OAuthStoreDeps } from "@/lib/auth/core/oauth.js";
2
- import { PrismaClient } from "../generated/prisma/client.js";
3
- import { HttpError } from "./http-error.js";
4
- import { toId } from "./id.helper.js";
2
+ import { PrismaClient } from "../../generated/prisma/client.js";
3
+ import { HttpError } from "../http-error.js";
4
+ import { toId } from "../id.helper.js";
5
5
 
6
6
  // Plain class, no DI container — constructed directly with a PrismaClient in
7
7
  // create-auth-app.ts. Identical Prisma queries to the reference combo.
8
8
  export class OAuthRepository implements OAuthStoreDeps {
9
9
  constructor(private readonly prisma: PrismaClient) {}
10
10
 
11
- async findAccountByProvider(provider: string, providerAccountId: string): Promise<{ userId: string } | null> {
11
+ async findAccountByProvider(
12
+ provider: string,
13
+ providerAccountId: string,
14
+ ): Promise<{ userId: string } | null> {
12
15
  const row = await this.prisma.oAuthAccount.findUnique({
13
16
  where: { provider_providerAccountId: { provider, providerAccountId } },
14
17
  });
15
18
  return row ? { userId: row.userId.toString() } : null;
16
19
  }
17
20
 
18
- async linkAccount(input: { userId: string; provider: string; providerAccountId: string; email?: string }): Promise<void> {
19
- await this.prisma.oAuthAccount.create({ data: { ...input, userId: toId(input.userId) } });
21
+ async linkAccount(input: {
22
+ userId: string;
23
+ provider: string;
24
+ providerAccountId: string;
25
+ email?: string;
26
+ }): Promise<void> {
27
+ await this.prisma.oAuthAccount.create({
28
+ data: { ...input, userId: toId(input.userId) },
29
+ });
20
30
  }
21
31
 
22
32
  async findUserByVerifiedEmail(email: string): Promise<{ id: string } | null> {
@@ -25,9 +35,17 @@ export class OAuthRepository implements OAuthStoreDeps {
25
35
  }
26
36
 
27
37
  /** OAuth-only signup: no password is ever set, which is why `User.passwordHash` is nullable. */
28
- async createUserFromOAuth(input: { email?: string }): Promise<{ id: string }> {
29
- if (!input.email) throw new HttpError(400, "OAuth provider did not return an email address");
30
- const row = await this.prisma.user.create({ data: { email: input.email, passwordHash: null } });
38
+ async createUserFromOAuth(input: {
39
+ email?: string;
40
+ }): Promise<{ id: string }> {
41
+ if (!input.email)
42
+ throw new HttpError(
43
+ 400,
44
+ "OAuth provider did not return an email address",
45
+ );
46
+ const row = await this.prisma.user.create({
47
+ data: { email: input.email, passwordHash: null },
48
+ });
31
49
  return { id: row.id.toString() };
32
50
  }
33
51
  }
@@ -0,0 +1,55 @@
1
+ import type { PasswordResetStoreDeps } from "@/lib/auth/core/password-reset.js";
2
+ import { PrismaClient } from "../../generated/prisma/client.js";
3
+ import { toId } from "../id.helper.js";
4
+
5
+ // Plain class, no DI container — constructed directly with a PrismaClient in
6
+ // create-auth-app.ts. Identical Prisma queries to the reference combo.
7
+ export class PasswordResetRepository implements PasswordResetStoreDeps {
8
+ constructor(private readonly prisma: PrismaClient) {}
9
+
10
+ async saveResetToken(input: {
11
+ userId: string;
12
+ tokenHash: string;
13
+ expiresAt: string;
14
+ }): Promise<void> {
15
+ await this.prisma.passwordResetToken.create({
16
+ data: {
17
+ userId: toId(input.userId),
18
+ tokenHash: input.tokenHash,
19
+ expiresAt: new Date(input.expiresAt),
20
+ },
21
+ });
22
+ }
23
+
24
+ async findValidResetToken(
25
+ tokenHash: string,
26
+ ): Promise<{
27
+ userId: string;
28
+ expiresAt: string;
29
+ consumedAt: string | null;
30
+ } | null> {
31
+ const row = await this.prisma.passwordResetToken.findUnique({
32
+ where: { tokenHash },
33
+ });
34
+ if (!row) return null;
35
+ return {
36
+ userId: row.userId.toString(),
37
+ expiresAt: row.expiresAt.toISOString(),
38
+ consumedAt: row.consumedAt?.toISOString() ?? null,
39
+ };
40
+ }
41
+
42
+ async consumeResetToken(tokenHash: string): Promise<void> {
43
+ await this.prisma.passwordResetToken.update({
44
+ where: { tokenHash },
45
+ data: { consumedAt: new Date() },
46
+ });
47
+ }
48
+
49
+ async setPasswordHash(userId: string, passwordHash: string): Promise<void> {
50
+ await this.prisma.user.update({
51
+ where: { id: toId(userId) },
52
+ data: { passwordHash },
53
+ });
54
+ }
55
+ }
@@ -1,10 +1,14 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import type { SessionStoreDeps } from "@/lib/auth/core/session-policy.js";
3
- import type { AuditEvent, Revoker, SessionRecord } from "@/lib/auth/core/types.js";
4
- import { PrismaClient } from "../generated/prisma/client.js";
5
- import type { AuthConfig } from "./auth.config.js";
3
+ import type {
4
+ AuditEvent,
5
+ Revoker,
6
+ SessionRecord,
7
+ } from "@/lib/auth/core/types.js";
8
+ import { PrismaClient } from "../../generated/prisma/client.js";
9
+ import type { AuthConfig } from "../auth.config.js";
6
10
  import { AuditLogRepository } from "./audit-log.repository.js";
7
- import { toId, toIdOrNull } from "./id.helper.js";
11
+ import { toId, toIdOrNull } from "../id.helper.js";
8
12
 
9
13
  function toSessionRecord(row: {
10
14
  id: bigint;
@@ -64,7 +68,9 @@ export class SessionRepository implements SessionStoreDeps {
64
68
  }
65
69
 
66
70
  async findSession(id: string): Promise<SessionRecord | null> {
67
- const row = await this.prisma.session.findUnique({ where: { id: toId(id) } });
71
+ const row = await this.prisma.session.findUnique({
72
+ where: { id: toId(id) },
73
+ });
68
74
  return row ? toSessionRecord(row) : null;
69
75
  }
70
76
 
@@ -92,7 +98,12 @@ export class SessionRepository implements SessionStoreDeps {
92
98
  * `createdBy` is the user themselves. A session is only ever created by the person logging in,
93
99
  * so recording it costs nothing and leaves the column meaningful rather than always null.
94
100
  */
95
- async createSession(input: { userId: string; userAgent?: string; ip?: string; provider?: string }): Promise<SessionRecord> {
101
+ async createSession(input: {
102
+ userId: string;
103
+ userAgent?: string;
104
+ ip?: string;
105
+ provider?: string;
106
+ }): Promise<SessionRecord> {
96
107
  const userIdBig = toId(input.userId);
97
108
  const row = await this.prisma.session.create({
98
109
  data: {
@@ -116,9 +127,17 @@ export class SessionRepository implements SessionStoreDeps {
116
127
  });
117
128
  }
118
129
 
119
- async revokeAllByUserExcept(userId: string, keepSessionId: string, revoker?: Revoker): Promise<void> {
130
+ async revokeAllByUserExcept(
131
+ userId: string,
132
+ keepSessionId: string,
133
+ revoker?: Revoker,
134
+ ): Promise<void> {
120
135
  await this.prisma.session.updateMany({
121
- where: { userId: toId(userId), id: { not: toId(keepSessionId) }, revokedAt: null },
136
+ where: {
137
+ userId: toId(userId),
138
+ id: { not: toId(keepSessionId) },
139
+ revokedAt: null,
140
+ },
122
141
  data: { ...revocationFields(revoker), sessionVersion: { increment: 1 } },
123
142
  });
124
143
  }
@@ -132,7 +151,9 @@ export class SessionRepository implements SessionStoreDeps {
132
151
  }
133
152
 
134
153
  async isDenylisted(jti: string): Promise<boolean> {
135
- const row = await this.prisma.denylistedAccessToken.findUnique({ where: { jti } });
154
+ const row = await this.prisma.denylistedAccessToken.findUnique({
155
+ where: { jti },
156
+ });
136
157
  if (!row) return false;
137
158
  if (row.expiresAt.getTime() < Date.now()) return false; // expired; a cleanup job reaps these, see Phase 6
138
159
  return true;
@@ -1,5 +1,5 @@
1
1
  import { verifyBackupCode } from "@/lib/auth/core/two-factor.js";
2
- import { PrismaClient } from "../generated/prisma/client.js";
2
+ import { PrismaClient } from "../../generated/prisma/client.js";
3
3
 
4
4
  // Plain class, no DI container — constructed directly with a PrismaClient in
5
5
  // create-auth-app.ts. Identical Prisma queries to the reference combo.
@@ -12,15 +12,22 @@ export class TwoFactorRepository {
12
12
  /** Replaces any prior set — re-confirming enrollment invalidates old backup codes. */
13
13
  async saveBackupCodes(userId: bigint, hashes: string[]): Promise<void> {
14
14
  await this.prisma.twoFactorBackupCode.deleteMany({ where: { userId } });
15
- await this.prisma.twoFactorBackupCode.createMany({ data: hashes.map((codeHash) => ({ userId, codeHash })) });
15
+ await this.prisma.twoFactorBackupCode.createMany({
16
+ data: hashes.map((codeHash) => ({ userId, codeHash })),
17
+ });
16
18
  }
17
19
 
18
20
  async consumeBackupCode(userId: bigint, code: string): Promise<boolean> {
19
- const unused = await this.prisma.twoFactorBackupCode.findMany({ where: { userId, usedAt: null } });
21
+ const unused = await this.prisma.twoFactorBackupCode.findMany({
22
+ where: { userId, usedAt: null },
23
+ });
20
24
  const match = unused.find((row) => verifyBackupCode(code, row.codeHash));
21
25
  if (!match) return false;
22
26
 
23
- await this.prisma.twoFactorBackupCode.update({ where: { id: match.id }, data: { usedAt: new Date() } });
27
+ await this.prisma.twoFactorBackupCode.update({
28
+ where: { id: match.id },
29
+ data: { usedAt: new Date() },
30
+ });
24
31
  return true;
25
32
  }
26
33
 
@@ -20,7 +20,7 @@
20
20
  // `declare module "express"`.
21
21
  import type { AccessTokenClaims } from "@/lib/auth/core/types.js";
22
22
  import type { AppAbility } from "./ability.js";
23
- import type { AuthzContext } from "./authz.middleware.js";
23
+ import type { AuthzContext } from "./middleware/authz.middleware.js";
24
24
 
25
25
  declare global {
26
26
  // Express's own declaration-merging style for augmenting its Request type requires a