@simple-auth-kit/cli 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/lib/copy.ts +1 -1
  2. package/package.json +1 -1
  3. package/registry/combos/express-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
  4. package/registry/combos/express-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
  5. package/registry/combos/express-drizzle/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
  6. package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/config}/auth.config.ts +1 -1
  7. package/registry/combos/express-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
  8. package/registry/combos/express-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  9. package/registry/combos/express-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
  10. package/registry/combos/{nestjs-drizzle/shared/src → express-drizzle/shared/src/helpers}/pagination.ts +17 -3
  11. package/registry/combos/express-drizzle/shared/src/middleware/auth-core-error.middleware.ts +6 -8
  12. package/registry/combos/express-drizzle/shared/src/middleware/auth.middleware.ts +6 -8
  13. package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/openapi}/openapi-fragment.ts +13 -4
  14. package/registry/combos/express-drizzle/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +251 -52
  15. package/registry/combos/express-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
  16. package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +2 -4
  17. package/registry/combos/express-drizzle/shared/src/repositories/session.repository.ts +3 -3
  18. package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
  19. package/registry/combos/express-drizzle/shared/src/route-tiers.ts +52 -11
  20. package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +2 -2
  21. package/registry/combos/express-drizzle/test/bootstrap.ts +4 -2
  22. package/registry/combos/express-drizzle/test/prove-cycle.ts +1095 -308
  23. package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +5 -5
  24. package/registry/combos/express-drizzle/variants/base/src/index.ts +8 -0
  25. package/registry/combos/express-drizzle/variants/base/src/middleware/authz.middleware.ts +2 -2
  26. package/registry/combos/express-drizzle/variants/base/src/openapi/openapi-admin.ts +1082 -0
  27. package/registry/combos/express-drizzle/variants/base/src/rbac.defaults.ts +60 -17
  28. package/registry/combos/express-drizzle/variants/base/src/repositories/audit-log.repository.ts +2 -2
  29. package/registry/combos/express-drizzle/variants/base/src/repositories/rbac.repository.ts +5 -5
  30. package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +18 -22
  31. package/registry/combos/express-drizzle/variants/base/src/seed.ts +76 -17
  32. package/registry/combos/express-drizzle/variants/base/src/services/auth.service.ts +7 -9
  33. package/registry/combos/express-drizzle/variants/base/test/variant-hooks.ts +167 -45
  34. package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +5 -5
  35. package/registry/combos/express-drizzle/variants/workspaces/src/index.ts +8 -0
  36. package/registry/combos/express-drizzle/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
  37. package/registry/combos/express-drizzle/variants/workspaces/src/openapi/openapi-admin.ts +1176 -0
  38. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +1 -1
  39. package/registry/combos/express-drizzle/variants/workspaces/src/rbac.defaults.ts +66 -19
  40. package/registry/combos/express-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
  41. package/registry/combos/express-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +5 -5
  42. package/registry/combos/express-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +9 -11
  43. package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +18 -22
  44. package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +1 -1
  45. package/registry/combos/express-drizzle/variants/workspaces/src/seed.ts +168 -35
  46. package/registry/combos/express-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
  47. package/registry/combos/express-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
  48. package/registry/combos/express-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
  49. package/registry/combos/express-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +25 -9
  50. package/registry/combos/express-prisma/shared/src/{rate-limit.store.ts → cache/rate-limit.store.ts} +4 -1
  51. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/config}/auth.config.ts +1 -1
  52. package/registry/combos/express-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  53. package/registry/combos/{nestjs-prisma/shared/src → express-prisma/shared/src/helpers}/id.helper.ts +6 -2
  54. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/helpers}/pagination.ts +17 -3
  55. package/registry/combos/express-prisma/shared/src/middleware/auth-core-error.middleware.ts +6 -8
  56. package/registry/combos/express-prisma/shared/src/middleware/auth.middleware.ts +6 -8
  57. package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/openapi}/openapi-fragment.ts +13 -4
  58. package/registry/combos/express-prisma/shared/src/{openapi-spec.ts → openapi/openapi-spec.ts} +313 -55
  59. package/registry/combos/express-prisma/shared/src/repositories/oauth.repository.ts +2 -2
  60. package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
  61. package/registry/combos/express-prisma/shared/src/repositories/session.repository.ts +2 -2
  62. package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
  63. package/registry/combos/express-prisma/shared/src/route-tiers.ts +52 -11
  64. package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +2 -2
  65. package/registry/combos/express-prisma/test/bootstrap.ts +7 -3
  66. package/registry/combos/express-prisma/test/prove-cycle.ts +1095 -308
  67. package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +5 -5
  68. package/registry/combos/express-prisma/variants/base/src/index.ts +8 -0
  69. package/registry/combos/express-prisma/variants/base/src/middleware/authz.middleware.ts +2 -2
  70. package/registry/combos/express-prisma/variants/base/src/openapi/openapi-admin.ts +1102 -0
  71. package/registry/combos/express-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
  72. package/registry/combos/express-prisma/variants/base/src/repositories/rbac.repository.ts +4 -4
  73. package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +18 -22
  74. package/registry/combos/express-prisma/variants/base/src/services/auth.service.ts +6 -8
  75. package/registry/combos/express-prisma/variants/base/test/variant-hooks.ts +176 -47
  76. package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +5 -5
  77. package/registry/combos/express-prisma/variants/workspaces/src/index.ts +8 -0
  78. package/registry/combos/express-prisma/variants/workspaces/src/middleware/authz.middleware.ts +3 -3
  79. package/registry/combos/express-prisma/variants/workspaces/src/openapi/openapi-admin.ts +1218 -0
  80. package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +1 -1
  81. package/registry/combos/express-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
  82. package/registry/combos/express-prisma/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
  83. package/registry/combos/express-prisma/variants/workspaces/src/repositories/workspace.repository.ts +2 -2
  84. package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +18 -22
  85. package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +1 -1
  86. package/registry/combos/express-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
  87. package/registry/combos/express-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
  88. package/registry/combos/nestjs-drizzle/shared/src/{guards → ability}/ability.guard.ts +1 -1
  89. package/registry/combos/nestjs-drizzle/shared/src/{ability.ts → ability/ability.ts} +8 -2
  90. package/registry/combos/nestjs-drizzle/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
  91. package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/cache}/rate-limit.store.ts +4 -1
  92. package/registry/combos/nestjs-drizzle/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
  93. package/registry/combos/nestjs-drizzle/shared/src/{db.ts → config/db.ts} +1 -1
  94. package/registry/combos/nestjs-drizzle/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  95. package/registry/combos/nestjs-drizzle/shared/src/controllers/auth.controller.ts +1 -1
  96. package/registry/combos/nestjs-drizzle/shared/src/guards/auth.guard.ts +1 -1
  97. package/registry/combos/nestjs-drizzle/shared/src/{id.helper.ts → helpers/id.helper.ts} +6 -2
  98. package/registry/combos/{express-prisma/shared/src → nestjs-drizzle/shared/src/helpers}/pagination.ts +17 -3
  99. package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +2 -2
  100. package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +8 -12
  101. package/registry/combos/nestjs-drizzle/shared/src/repositories/session.repository.ts +3 -3
  102. package/registry/combos/nestjs-drizzle/shared/src/repositories/two-factor.repository.ts +1 -1
  103. package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
  104. package/registry/combos/nestjs-drizzle/test/bootstrap.ts +1 -1
  105. package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1 -1
  106. package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +10 -6
  107. package/registry/combos/nestjs-drizzle/variants/base/src/controllers/admin.controller.ts +1 -1
  108. package/registry/combos/nestjs-drizzle/variants/base/src/guards/authz.guard.ts +2 -2
  109. package/registry/combos/nestjs-drizzle/variants/base/src/index.ts +9 -0
  110. package/registry/combos/nestjs-drizzle/variants/base/src/rbac.defaults.ts +58 -16
  111. package/registry/combos/nestjs-drizzle/variants/base/src/repositories/audit-log.repository.ts +3 -3
  112. package/registry/combos/nestjs-drizzle/variants/base/src/repositories/rbac.repository.ts +4 -4
  113. package/registry/combos/nestjs-drizzle/variants/base/src/seed.ts +76 -17
  114. package/registry/combos/nestjs-drizzle/variants/base/src/services/auth.service.ts +7 -9
  115. package/registry/combos/nestjs-drizzle/variants/base/test/variant-hooks.ts +167 -45
  116. package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +10 -6
  117. package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/admin.controller.ts +1 -1
  118. package/registry/combos/nestjs-drizzle/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
  119. package/registry/combos/nestjs-drizzle/variants/workspaces/src/guards/authz.guard.ts +2 -2
  120. package/registry/combos/nestjs-drizzle/variants/workspaces/src/index.ts +9 -0
  121. package/registry/combos/nestjs-drizzle/variants/workspaces/src/rbac.defaults.ts +64 -18
  122. package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/audit-log.repository.ts +3 -3
  123. package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/rbac.repository.ts +4 -4
  124. package/registry/combos/nestjs-drizzle/variants/workspaces/src/repositories/workspace.repository.ts +8 -10
  125. package/registry/combos/nestjs-drizzle/variants/workspaces/src/seed.ts +171 -36
  126. package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +7 -9
  127. package/registry/combos/nestjs-drizzle/variants/workspaces/test/variant-hooks.ts +539 -133
  128. package/registry/combos/nestjs-prisma/shared/src/{guards → ability}/ability.guard.ts +1 -1
  129. package/registry/combos/nestjs-prisma/shared/src/{ability.ts → ability/ability.ts} +8 -2
  130. package/registry/combos/nestjs-prisma/shared/src/{permission-cache.ts → cache/permission-cache.ts} +27 -10
  131. package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/cache}/rate-limit.store.ts +4 -1
  132. package/registry/combos/nestjs-prisma/shared/src/{auth.config.ts → config/auth.config.ts} +1 -1
  133. package/registry/combos/nestjs-prisma/shared/src/{key-provider.ts → config/key-provider.ts} +6 -2
  134. package/registry/combos/nestjs-prisma/shared/src/controllers/auth.controller.ts +1 -1
  135. package/registry/combos/nestjs-prisma/shared/src/guards/auth.guard.ts +1 -1
  136. package/registry/combos/{express-prisma/shared/src → nestjs-prisma/shared/src/helpers}/id.helper.ts +6 -2
  137. package/registry/combos/nestjs-prisma/shared/src/{pagination.ts → helpers/pagination.ts} +17 -3
  138. package/registry/combos/nestjs-prisma/shared/src/repositories/oauth.repository.ts +1 -1
  139. package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +2 -4
  140. package/registry/combos/nestjs-prisma/shared/src/repositories/session.repository.ts +2 -2
  141. package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
  142. package/registry/combos/nestjs-prisma/test/bootstrap.ts +1 -1
  143. package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1 -1
  144. package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +9 -5
  145. package/registry/combos/nestjs-prisma/variants/base/src/controllers/admin.controller.ts +1 -1
  146. package/registry/combos/nestjs-prisma/variants/base/src/gateways/audit-log.gateway.ts +1 -1
  147. package/registry/combos/nestjs-prisma/variants/base/src/guards/authz.guard.ts +2 -2
  148. package/registry/combos/nestjs-prisma/variants/base/src/index.ts +9 -0
  149. package/registry/combos/nestjs-prisma/variants/base/src/{docs.ts → openapi/docs.ts} +23 -6
  150. package/registry/combos/nestjs-prisma/variants/base/src/repositories/audit-log.repository.ts +2 -2
  151. package/registry/combos/nestjs-prisma/variants/base/src/repositories/country.repository.ts +2 -2
  152. package/registry/combos/nestjs-prisma/variants/base/src/repositories/customer.repository.ts +6 -5
  153. package/registry/combos/nestjs-prisma/variants/base/src/repositories/language.repository.ts +2 -2
  154. package/registry/combos/nestjs-prisma/variants/base/src/repositories/rbac.repository.ts +3 -3
  155. package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +6 -8
  156. package/registry/combos/nestjs-prisma/variants/base/test/variant-hooks.ts +176 -47
  157. package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +9 -5
  158. package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/admin.controller.ts +1 -1
  159. package/registry/combos/nestjs-prisma/variants/workspaces/src/controllers/workspace.controller.ts +1 -1
  160. package/registry/combos/nestjs-prisma/variants/workspaces/src/guards/authz.guard.ts +2 -2
  161. package/registry/combos/nestjs-prisma/variants/workspaces/src/index.ts +9 -0
  162. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/audit-log.repository.ts +2 -2
  163. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/country.repository.ts +2 -2
  164. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/customer.repository.ts +6 -5
  165. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +2 -2
  166. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/rbac.repository.ts +3 -3
  167. package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/workspace.repository.ts +1 -1
  168. package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +6 -8
  169. package/registry/combos/nestjs-prisma/variants/workspaces/test/variant-hooks.ts +538 -134
  170. package/simple-auth-kit.ts +13 -9
  171. package/registry/combos/express-drizzle/variants/base/src/openapi-admin.ts +0 -565
  172. package/registry/combos/express-drizzle/variants/workspaces/src/openapi-admin.ts +0 -596
  173. package/registry/combos/express-prisma/variants/base/src/openapi-admin.ts +0 -569
  174. package/registry/combos/express-prisma/variants/workspaces/src/openapi-admin.ts +0 -601
  175. /package/registry/combos/express-drizzle/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
  176. /package/registry/combos/express-prisma/shared/src/{http-error.ts → errors/http-error.ts} +0 -0
@@ -18,7 +18,8 @@ export const openApiSpec: Record<string, unknown> = {
18
18
  openapi: "3.0.3",
19
19
  info: {
20
20
  title: "simple-auth-kit API",
21
- description: "express-prisma combo — signup/login, sessions, TOTP 2FA, OAuth, password reset, RBAC.",
21
+ description:
22
+ "express-prisma combo — signup/login, sessions, TOTP 2FA, OAuth, password reset, RBAC.",
22
23
  version: "1.0",
23
24
  },
24
25
  servers: [{ url: "/" }],
@@ -38,18 +39,37 @@ export const openApiSpec: Record<string, unknown> = {
38
39
  properties: {
39
40
  email: { type: "string", example: "alice@example.com" },
40
41
  password: { type: "string", example: "correct-horse-battery-staple" },
41
- firstName: { type: "string", description: "Unique across the deployment.", example: "Alice" },
42
- lastName: { type: "string", description: "Unique across the deployment.", example: "Nguyen" },
42
+ firstName: {
43
+ type: "string",
44
+ description: "Unique across the deployment.",
45
+ example: "Alice",
46
+ },
47
+ lastName: {
48
+ type: "string",
49
+ description: "Unique across the deployment.",
50
+ example: "Nguyen",
51
+ },
43
52
  displayName: { type: "string", example: "Alice Nguyen" },
44
- phone: { type: "string", description: "Unique across the deployment." },
45
- username: { type: "string", description: "Unique across the deployment." },
53
+ phone: {
54
+ type: "string",
55
+ description: "Unique across the deployment.",
56
+ },
57
+ username: {
58
+ type: "string",
59
+ description: "Unique across the deployment.",
60
+ },
46
61
  },
47
62
  },
48
63
  LoginRequest: {
49
64
  type: "object",
50
65
  required: ["identifier", "password"],
51
66
  properties: {
52
- identifier: { type: "string", description: "Email, username, or phone — whichever the account has set.", example: "alice@example.com" },
67
+ identifier: {
68
+ type: "string",
69
+ description:
70
+ "Email, username, or phone — whichever the account has set.",
71
+ example: "alice@example.com",
72
+ },
53
73
  password: { type: "string", example: "correct-horse-battery-staple" },
54
74
  },
55
75
  },
@@ -57,8 +77,16 @@ export const openApiSpec: Record<string, unknown> = {
57
77
  type: "object",
58
78
  required: ["challengeToken", "code"],
59
79
  properties: {
60
- challengeToken: { type: "string", description: "Returned by POST /auth/login when the account has 2FA enabled" },
61
- code: { type: "string", description: "6-digit TOTP code, or an unused backup code", example: "123456" },
80
+ challengeToken: {
81
+ type: "string",
82
+ description:
83
+ "Returned by POST /auth/login when the account has 2FA enabled",
84
+ },
85
+ code: {
86
+ type: "string",
87
+ description: "6-digit TOTP code, or an unused backup code",
88
+ example: "123456",
89
+ },
62
90
  },
63
91
  },
64
92
  RefreshRequest: {
@@ -86,7 +114,10 @@ export const openApiSpec: Record<string, unknown> = {
86
114
  type: "object",
87
115
  required: ["token", "newPassword"],
88
116
  properties: {
89
- token: { type: "string", description: "Raw token from the sendPasswordResetEmail hook" },
117
+ token: {
118
+ type: "string",
119
+ description: "Raw token from the sendPasswordResetEmail hook",
120
+ },
90
121
  newPassword: { type: "string" },
91
122
  },
92
123
  },
@@ -126,7 +157,14 @@ export const openApiSpec: Record<string, unknown> = {
126
157
  },
127
158
  CurrentUser: {
128
159
  type: "object",
129
- required: ["sub", "sessionId", "roles", "permissions", "twoFactorEnabled", "email"],
160
+ required: [
161
+ "sub",
162
+ "sessionId",
163
+ "roles",
164
+ "permissions",
165
+ "twoFactorEnabled",
166
+ "email",
167
+ ],
130
168
  properties: {
131
169
  sub: { type: "string", description: "User id" },
132
170
  sessionId: { type: "string" },
@@ -146,7 +184,12 @@ export const openApiSpec: Record<string, unknown> = {
146
184
  displayName: { type: "string", nullable: true },
147
185
  phone: { type: "string", nullable: true },
148
186
  username: { type: "string", nullable: true },
149
- photo: { type: "string", nullable: true, description: "A data URI or an externally-hosted URL — stored as-is, never processed server-side." },
187
+ photo: {
188
+ type: "string",
189
+ nullable: true,
190
+ description:
191
+ "A data URI or an externally-hosted URL — stored as-is, never processed server-side.",
192
+ },
150
193
  },
151
194
  },
152
195
  SelfProfile: {
@@ -160,7 +203,12 @@ export const openApiSpec: Record<string, unknown> = {
160
203
  displayName: { type: "string", nullable: true },
161
204
  phone: { type: "string", nullable: true },
162
205
  username: { type: "string", nullable: true },
163
- photo: { type: "string", nullable: true, description: "A data URI or an externally-hosted URL — stored as-is, never processed server-side." },
206
+ photo: {
207
+ type: "string",
208
+ nullable: true,
209
+ description:
210
+ "A data URI or an externally-hosted URL — stored as-is, never processed server-side.",
211
+ },
164
212
  createdAt: { type: "string", format: "date-time" },
165
213
  },
166
214
  },
@@ -186,21 +234,32 @@ export const openApiSpec: Record<string, unknown> = {
186
234
  required: ["secret", "provisioningUri"],
187
235
  properties: {
188
236
  secret: { type: "string", description: "Base32 TOTP secret" },
189
- provisioningUri: { type: "string", description: "otpauth:// provisioning URI, render as a QR code" },
237
+ provisioningUri: {
238
+ type: "string",
239
+ description: "otpauth:// provisioning URI, render as a QR code",
240
+ },
190
241
  },
191
242
  },
192
243
  ConfirmTwoFactorResponse: {
193
244
  type: "object",
194
245
  required: ["backupCodes"],
195
246
  properties: {
196
- backupCodes: { type: "array", items: { type: "string" }, description: "One-time recovery codes — shown once" },
247
+ backupCodes: {
248
+ type: "array",
249
+ items: { type: "string" },
250
+ description: "One-time recovery codes — shown once",
251
+ },
197
252
  },
198
253
  },
199
254
  OAuthStartResponse: {
200
255
  type: "object",
201
256
  required: ["url"],
202
257
  properties: {
203
- url: { type: "string", description: "Redirect the user/webview to this URL to start the OAuth flow" },
258
+ url: {
259
+ type: "string",
260
+ description:
261
+ "Redirect the user/webview to this URL to start the OAuth flow",
262
+ },
204
263
  },
205
264
  },
206
265
  ErrorResponse: {
@@ -223,10 +282,21 @@ export const openApiSpec: Record<string, unknown> = {
223
282
  summary: "Create a user and issue a session",
224
283
  requestBody: {
225
284
  required: true,
226
- content: { "application/json": { schema: { $ref: "#/components/schemas/SignupRequest" } } },
285
+ content: {
286
+ "application/json": {
287
+ schema: { $ref: "#/components/schemas/SignupRequest" },
288
+ },
289
+ },
227
290
  },
228
291
  responses: {
229
- "201": { description: "Created", content: { "application/json": { schema: { $ref: "#/components/schemas/AuthTokens" } } } },
292
+ "201": {
293
+ description: "Created",
294
+ content: {
295
+ "application/json": {
296
+ schema: { $ref: "#/components/schemas/AuthTokens" },
297
+ },
298
+ },
299
+ },
230
300
  "400": errorResponse("Missing required field"),
231
301
  "409": errorResponse("Email already registered"),
232
302
  },
@@ -236,17 +306,27 @@ export const openApiSpec: Record<string, unknown> = {
236
306
  post: {
237
307
  tags: ["auth"],
238
308
  summary: "Log in with email, username, or phone + password",
239
- description: "identifier is matched against email, username, and phone, in that order. Returns tokens directly, or a 2FA challenge if the account has 2FA enabled.",
309
+ description:
310
+ "identifier is matched against email, username, and phone, in that order. Returns tokens directly, or a 2FA challenge if the account has 2FA enabled.",
240
311
  requestBody: {
241
312
  required: true,
242
- content: { "application/json": { schema: { $ref: "#/components/schemas/LoginRequest" } } },
313
+ content: {
314
+ "application/json": {
315
+ schema: { $ref: "#/components/schemas/LoginRequest" },
316
+ },
317
+ },
243
318
  },
244
319
  responses: {
245
320
  "200": {
246
321
  description: "OK",
247
322
  content: {
248
323
  "application/json": {
249
- schema: { oneOf: [{ $ref: "#/components/schemas/AuthTokens" }, { $ref: "#/components/schemas/TwoFactorChallenge" }] },
324
+ schema: {
325
+ oneOf: [
326
+ { $ref: "#/components/schemas/AuthTokens" },
327
+ { $ref: "#/components/schemas/TwoFactorChallenge" },
328
+ ],
329
+ },
250
330
  },
251
331
  },
252
332
  },
@@ -262,10 +342,21 @@ export const openApiSpec: Record<string, unknown> = {
262
342
  summary: "Complete the 2FA challenge from POST /auth/login",
263
343
  requestBody: {
264
344
  required: true,
265
- content: { "application/json": { schema: { $ref: "#/components/schemas/LoginTwoFactorRequest" } } },
345
+ content: {
346
+ "application/json": {
347
+ schema: { $ref: "#/components/schemas/LoginTwoFactorRequest" },
348
+ },
349
+ },
266
350
  },
267
351
  responses: {
268
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/AuthTokens" } } } },
352
+ "200": {
353
+ description: "OK",
354
+ content: {
355
+ "application/json": {
356
+ schema: { $ref: "#/components/schemas/AuthTokens" },
357
+ },
358
+ },
359
+ },
269
360
  "400": errorResponse("Missing required field"),
270
361
  "401": errorResponse("Invalid or expired challenge token / code"),
271
362
  },
@@ -277,10 +368,21 @@ export const openApiSpec: Record<string, unknown> = {
277
368
  summary: "Rotate a refresh token for a new access+refresh pair",
278
369
  requestBody: {
279
370
  required: true,
280
- content: { "application/json": { schema: { $ref: "#/components/schemas/RefreshRequest" } } },
371
+ content: {
372
+ "application/json": {
373
+ schema: { $ref: "#/components/schemas/RefreshRequest" },
374
+ },
375
+ },
281
376
  },
282
377
  responses: {
283
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/RefreshResponse" } } } },
378
+ "200": {
379
+ description: "OK",
380
+ content: {
381
+ "application/json": {
382
+ schema: { $ref: "#/components/schemas/RefreshResponse" },
383
+ },
384
+ },
385
+ },
284
386
  "400": errorResponse("Missing required field"),
285
387
  "401": errorResponse("Stale/reused/invalid refresh token"),
286
388
  },
@@ -290,25 +392,45 @@ export const openApiSpec: Record<string, unknown> = {
290
392
  get: {
291
393
  tags: ["auth"],
292
394
  summary: "Current session's identity",
293
- description: "`roles` and `permissions` are whatever applies to this request — see authz.middleware.ts for how they are resolved.",
395
+ description:
396
+ "`roles` and `permissions` are whatever applies to this request — see authz.middleware.ts for how they are resolved.",
294
397
  security: [{ bearerAuth: [] }],
295
398
  parameters: adminSpec.scopeParameters,
296
399
  responses: {
297
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/CurrentUser" } } } },
400
+ "200": {
401
+ description: "OK",
402
+ content: {
403
+ "application/json": {
404
+ schema: { $ref: "#/components/schemas/CurrentUser" },
405
+ },
406
+ },
407
+ },
298
408
  "401": errorResponse("Missing or invalid access token"),
299
409
  },
300
410
  },
301
411
  patch: {
302
412
  tags: ["auth"],
303
413
  summary: "Update the current user's own profile",
304
- description: "Self-service — no admin permission required. Updates the caller's own row directly; unrelated to any workspace.",
414
+ description:
415
+ "Self-service — no admin permission required. Updates the caller's own row directly; unrelated to any workspace.",
305
416
  security: [{ bearerAuth: [] }],
306
417
  requestBody: {
307
418
  required: true,
308
- content: { "application/json": { schema: { $ref: "#/components/schemas/UpdateUserRequest" } } },
419
+ content: {
420
+ "application/json": {
421
+ schema: { $ref: "#/components/schemas/UpdateUserRequest" },
422
+ },
423
+ },
309
424
  },
310
425
  responses: {
311
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/SelfProfile" } } } },
426
+ "200": {
427
+ description: "OK",
428
+ content: {
429
+ "application/json": {
430
+ schema: { $ref: "#/components/schemas/SelfProfile" },
431
+ },
432
+ },
433
+ },
312
434
  "401": errorResponse("Missing or invalid access token"),
313
435
  },
314
436
  },
@@ -321,7 +443,14 @@ export const openApiSpec: Record<string, unknown> = {
321
443
  responses: {
322
444
  "200": {
323
445
  description: "OK",
324
- content: { "application/json": { schema: { type: "array", items: { $ref: "#/components/schemas/SessionSummary" } } } },
446
+ content: {
447
+ "application/json": {
448
+ schema: {
449
+ type: "array",
450
+ items: { $ref: "#/components/schemas/SessionSummary" },
451
+ },
452
+ },
453
+ },
325
454
  },
326
455
  "401": errorResponse("Missing or invalid access token"),
327
456
  },
@@ -333,7 +462,14 @@ export const openApiSpec: Record<string, unknown> = {
333
462
  summary: "Revoke the current session",
334
463
  security: [{ bearerAuth: [] }],
335
464
  responses: {
336
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
465
+ "200": {
466
+ description: "OK",
467
+ content: {
468
+ "application/json": {
469
+ schema: { $ref: "#/components/schemas/OkResponse" },
470
+ },
471
+ },
472
+ },
337
473
  "401": errorResponse("Missing or invalid access token"),
338
474
  },
339
475
  },
@@ -344,7 +480,14 @@ export const openApiSpec: Record<string, unknown> = {
344
480
  summary: "Revoke every session for the current user",
345
481
  security: [{ bearerAuth: [] }],
346
482
  responses: {
347
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
483
+ "200": {
484
+ description: "OK",
485
+ content: {
486
+ "application/json": {
487
+ schema: { $ref: "#/components/schemas/OkResponse" },
488
+ },
489
+ },
490
+ },
348
491
  "401": errorResponse("Missing or invalid access token"),
349
492
  },
350
493
  },
@@ -355,7 +498,14 @@ export const openApiSpec: Record<string, unknown> = {
355
498
  summary: "Revoke every session except the current one",
356
499
  security: [{ bearerAuth: [] }],
357
500
  responses: {
358
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
501
+ "200": {
502
+ description: "OK",
503
+ content: {
504
+ "application/json": {
505
+ schema: { $ref: "#/components/schemas/OkResponse" },
506
+ },
507
+ },
508
+ },
359
509
  "401": errorResponse("Missing or invalid access token"),
360
510
  },
361
511
  },
@@ -364,16 +514,30 @@ export const openApiSpec: Record<string, unknown> = {
364
514
  post: {
365
515
  tags: ["auth"],
366
516
  summary: "Change the current user's password",
367
- description: "Requires the current password. Revokes every other session for the user; the session making this call is left alone.",
517
+ description:
518
+ "Requires the current password. Revokes every other session for the user; the session making this call is left alone.",
368
519
  security: [{ bearerAuth: [] }],
369
520
  requestBody: {
370
521
  required: true,
371
- content: { "application/json": { schema: { $ref: "#/components/schemas/ChangePasswordRequest" } } },
522
+ content: {
523
+ "application/json": {
524
+ schema: { $ref: "#/components/schemas/ChangePasswordRequest" },
525
+ },
526
+ },
372
527
  },
373
528
  responses: {
374
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
529
+ "200": {
530
+ description: "OK",
531
+ content: {
532
+ "application/json": {
533
+ schema: { $ref: "#/components/schemas/OkResponse" },
534
+ },
535
+ },
536
+ },
375
537
  "400": errorResponse("Missing required field"),
376
- "401": errorResponse("Missing/invalid access token, or wrong current password"),
538
+ "401": errorResponse(
539
+ "Missing/invalid access token, or wrong current password",
540
+ ),
377
541
  },
378
542
  },
379
543
  },
@@ -384,7 +548,16 @@ export const openApiSpec: Record<string, unknown> = {
384
548
  description: "Not active until confirmed via POST /auth/2fa/confirm.",
385
549
  security: [{ bearerAuth: [] }],
386
550
  responses: {
387
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/EnrollTwoFactorResponse" } } } },
551
+ "200": {
552
+ description: "OK",
553
+ content: {
554
+ "application/json": {
555
+ schema: {
556
+ $ref: "#/components/schemas/EnrollTwoFactorResponse",
557
+ },
558
+ },
559
+ },
560
+ },
388
561
  "401": errorResponse("Missing or invalid access token"),
389
562
  },
390
563
  },
@@ -393,14 +566,28 @@ export const openApiSpec: Record<string, unknown> = {
393
566
  post: {
394
567
  tags: ["auth"],
395
568
  summary: "Confirm TOTP enrollment with a code",
396
- description: "Enables 2FA and returns one-time backup codes, shown only here.",
569
+ description:
570
+ "Enables 2FA and returns one-time backup codes, shown only here.",
397
571
  security: [{ bearerAuth: [] }],
398
572
  requestBody: {
399
573
  required: true,
400
- content: { "application/json": { schema: { $ref: "#/components/schemas/TwoFactorCodeRequest" } } },
574
+ content: {
575
+ "application/json": {
576
+ schema: { $ref: "#/components/schemas/TwoFactorCodeRequest" },
577
+ },
578
+ },
401
579
  },
402
580
  responses: {
403
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/ConfirmTwoFactorResponse" } } } },
581
+ "200": {
582
+ description: "OK",
583
+ content: {
584
+ "application/json": {
585
+ schema: {
586
+ $ref: "#/components/schemas/ConfirmTwoFactorResponse",
587
+ },
588
+ },
589
+ },
590
+ },
404
591
  "400": errorResponse("call enrollTwoFactor first"),
405
592
  "401": errorResponse("Invalid two-factor code"),
406
593
  },
@@ -414,10 +601,21 @@ export const openApiSpec: Record<string, unknown> = {
414
601
  security: [{ bearerAuth: [] }],
415
602
  requestBody: {
416
603
  required: true,
417
- content: { "application/json": { schema: { $ref: "#/components/schemas/TwoFactorCodeRequest" } } },
604
+ content: {
605
+ "application/json": {
606
+ schema: { $ref: "#/components/schemas/TwoFactorCodeRequest" },
607
+ },
608
+ },
418
609
  },
419
610
  responses: {
420
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
611
+ "200": {
612
+ description: "OK",
613
+ content: {
614
+ "application/json": {
615
+ schema: { $ref: "#/components/schemas/OkResponse" },
616
+ },
617
+ },
618
+ },
421
619
  "400": errorResponse("Two-factor is not enabled"),
422
620
  "401": errorResponse("Invalid two-factor code"),
423
621
  },
@@ -427,13 +625,25 @@ export const openApiSpec: Record<string, unknown> = {
427
625
  post: {
428
626
  tags: ["auth"],
429
627
  summary: "Request a password reset",
430
- description: "Always reports success, whether or not the email exists — avoids account enumeration.",
628
+ description:
629
+ "Always reports success, whether or not the email exists — avoids account enumeration.",
431
630
  requestBody: {
432
631
  required: true,
433
- content: { "application/json": { schema: { $ref: "#/components/schemas/ForgotPasswordRequest" } } },
632
+ content: {
633
+ "application/json": {
634
+ schema: { $ref: "#/components/schemas/ForgotPasswordRequest" },
635
+ },
636
+ },
434
637
  },
435
638
  responses: {
436
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
639
+ "200": {
640
+ description: "OK",
641
+ content: {
642
+ "application/json": {
643
+ schema: { $ref: "#/components/schemas/OkResponse" },
644
+ },
645
+ },
646
+ },
437
647
  "400": errorResponse("Missing required field"),
438
648
  },
439
649
  },
@@ -445,10 +655,21 @@ export const openApiSpec: Record<string, unknown> = {
445
655
  description: "Revokes every existing session for the user on success.",
446
656
  requestBody: {
447
657
  required: true,
448
- content: { "application/json": { schema: { $ref: "#/components/schemas/ResetPasswordRequest" } } },
658
+ content: {
659
+ "application/json": {
660
+ schema: { $ref: "#/components/schemas/ResetPasswordRequest" },
661
+ },
662
+ },
449
663
  },
450
664
  responses: {
451
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OkResponse" } } } },
665
+ "200": {
666
+ description: "OK",
667
+ content: {
668
+ "application/json": {
669
+ schema: { $ref: "#/components/schemas/OkResponse" },
670
+ },
671
+ },
672
+ },
452
673
  "400": errorResponse("Missing required field"),
453
674
  "401": errorResponse("Invalid or expired reset token"),
454
675
  },
@@ -458,9 +679,23 @@ export const openApiSpec: Record<string, unknown> = {
458
679
  get: {
459
680
  tags: ["auth"],
460
681
  summary: "Get the redirect URL to start an OAuth login",
461
- parameters: [{ name: "provider", in: "path", required: true, schema: { type: "string", enum: ["google", "apple"] } }],
682
+ parameters: [
683
+ {
684
+ name: "provider",
685
+ in: "path",
686
+ required: true,
687
+ schema: { type: "string", enum: ["google", "apple"] },
688
+ },
689
+ ],
462
690
  responses: {
463
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/OAuthStartResponse" } } } },
691
+ "200": {
692
+ description: "OK",
693
+ content: {
694
+ "application/json": {
695
+ schema: { $ref: "#/components/schemas/OAuthStartResponse" },
696
+ },
697
+ },
698
+ },
464
699
  "400": errorResponse("Unknown or unconfigured OAuth provider"),
465
700
  },
466
701
  },
@@ -468,14 +703,37 @@ export const openApiSpec: Record<string, unknown> = {
468
703
  "/auth/oauth/{provider}/callback": {
469
704
  get: {
470
705
  tags: ["auth"],
471
- summary: "OAuth provider callback — exchanges the code and completes login",
706
+ summary:
707
+ "OAuth provider callback — exchanges the code and completes login",
472
708
  parameters: [
473
- { name: "provider", in: "path", required: true, schema: { type: "string", enum: ["google", "apple"] } },
474
- { name: "code", in: "query", required: true, schema: { type: "string" } },
475
- { name: "state", in: "query", required: true, schema: { type: "string" } },
709
+ {
710
+ name: "provider",
711
+ in: "path",
712
+ required: true,
713
+ schema: { type: "string", enum: ["google", "apple"] },
714
+ },
715
+ {
716
+ name: "code",
717
+ in: "query",
718
+ required: true,
719
+ schema: { type: "string" },
720
+ },
721
+ {
722
+ name: "state",
723
+ in: "query",
724
+ required: true,
725
+ schema: { type: "string" },
726
+ },
476
727
  ],
477
728
  responses: {
478
- "200": { description: "OK", content: { "application/json": { schema: { $ref: "#/components/schemas/AuthTokens" } } } },
729
+ "200": {
730
+ description: "OK",
731
+ content: {
732
+ "application/json": {
733
+ schema: { $ref: "#/components/schemas/AuthTokens" },
734
+ },
735
+ },
736
+ },
479
737
  "400": errorResponse("Unknown or unconfigured OAuth provider"),
480
738
  "401": errorResponse("OAuth exchange failed"),
481
739
  },
@@ -1,7 +1,7 @@
1
1
  import type { OAuthStoreDeps } from "@/lib/auth/core/oauth.js";
2
2
  import { PrismaClient } from "../../generated/prisma/client.js";
3
- import { HttpError } from "../http-error.js";
4
- import { toId } from "../id.helper.js";
3
+ import { HttpError } from "../errors/http-error.js";
4
+ import { toId } from "../helpers/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.
@@ -1,6 +1,6 @@
1
1
  import type { PasswordResetStoreDeps } from "@/lib/auth/core/password-reset.js";
2
2
  import { PrismaClient } from "../../generated/prisma/client.js";
3
- import { toId } from "../id.helper.js";
3
+ import { toId } from "../helpers/id.helper.js";
4
4
 
5
5
  // Plain class, no DI container — constructed directly with a PrismaClient in
6
6
  // create-auth-app.ts. Identical Prisma queries to the reference combo.
@@ -21,9 +21,7 @@ export class PasswordResetRepository implements PasswordResetStoreDeps {
21
21
  });
22
22
  }
23
23
 
24
- async findValidResetToken(
25
- tokenHash: string,
26
- ): Promise<{
24
+ async findValidResetToken(tokenHash: string): Promise<{
27
25
  userId: string;
28
26
  expiresAt: string;
29
27
  consumedAt: string | null;
@@ -6,9 +6,9 @@ import type {
6
6
  SessionRecord,
7
7
  } from "@/lib/auth/core/types.js";
8
8
  import { PrismaClient } from "../../generated/prisma/client.js";
9
- import type { AuthConfig } from "../auth.config.js";
9
+ import type { AuthConfig } from "../config/auth.config.js";
10
10
  import { AuditLogRepository } from "./audit-log.repository.js";
11
- import { toId, toIdOrNull } from "../id.helper.js";
11
+ import { toId, toIdOrNull } from "../helpers/id.helper.js";
12
12
 
13
13
  function toSessionRecord(row: {
14
14
  id: bigint;
@@ -19,7 +19,7 @@
19
19
  // the `Express` namespace, see @types/express-serve-static-core) rather than the reference's
20
20
  // `declare module "express"`.
21
21
  import type { AccessTokenClaims } from "@/lib/auth/core/types.js";
22
- import type { AppAbility } from "./ability.js";
22
+ import type { AppAbility } from "./ability/ability.js";
23
23
  import type { AuthzContext } from "./middleware/authz.middleware.js";
24
24
 
25
25
  declare global {