@techelusive/nest-auth 0.0.0-stage → 0.2.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 (340) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +21 -0
  3. package/README.md +204 -2
  4. package/api/openapi.json +4399 -0
  5. package/api/techelusive-authentication.postman_collection.json +3178 -0
  6. package/dist/auth-options.d.ts +156 -0
  7. package/dist/auth-options.js +3 -0
  8. package/dist/auth-options.js.map +1 -0
  9. package/dist/auth-options.validation.d.ts +9 -0
  10. package/dist/auth-options.validation.js +74 -0
  11. package/dist/auth-options.validation.js.map +1 -0
  12. package/dist/auth.module.d.ts +38 -0
  13. package/dist/auth.module.js +173 -0
  14. package/dist/auth.module.js.map +1 -0
  15. package/dist/common/auth-error.d.ts +16 -0
  16. package/dist/common/auth-error.js +23 -0
  17. package/dist/common/auth-error.js.map +1 -0
  18. package/dist/common/auth-exception.filter.d.ts +24 -0
  19. package/dist/common/auth-exception.filter.js +66 -0
  20. package/dist/common/auth-exception.filter.js.map +1 -0
  21. package/dist/common/crypto/jwt.d.ts +89 -0
  22. package/dist/common/crypto/jwt.js +144 -0
  23. package/dist/common/crypto/jwt.js.map +1 -0
  24. package/dist/common/crypto/random-secrets.d.ts +78 -0
  25. package/dist/common/crypto/random-secrets.js +113 -0
  26. package/dist/common/crypto/random-secrets.js.map +1 -0
  27. package/dist/common/crypto/remote-public-keys.d.ts +49 -0
  28. package/dist/common/crypto/remote-public-keys.js +105 -0
  29. package/dist/common/crypto/remote-public-keys.js.map +1 -0
  30. package/dist/common/crypto/totp.d.ts +68 -0
  31. package/dist/common/crypto/totp.js +148 -0
  32. package/dist/common/crypto/totp.js.map +1 -0
  33. package/dist/common/encryption.service.d.ts +36 -0
  34. package/dist/common/encryption.service.js +111 -0
  35. package/dist/common/encryption.service.js.map +1 -0
  36. package/dist/common/identifiers.d.ts +48 -0
  37. package/dist/common/identifiers.js +89 -0
  38. package/dist/common/identifiers.js.map +1 -0
  39. package/dist/common/network-safety.d.ts +19 -0
  40. package/dist/common/network-safety.js +72 -0
  41. package/dist/common/network-safety.js.map +1 -0
  42. package/dist/common/rate-limiter.service.d.ts +63 -0
  43. package/dist/common/rate-limiter.service.js +158 -0
  44. package/dist/common/rate-limiter.service.js.map +1 -0
  45. package/dist/common/refresh-token-cookie.d.ts +78 -0
  46. package/dist/common/refresh-token-cookie.js +185 -0
  47. package/dist/common/refresh-token-cookie.js.map +1 -0
  48. package/dist/common/request-context.d.ts +14 -0
  49. package/dist/common/request-context.js +22 -0
  50. package/dist/common/request-context.js.map +1 -0
  51. package/dist/common/strict-validation.pipe.d.ts +8 -0
  52. package/dist/common/strict-validation.pipe.js +16 -0
  53. package/dist/common/strict-validation.pipe.js.map +1 -0
  54. package/dist/common/time.d.ts +15 -0
  55. package/dist/common/time.js +34 -0
  56. package/dist/common/time.js.map +1 -0
  57. package/dist/constants.d.ts +1326 -0
  58. package/dist/constants.js +1142 -0
  59. package/dist/constants.js.map +1 -0
  60. package/dist/database/create-auth-tables.migration.d.ts +26 -0
  61. package/dist/database/create-auth-tables.migration.js +291 -0
  62. package/dist/database/create-auth-tables.migration.js.map +1 -0
  63. package/dist/database/email-change-and-retention.migration.d.ts +25 -0
  64. package/dist/database/email-change-and-retention.migration.js +54 -0
  65. package/dist/database/email-change-and-retention.migration.js.map +1 -0
  66. package/dist/database/entities/api-key.entity.d.ts +17 -0
  67. package/dist/database/entities/api-key.entity.js +80 -0
  68. package/dist/database/entities/api-key.entity.js.map +1 -0
  69. package/dist/database/entities/audit-log.entity.d.ts +13 -0
  70. package/dist/database/entities/audit-log.entity.js +64 -0
  71. package/dist/database/entities/audit-log.entity.js.map +1 -0
  72. package/dist/database/entities/backup-code.entity.d.ts +9 -0
  73. package/dist/database/entities/backup-code.entity.js +52 -0
  74. package/dist/database/entities/backup-code.entity.js.map +1 -0
  75. package/dist/database/entities/index.d.ts +43 -0
  76. package/dist/database/entities/index.js +82 -0
  77. package/dist/database/entities/index.js.map +1 -0
  78. package/dist/database/entities/json-object.d.ts +12 -0
  79. package/dist/database/entities/json-object.js +3 -0
  80. package/dist/database/entities/json-object.js.map +1 -0
  81. package/dist/database/entities/link-token.entity.d.ts +15 -0
  82. package/dist/database/entities/link-token.entity.js +64 -0
  83. package/dist/database/entities/link-token.entity.js.map +1 -0
  84. package/dist/database/entities/one-time-code.entity.d.ts +16 -0
  85. package/dist/database/entities/one-time-code.entity.js +69 -0
  86. package/dist/database/entities/one-time-code.entity.js.map +1 -0
  87. package/dist/database/entities/organization-invitation.entity.d.ts +14 -0
  88. package/dist/database/entities/organization-invitation.entity.js +73 -0
  89. package/dist/database/entities/organization-invitation.entity.js.map +1 -0
  90. package/dist/database/entities/organization-member.entity.d.ts +9 -0
  91. package/dist/database/entities/organization-member.entity.js +48 -0
  92. package/dist/database/entities/organization-member.entity.js.map +1 -0
  93. package/dist/database/entities/organization.entity.d.ts +12 -0
  94. package/dist/database/entities/organization.entity.js +58 -0
  95. package/dist/database/entities/organization.entity.js.map +1 -0
  96. package/dist/database/entities/passkey.entity.d.ts +19 -0
  97. package/dist/database/entities/passkey.entity.js +78 -0
  98. package/dist/database/entities/passkey.entity.js.map +1 -0
  99. package/dist/database/entities/password.entity.d.ts +6 -0
  100. package/dist/database/entities/password.entity.js +36 -0
  101. package/dist/database/entities/password.entity.js.map +1 -0
  102. package/dist/database/entities/refresh-token.entity.d.ts +9 -0
  103. package/dist/database/entities/refresh-token.entity.js +53 -0
  104. package/dist/database/entities/refresh-token.entity.js.map +1 -0
  105. package/dist/database/entities/session.entity.d.ts +20 -0
  106. package/dist/database/entities/session.entity.js +87 -0
  107. package/dist/database/entities/session.entity.js.map +1 -0
  108. package/dist/database/entities/settings.entity.d.ts +7 -0
  109. package/dist/database/entities/settings.entity.js +41 -0
  110. package/dist/database/entities/settings.entity.js.map +1 -0
  111. package/dist/database/entities/sign-in-challenge.entity.d.ts +23 -0
  112. package/dist/database/entities/sign-in-challenge.entity.js +80 -0
  113. package/dist/database/entities/sign-in-challenge.entity.js.map +1 -0
  114. package/dist/database/entities/signing-key.entity.d.ts +11 -0
  115. package/dist/database/entities/signing-key.entity.js +54 -0
  116. package/dist/database/entities/signing-key.entity.js.map +1 -0
  117. package/dist/database/entities/social-account.entity.d.ts +13 -0
  118. package/dist/database/entities/social-account.entity.js +54 -0
  119. package/dist/database/entities/social-account.entity.js.map +1 -0
  120. package/dist/database/entities/two-factor-secret.entity.d.ts +10 -0
  121. package/dist/database/entities/two-factor-secret.entity.js +48 -0
  122. package/dist/database/entities/two-factor-secret.entity.js.map +1 -0
  123. package/dist/database/entities/user.entity.d.ts +34 -0
  124. package/dist/database/entities/user.entity.js +129 -0
  125. package/dist/database/entities/user.entity.js.map +1 -0
  126. package/dist/database/entities/webhook-delivery.entity.d.ts +17 -0
  127. package/dist/database/entities/webhook-delivery.entity.js +79 -0
  128. package/dist/database/entities/webhook-delivery.entity.js.map +1 -0
  129. package/dist/database/entities/webhook.entity.d.ts +12 -0
  130. package/dist/database/entities/webhook.entity.js +58 -0
  131. package/dist/database/entities/webhook.entity.js.map +1 -0
  132. package/dist/database/typeorm-row.d.ts +21 -0
  133. package/dist/database/typeorm-row.js +29 -0
  134. package/dist/database/typeorm-row.js.map +1 -0
  135. package/dist/features/account/account.controller.d.ts +259 -0
  136. package/dist/features/account/account.controller.js +372 -0
  137. package/dist/features/account/account.controller.js.map +1 -0
  138. package/dist/features/account/account.dto.d.ts +16 -0
  139. package/dist/features/account/account.dto.js +61 -0
  140. package/dist/features/account/account.dto.js.map +1 -0
  141. package/dist/features/account/data-export.service.d.ts +85 -0
  142. package/dist/features/account/data-export.service.js +125 -0
  143. package/dist/features/account/data-export.service.js.map +1 -0
  144. package/dist/features/account/health.service.d.ts +30 -0
  145. package/dist/features/account/health.service.js +59 -0
  146. package/dist/features/account/health.service.js.map +1 -0
  147. package/dist/features/admin/admin.controller.d.ts +382 -0
  148. package/dist/features/admin/admin.controller.js +623 -0
  149. package/dist/features/admin/admin.controller.js.map +1 -0
  150. package/dist/features/admin/admin.dto.d.ts +61 -0
  151. package/dist/features/admin/admin.dto.js +253 -0
  152. package/dist/features/admin/admin.dto.js.map +1 -0
  153. package/dist/features/admin/admin.service.d.ts +142 -0
  154. package/dist/features/admin/admin.service.js +227 -0
  155. package/dist/features/admin/admin.service.js.map +1 -0
  156. package/dist/features/api-keys/api-keys.service.d.ts +67 -0
  157. package/dist/features/api-keys/api-keys.service.js +144 -0
  158. package/dist/features/api-keys/api-keys.service.js.map +1 -0
  159. package/dist/features/email-change/email-change.controller.d.ts +38 -0
  160. package/dist/features/email-change/email-change.controller.js +85 -0
  161. package/dist/features/email-change/email-change.controller.js.map +1 -0
  162. package/dist/features/email-change/email-change.dto.d.ts +10 -0
  163. package/dist/features/email-change/email-change.dto.js +43 -0
  164. package/dist/features/email-change/email-change.dto.js.map +1 -0
  165. package/dist/features/email-change/email-change.service.d.ts +74 -0
  166. package/dist/features/email-change/email-change.service.js +179 -0
  167. package/dist/features/email-change/email-change.service.js.map +1 -0
  168. package/dist/features/email-links/email-verification.service.d.ts +46 -0
  169. package/dist/features/email-links/email-verification.service.js +94 -0
  170. package/dist/features/email-links/email-verification.service.js.map +1 -0
  171. package/dist/features/email-links/link-tokens.service.d.ts +44 -0
  172. package/dist/features/email-links/link-tokens.service.js +122 -0
  173. package/dist/features/email-links/link-tokens.service.js.map +1 -0
  174. package/dist/features/events/auth-events.service.d.ts +52 -0
  175. package/dist/features/events/auth-events.service.js +117 -0
  176. package/dist/features/events/auth-events.service.js.map +1 -0
  177. package/dist/features/maintenance/data-retention.service.d.ts +60 -0
  178. package/dist/features/maintenance/data-retention.service.js +182 -0
  179. package/dist/features/maintenance/data-retention.service.js.map +1 -0
  180. package/dist/features/notifications/notifications.service.d.ts +40 -0
  181. package/dist/features/notifications/notifications.service.js +94 -0
  182. package/dist/features/notifications/notifications.service.js.map +1 -0
  183. package/dist/features/one-time-codes/one-time-codes.service.d.ts +40 -0
  184. package/dist/features/one-time-codes/one-time-codes.service.js +102 -0
  185. package/dist/features/one-time-codes/one-time-codes.service.js.map +1 -0
  186. package/dist/features/organizations/organizations.controller.d.ts +220 -0
  187. package/dist/features/organizations/organizations.controller.js +337 -0
  188. package/dist/features/organizations/organizations.controller.js.map +1 -0
  189. package/dist/features/organizations/organizations.dto.d.ts +31 -0
  190. package/dist/features/organizations/organizations.dto.js +100 -0
  191. package/dist/features/organizations/organizations.dto.js.map +1 -0
  192. package/dist/features/organizations/organizations.service.d.ts +274 -0
  193. package/dist/features/organizations/organizations.service.js +481 -0
  194. package/dist/features/organizations/organizations.service.js.map +1 -0
  195. package/dist/features/passkeys/passkeys.controller.d.ts +92 -0
  196. package/dist/features/passkeys/passkeys.controller.js +183 -0
  197. package/dist/features/passkeys/passkeys.controller.js.map +1 -0
  198. package/dist/features/passkeys/passkeys.dto.d.ts +13 -0
  199. package/dist/features/passkeys/passkeys.dto.js +50 -0
  200. package/dist/features/passkeys/passkeys.dto.js.map +1 -0
  201. package/dist/features/passkeys/passkeys.service.d.ts +159 -0
  202. package/dist/features/passkeys/passkeys.service.js +350 -0
  203. package/dist/features/passkeys/passkeys.service.js.map +1 -0
  204. package/dist/features/passwordless/passwordless-auth.service.d.ts +152 -0
  205. package/dist/features/passwordless/passwordless-auth.service.js +304 -0
  206. package/dist/features/passwordless/passwordless-auth.service.js.map +1 -0
  207. package/dist/features/passwordless/passwordless.controller.d.ts +75 -0
  208. package/dist/features/passwordless/passwordless.controller.js +163 -0
  209. package/dist/features/passwordless/passwordless.controller.js.map +1 -0
  210. package/dist/features/passwordless/passwordless.dto.d.ts +29 -0
  211. package/dist/features/passwordless/passwordless.dto.js +101 -0
  212. package/dist/features/passwordless/passwordless.dto.js.map +1 -0
  213. package/dist/features/passwords/password-auth.service.d.ts +154 -0
  214. package/dist/features/passwords/password-auth.service.js +317 -0
  215. package/dist/features/passwords/password-auth.service.js.map +1 -0
  216. package/dist/features/passwords/password-policy.service.d.ts +63 -0
  217. package/dist/features/passwords/password-policy.service.js +175 -0
  218. package/dist/features/passwords/password-policy.service.js.map +1 -0
  219. package/dist/features/passwords/password.controller.d.ts +95 -0
  220. package/dist/features/passwords/password.controller.js +193 -0
  221. package/dist/features/passwords/password.controller.js.map +1 -0
  222. package/dist/features/passwords/password.dto.d.ts +36 -0
  223. package/dist/features/passwords/password.dto.js +140 -0
  224. package/dist/features/passwords/password.dto.js.map +1 -0
  225. package/dist/features/sessions/session.service.d.ts +132 -0
  226. package/dist/features/sessions/session.service.js +308 -0
  227. package/dist/features/sessions/session.service.js.map +1 -0
  228. package/dist/features/sessions/session.types.d.ts +34 -0
  229. package/dist/features/sessions/session.types.js +3 -0
  230. package/dist/features/sessions/session.types.js.map +1 -0
  231. package/dist/features/settings/settings.defaults.d.ts +2 -0
  232. package/dist/features/settings/settings.defaults.js +7 -0
  233. package/dist/features/settings/settings.defaults.js.map +1 -0
  234. package/dist/features/settings/settings.service.d.ts +44 -0
  235. package/dist/features/settings/settings.service.js +112 -0
  236. package/dist/features/settings/settings.service.js.map +1 -0
  237. package/dist/features/settings/settings.types.d.ts +133 -0
  238. package/dist/features/settings/settings.types.js +3 -0
  239. package/dist/features/settings/settings.types.js.map +1 -0
  240. package/dist/features/settings/settings.validation.d.ts +33 -0
  241. package/dist/features/settings/settings.validation.js +614 -0
  242. package/dist/features/settings/settings.validation.js.map +1 -0
  243. package/dist/features/sign-in/sign-in-challenges.service.d.ts +54 -0
  244. package/dist/features/sign-in/sign-in-challenges.service.js +104 -0
  245. package/dist/features/sign-in/sign-in-challenges.service.js.map +1 -0
  246. package/dist/features/sign-in/sign-in.service.d.ts +127 -0
  247. package/dist/features/sign-in/sign-in.service.js +248 -0
  248. package/dist/features/sign-in/sign-in.service.js.map +1 -0
  249. package/dist/features/sign-in/sign-in.types.d.ts +22 -0
  250. package/dist/features/sign-in/sign-in.types.js +3 -0
  251. package/dist/features/sign-in/sign-in.types.js.map +1 -0
  252. package/dist/features/signing-keys/jwks.controller.d.ts +17 -0
  253. package/dist/features/signing-keys/jwks.controller.js +51 -0
  254. package/dist/features/signing-keys/jwks.controller.js.map +1 -0
  255. package/dist/features/signing-keys/signing-keys.service.d.ts +67 -0
  256. package/dist/features/signing-keys/signing-keys.service.js +142 -0
  257. package/dist/features/signing-keys/signing-keys.service.js.map +1 -0
  258. package/dist/features/social/social-auth.service.d.ts +100 -0
  259. package/dist/features/social/social-auth.service.js +232 -0
  260. package/dist/features/social/social-auth.service.js.map +1 -0
  261. package/dist/features/social/social-providers.service.d.ts +97 -0
  262. package/dist/features/social/social-providers.service.js +214 -0
  263. package/dist/features/social/social-providers.service.js.map +1 -0
  264. package/dist/features/social/social.controller.d.ts +54 -0
  265. package/dist/features/social/social.controller.js +124 -0
  266. package/dist/features/social/social.controller.js.map +1 -0
  267. package/dist/features/social/social.dto.d.ts +11 -0
  268. package/dist/features/social/social.dto.js +38 -0
  269. package/dist/features/social/social.dto.js.map +1 -0
  270. package/dist/features/two-factor/two-factor.controller.d.ts +84 -0
  271. package/dist/features/two-factor/two-factor.controller.js +174 -0
  272. package/dist/features/two-factor/two-factor.controller.js.map +1 -0
  273. package/dist/features/two-factor/two-factor.dto.d.ts +10 -0
  274. package/dist/features/two-factor/two-factor.dto.js +37 -0
  275. package/dist/features/two-factor/two-factor.dto.js.map +1 -0
  276. package/dist/features/two-factor/two-factor.service.d.ts +171 -0
  277. package/dist/features/two-factor/two-factor.service.js +348 -0
  278. package/dist/features/two-factor/two-factor.service.js.map +1 -0
  279. package/dist/features/users/sign-in-methods.service.d.ts +40 -0
  280. package/dist/features/users/sign-in-methods.service.js +81 -0
  281. package/dist/features/users/sign-in-methods.service.js.map +1 -0
  282. package/dist/features/users/sign-up-rules.d.ts +12 -0
  283. package/dist/features/users/sign-up-rules.js +29 -0
  284. package/dist/features/users/sign-up-rules.js.map +1 -0
  285. package/dist/features/users/users.service.d.ts +246 -0
  286. package/dist/features/users/users.service.js +509 -0
  287. package/dist/features/users/users.service.js.map +1 -0
  288. package/dist/features/users/users.types.d.ts +24 -0
  289. package/dist/features/users/users.types.js +3 -0
  290. package/dist/features/users/users.types.js.map +1 -0
  291. package/dist/features/webhooks/webhook-delivery.worker.d.ts +55 -0
  292. package/dist/features/webhooks/webhook-delivery.worker.js +175 -0
  293. package/dist/features/webhooks/webhook-delivery.worker.js.map +1 -0
  294. package/dist/features/webhooks/webhook-signature.d.ts +28 -0
  295. package/dist/features/webhooks/webhook-signature.js +85 -0
  296. package/dist/features/webhooks/webhook-signature.js.map +1 -0
  297. package/dist/features/webhooks/webhooks.service.d.ts +144 -0
  298. package/dist/features/webhooks/webhooks.service.js +238 -0
  299. package/dist/features/webhooks/webhooks.service.js.map +1 -0
  300. package/dist/guards/auth.decorators.d.ts +65 -0
  301. package/dist/guards/auth.decorators.js +68 -0
  302. package/dist/guards/auth.decorators.js.map +1 -0
  303. package/dist/guards/auth.guard.d.ts +47 -0
  304. package/dist/guards/auth.guard.js +132 -0
  305. package/dist/guards/auth.guard.js.map +1 -0
  306. package/dist/index.d.ts +38 -0
  307. package/dist/index.js +95 -0
  308. package/dist/index.js.map +1 -0
  309. package/dist/integrations/console-email-sender.d.ts +15 -0
  310. package/dist/integrations/console-email-sender.js +26 -0
  311. package/dist/integrations/console-email-sender.js.map +1 -0
  312. package/dist/integrations/msg91-sms-sender.d.ts +28 -0
  313. package/dist/integrations/msg91-sms-sender.js +46 -0
  314. package/dist/integrations/msg91-sms-sender.js.map +1 -0
  315. package/docs/README.md +39 -0
  316. package/docs/admin-api-keys-webhooks.md +147 -0
  317. package/docs/api-reference.md +175 -0
  318. package/docs/architecture.md +119 -0
  319. package/docs/client-integration.md +192 -0
  320. package/docs/code-walkthrough.md +273 -0
  321. package/docs/configuration.md +229 -0
  322. package/docs/endpoints/README.md +151 -0
  323. package/docs/endpoints/account.md +363 -0
  324. package/docs/endpoints/admin.md +422 -0
  325. package/docs/endpoints/email-password.md +261 -0
  326. package/docs/endpoints/organizations.md +192 -0
  327. package/docs/endpoints/passkeys.md +195 -0
  328. package/docs/endpoints/passwordless.md +214 -0
  329. package/docs/endpoints/social.md +116 -0
  330. package/docs/endpoints/two-factor.md +146 -0
  331. package/docs/error-codes.md +100 -0
  332. package/docs/events.md +83 -0
  333. package/docs/getting-started.md +139 -0
  334. package/docs/manual-testing.md +215 -0
  335. package/docs/organizations.md +86 -0
  336. package/docs/production-checklist.md +60 -0
  337. package/docs/protecting-routes.md +147 -0
  338. package/docs/security.md +61 -0
  339. package/docs/sign-in-flows.md +294 -0
  340. package/package.json +92 -4
@@ -0,0 +1,422 @@
1
+ # Admin
2
+
3
+ Every route needs a signed-in user holding one of `settings.admin.adminRoles` (default `["admin"]`), and the session is checked live. Other users get `403 FORBIDDEN`. Concepts are explained in [Admin, API keys and webhooks](../admin-api-keys-webhooks.md).
4
+
5
+ ## Paged lists
6
+
7
+ List endpoints accept `?page=1&pageSize=50` (page size 1–200, default 50) and return:
8
+
9
+ ```json
10
+ { "success": true, "users": [], "total": 1234, "page": 1, "pageSize": 50 }
11
+ ```
12
+
13
+ ---
14
+
15
+ ## Users
16
+
17
+ ### GET /auth/admin/users
18
+
19
+ | Query | Type | Notes |
20
+ |---|---|---|
21
+ | `search` | string | matches email, phone, first or last name (partial, case-insensitive) |
22
+ | `status` | `PENDING_VERIFICATION` \| `ACTIVE` \| `SUSPENDED` \| `DEACTIVATED` \| `DELETED` | deleted users are hidden unless asked for |
23
+ | `role` | string | e.g. `seller` |
24
+ | `page`, `pageSize` | number | |
25
+
26
+ ```http
27
+ GET /auth/admin/users?search=asha&status=ACTIVE&page=1&pageSize=20
28
+ ```
29
+
30
+ **Response `200`**
31
+
32
+ ```json
33
+ { "success": true, "users": [{ "id": "5f0c...", "email": "asha@example.com", "status": "ACTIVE", "...": "user object" }], "total": 1, "page": 1, "pageSize": 20 }
34
+ ```
35
+
36
+ ### GET /auth/admin/users/:userId
37
+
38
+ **Response `200`**
39
+
40
+ ```json
41
+ {
42
+ "success": true,
43
+ "user": { "id": "5f0c...", "...": "user object" },
44
+ "suspensionReason": null,
45
+ "statusChangedAt": "2026-10-01T09:05:00.000Z",
46
+ "termsAcceptedAt": "2026-10-01T09:00:00.000Z",
47
+ "privacyPolicyAcceptedAt": "2026-10-01T09:00:00.000Z",
48
+ "signInMethods": { "hasPassword": true, "canUseEmailCode": false, "canUsePhoneCode": false, "socialAccounts": [], "passkeyCount": 0 },
49
+ "sessions": [
50
+ { "id": "8c1e...", "signInMethod": "password", "ipAddress": "203.0.113.10", "userAgent": "Mozilla/5.0 ...", "createdAt": "2026-10-20T06:30:00.000Z", "lastUsedAt": "2026-10-20T07:10:00.000Z" }
51
+ ]
52
+ }
53
+ ```
54
+
55
+ Errors: `404 NOT_FOUND` (also for deleted users).
56
+
57
+ ### PATCH /auth/admin/users/:userId
58
+
59
+ **Request** (all optional):
60
+
61
+ ```json
62
+ { "firstName": "Asha", "lastName": "Rao", "metadata": { "kycStatus": "approved" } }
63
+ ```
64
+
65
+ **Response `200`:** `{ "success": true, "user": { ... } }`
66
+
67
+ ### PUT /auth/admin/users/:userId/roles
68
+
69
+ Replaces the user's roles. The change appears in the user's next access token.
70
+
71
+ **Request**
72
+
73
+ ```json
74
+ { "roles": ["user", "seller"] }
75
+ ```
76
+
77
+ Each role starts with a letter and uses letters, digits, `_`, `:` or `-`, up to 50 characters, with at most 20 roles.
78
+
79
+ **Response `200`:** `{ "success": true, "user": { ... } }`
80
+
81
+ ### POST /auth/admin/users/:userId/suspend
82
+
83
+ Blocks the account and signs it out everywhere. The reason is visible to admins only.
84
+
85
+ **Request**
86
+
87
+ ```json
88
+ { "reason": "Chargeback fraud, support ticket 4411" }
89
+ ```
90
+
91
+ `reason` is required, up to 500 characters.
92
+
93
+ **Response `200`:** `{ "success": true, "user": { "status": "SUSPENDED", ... } }`
94
+
95
+ ### POST /auth/admin/users/:userId/unsuspend
96
+
97
+ No body. The account returns to `ACTIVE`, or to `PENDING_VERIFICATION` if verification is still owed. **Response `200`:** `{ "success": true, "user": { ... } }`
98
+
99
+ ### POST /auth/admin/users/:userId/verify-email
100
+
101
+ No body. Marks the email verified (e.g. after a support check) and activates the account if nothing else is pending. **Response `200`:** `{ "success": true, "user": { ... } }`
102
+
103
+ ### POST /auth/admin/users/:userId/reset-2fa
104
+
105
+ No body. Turns off two-factor for a user who lost their phone and backup codes. Confirm their identity first. **Response `200`:** `{ "success": true }`
106
+
107
+ ### POST /auth/admin/users/:userId/logout-all
108
+
109
+ No body. **Response `200`:** `{ "success": true, "endedSessions": 2 }`
110
+
111
+ ### DELETE /auth/admin/users/:userId
112
+
113
+ No body. Deletes the account permanently (personal data wiped). **Response `200`:** `{ "success": true }`
114
+
115
+ ---
116
+
117
+ ## Settings
118
+
119
+ ### GET /auth/admin/settings
120
+
121
+ **Response `200`:** the settings in force (every section; see [Configuration](../configuration.md#reference)).
122
+
123
+ ```json
124
+ {
125
+ "success": true,
126
+ "settings": {
127
+ "registration": { "enabled": true, "defaultRoles": ["user"], "requireFirstAndLastName": true, "requirePhoneNumber": false, "requireTermsAcceptance": true, "requirePrivacyPolicyAcceptance": true, "allowedEmailDomains": [] },
128
+ "activation": { "requireVerifiedEmail": true, "requireVerifiedPhone": false },
129
+ "password": { "enabled": true, "minimumLength": 10, "requireUppercaseLetter": false, "requireLowercaseLetter": false, "requireNumber": false, "requireSymbol": false, "rejectBreachedPasswords": true, "notifyOnChange": true },
130
+ "emailVerification": { "linkExpiresInHours": 24 },
131
+ "passwordReset": { "linkExpiresInMinutes": 60 },
132
+ "emailChange": { "linkExpiresInMinutes": 60 },
133
+ "emailOtp": { "enabled": false, "codeLength": 6, "expiresInMinutes": 10, "resendAfterSeconds": 60, "allowSignUp": true },
134
+ "magicLink": { "enabled": false, "expiresInMinutes": 15, "allowSignUp": true },
135
+ "phoneOtp": { "enabled": false, "codeLength": 6, "expiresInMinutes": 10, "resendAfterSeconds": 60, "allowSignUp": true, "allowedCountryCodes": [] },
136
+ "google": { "enabled": false, "clientIds": [] },
137
+ "apple": { "enabled": false, "clientIds": [] },
138
+ "facebook": { "enabled": false, "appId": "" },
139
+ "passkeys": { "enabled": false },
140
+ "twoFactor": { "enabled": true, "challengeExpiresInMinutes": 5 },
141
+ "organizations": { "enabled": false, "allowUsersToCreate": true, "invitationExpiresInDays": 7 },
142
+ "tokens": { "accessTokenMinutes": 15, "refreshTokenDays": 30, "sessionDays": 90 },
143
+ "admin": { "adminRoles": ["admin"] },
144
+ "security": { "notifyOnNewSignIn": false },
145
+ "retention": { "enabled": true, "expiredRecordDays": 7, "webhookDeliveryDays": 30, "auditLogDays": 365 }
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### PATCH /auth/admin/settings
151
+
152
+ Changes only the fields you send. Changes reach every server within 10 seconds.
153
+
154
+ **Request**
155
+
156
+ ```json
157
+ { "emailOtp": { "enabled": true }, "phoneOtp": { "enabled": true, "allowedCountryCodes": ["+91"] } }
158
+ ```
159
+
160
+ **Response `200`:** `{ "success": true, "settings": { ...settings in force } }`
161
+
162
+ **Errors**
163
+
164
+ ```json
165
+ {
166
+ "success": false,
167
+ "code": "INVALID_SETTINGS",
168
+ "message": "Some settings are not valid.",
169
+ "problems": ["tokens.accessTokenMinutes must not be greater than 60", "google.clientIds is required when Google is enabled"]
170
+ }
171
+ ```
172
+
173
+ ### DELETE /auth/admin/settings
174
+
175
+ No body. Discards all admin changes; the settings from code apply again. **Response `200`:** `{ "success": true, "settings": { ... } }`
176
+
177
+ ---
178
+
179
+ ## Security and operations
180
+
181
+ ### GET /auth/admin/audit-log
182
+
183
+ | Query | Type |
184
+ |---|---|
185
+ | `userId` | uuid |
186
+ | `eventName` | string, e.g. `user.sign_in_failed` |
187
+ | `page`, `pageSize` | number |
188
+
189
+ **Response `200`**
190
+
191
+ ```json
192
+ {
193
+ "success": true,
194
+ "entries": [
195
+ {
196
+ "id": "e1f2...",
197
+ "userId": "5f0c...",
198
+ "performedBy": null,
199
+ "eventName": "user.sign_in_failed",
200
+ "ipAddress": "203.0.113.10",
201
+ "userAgent": "Mozilla/5.0 ...",
202
+ "details": { "signInMethod": "password", "reason": "wrong_email_or_password" },
203
+ "createdAt": "2026-10-20T06:29:00.000Z"
204
+ }
205
+ ],
206
+ "total": 1,
207
+ "page": 1,
208
+ "pageSize": 50
209
+ }
210
+ ```
211
+
212
+ ### GET /auth/admin/stats
213
+
214
+ Numbers for an admin dashboard or monitoring. The last-24-hours counts come from the audit log, so they cover every server.
215
+
216
+ **Response `200`**
217
+
218
+ ```json
219
+ {
220
+ "success": true,
221
+ "usersByStatus": { "PENDING_VERIFICATION": 0, "ACTIVE": 1, "SUSPENDED": 0, "DEACTIVATED": 0, "DELETED": 0 },
222
+ "activeSessions": 1,
223
+ "last24Hours": { "signIns": 1, "failedSignIns": 0, "newUsers": 1, "refreshTokenReuses": 0, "passwordResetsRequested": 0 },
224
+ "pendingWebhookDeliveries": 0,
225
+ "generatedAt": "2026-10-20T09:36:59.812Z"
226
+ }
227
+ ```
228
+
229
+ Worth alerting on: a jump in `failedSignIns` (password guessing), any `refreshTokenReuses` (a token may have been stolen), and `pendingWebhookDeliveries` that keeps growing (a receiver is down).
230
+
231
+ ### POST /auth/admin/maintenance/cleanup
232
+
233
+ No body. Runs the [data-retention cleanup](../configuration.md#data-retention) now (it also runs every hour on each server, even when you never call this). Works even when `retention.enabled` is false, so you can trigger it from your own scheduler instead.
234
+
235
+ **Response `200`:** rows removed per step.
236
+
237
+ ```json
238
+ {
239
+ "success": true,
240
+ "removed": { "endedSessions": 12, "expiredRefreshTokens": 40, "linkTokens": 3, "oneTimeCodes": 25, "signInChallenges": 2, "organizationInvitations": 0, "webhookDeliveries": 130, "auditLog": 0 }
241
+ }
242
+ ```
243
+
244
+ ### POST /auth/admin/signing-keys/rotate
245
+
246
+ No body. New tokens are signed with a new key; tokens signed by the old key keep working until they expire. **Response `200`:** `{ "success": true }`
247
+
248
+ ---
249
+
250
+ ## API keys
251
+
252
+ ### API key object
253
+
254
+ ```json
255
+ {
256
+ "id": "b7c8...",
257
+ "name": "Partner: QuickBooks sync",
258
+ "keyPreview": "ak_3Fx9Lm",
259
+ "scopes": ["orders:read"],
260
+ "organizationId": null,
261
+ "createdByUserId": "5f0c...",
262
+ "createdAt": "2026-10-20T06:30:00.000Z",
263
+ "lastUsedAt": null,
264
+ "expiresAt": "2027-06-30T00:00:00.000Z",
265
+ "revokedAt": null
266
+ }
267
+ ```
268
+
269
+ ### GET /auth/admin/api-keys
270
+
271
+ **Response `200`:** `{ "success": true, "apiKeys": [ ...API key objects ] }` (the key itself is never returned again).
272
+
273
+ ### POST /auth/admin/api-keys
274
+
275
+ **Request**
276
+
277
+ ```json
278
+ { "name": "Partner: QuickBooks sync", "scopes": ["orders:read", "invoices:write"], "organizationId": null, "expiresAt": "2027-06-30T00:00:00Z" }
279
+ ```
280
+
281
+ | Field | Type | Required | Rules |
282
+ |---|---|---|---|
283
+ | `name` | string | yes | up to 100 characters |
284
+ | `scopes` | string[] | yes | up to 50, like `orders:read` (lowercase) |
285
+ | `organizationId` | uuid | no | ties the key to one organization |
286
+ | `expiresAt` | ISO date string | no | |
287
+
288
+ **Response `201`.** `apiKey` is shown **only here**; store it in the calling server's secret manager:
289
+
290
+ ```json
291
+ {
292
+ "success": true,
293
+ "apiKey": "ak_3Fx9LmQ2x4bWZkY2V0ZXN0dG9rZW5leGFtcGxlMTIzNDU",
294
+ "details": { "id": "b7c8...", "name": "Partner: QuickBooks sync", "keyPreview": "ak_3Fx9Lm", "...": "API key object" }
295
+ }
296
+ ```
297
+
298
+ ### DELETE /auth/admin/api-keys/:apiKeyId
299
+
300
+ No body. Revokes immediately. **Response `200`:** `{ "success": true }`. Errors: `404 NOT_FOUND`.
301
+
302
+ ---
303
+
304
+ ## Webhooks
305
+
306
+ ### Webhook object
307
+
308
+ ```json
309
+ {
310
+ "id": "d4e5...",
311
+ "url": "https://hooks.myapp.com/auth",
312
+ "eventNames": ["user.created", "user.deleted"],
313
+ "isEnabled": true,
314
+ "description": "CRM sync",
315
+ "createdAt": "2026-10-20T06:30:00.000Z"
316
+ }
317
+ ```
318
+
319
+ ### GET /auth/admin/webhooks
320
+
321
+ **Response `200`:** `{ "success": true, "webhooks": [ ...webhook objects ] }`
322
+
323
+ ### POST /auth/admin/webhooks
324
+
325
+ **Request**
326
+
327
+ ```json
328
+ { "url": "https://hooks.myapp.com/auth", "eventNames": ["user.created", "user.deleted"], "description": "CRM sync", "isEnabled": true }
329
+ ```
330
+
331
+ | Field | Type | Required | Rules |
332
+ |---|---|---|---|
333
+ | `url` | string | yes | public https URL, up to 2048 characters |
334
+ | `eventNames` | string[] | no | [event names](../events.md); empty = all events |
335
+ | `description` | string | no | up to 200 characters |
336
+ | `isEnabled` | boolean | no | default `true` |
337
+
338
+ **Response `201`.** `signingSecret` is shown **only here**:
339
+
340
+ ```json
341
+ { "success": true, "webhook": { "id": "d4e5...", "...": "webhook object" }, "signingSecret": "whsec_c2VjcmV0ZXhhbXBsZTEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI" }
342
+ ```
343
+
344
+ **Errors**
345
+
346
+ | Status | `code` | When |
347
+ |---|---|---|
348
+ | 400 | `UNSAFE_WEBHOOK_URL` | Not https, has credentials, private/loopback address, or doesn't resolve (`problem` says which) |
349
+ | 400 | `VALIDATION_ERROR` | Unknown event names, or more than 50 webhooks |
350
+
351
+ ```json
352
+ { "success": false, "code": "UNSAFE_WEBHOOK_URL", "message": "Webhook URLs must be public https addresses.", "problem": "URL points to a private network address" }
353
+ ```
354
+
355
+ ### PATCH /auth/admin/webhooks/:webhookId
356
+
357
+ Same fields as create, all optional. **Response `200`:** `{ "success": true, "webhook": { ... } }`
358
+
359
+ ### DELETE /auth/admin/webhooks/:webhookId
360
+
361
+ **Response `200`:** `{ "success": true }`
362
+
363
+ ### POST /auth/admin/webhooks/:webhookId/rotate-secret
364
+
365
+ No body. The old secret stops working at once. **Response `200`:** `{ "success": true, "signingSecret": "whsec_..." }`
366
+
367
+ ### POST /auth/admin/webhooks/:webhookId/test
368
+
369
+ No body. Queues a `webhook.test` delivery. **Response `200`:** `{ "success": true }`
370
+
371
+ ### GET /auth/admin/webhooks/:webhookId/deliveries
372
+
373
+ Paged, newest first.
374
+
375
+ **Response `200`**
376
+
377
+ ```json
378
+ {
379
+ "success": true,
380
+ "deliveries": [
381
+ {
382
+ "id": "7d0b...",
383
+ "webhookId": "d4e5...",
384
+ "eventName": "user.created",
385
+ "payload": { "name": "user.created", "userId": "5f0c...", "data": { "status": "ACTIVE" }, "occurredAt": "2026-10-20T06:30:00.000Z" },
386
+ "status": "failed",
387
+ "attemptCount": 8,
388
+ "nextAttemptAt": "2026-10-23T06:30:00.000Z",
389
+ "lastResponseStatus": 500,
390
+ "lastError": "HTTP 500",
391
+ "createdAt": "2026-10-20T06:30:00.000Z",
392
+ "deliveredAt": null
393
+ }
394
+ ],
395
+ "total": 1,
396
+ "page": 1,
397
+ "pageSize": 50
398
+ }
399
+ ```
400
+
401
+ `status` is `pending`, `delivered` or `failed`.
402
+
403
+ ### POST /auth/admin/webhooks/:webhookId/deliveries/:deliveryId/retry
404
+
405
+ No body. Sends again right away. **Response `200`:** `{ "success": true }`
406
+
407
+ ### What your webhook receives
408
+
409
+ ```http
410
+ POST https://hooks.myapp.com/auth
411
+ Content-Type: application/json
412
+ User-Agent: techelusive-nest-auth-webhooks/1.0
413
+ X-Techelusive-Event: user.created
414
+ X-Techelusive-Delivery: 7d0b...
415
+ X-Techelusive-Signature: t=1792600000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
416
+ ```
417
+
418
+ ```json
419
+ { "id": "7d0b...", "name": "user.created", "userId": "5f0c...", "data": { "status": "ACTIVE" }, "occurredAt": "2026-10-20T06:30:00.000Z" }
420
+ ```
421
+
422
+ Reply with any `2xx` within 10 seconds. To verify the signature, see [Admin, API keys and webhooks](../admin-api-keys-webhooks.md#verifying-the-signature-receiving-side).
@@ -0,0 +1,261 @@
1
+ # Email + password
2
+
3
+ Turned on by default (`password.enabled`). Every endpoint here returns `403 SIGN_IN_METHOD_DISABLED` when it is off. Shared formats, the sign-in result and the user object are described in [Endpoints](README.md).
4
+
5
+ ---
6
+
7
+ ## POST /auth/register
8
+
9
+ Creates an account.
10
+
11
+ | | |
12
+ |---|---|
13
+ | Auth | Public |
14
+ | Rate limit | 10 per IP per hour |
15
+ | Settings used | `registration.*`, `activation.*`, `password.*` |
16
+
17
+ **Request**
18
+
19
+ ```json
20
+ {
21
+ "email": "asha@example.com",
22
+ "password": "correct horse battery",
23
+ "firstName": "Asha",
24
+ "lastName": "Rao",
25
+ "phoneNumber": "+919876543210",
26
+ "acceptTerms": true,
27
+ "acceptPrivacyPolicy": true
28
+ }
29
+ ```
30
+
31
+ | Field | Type | Required | Rules |
32
+ |---|---|---|---|
33
+ | `email` | string | yes | email format |
34
+ | `password` | string | yes | password rules from the settings |
35
+ | `firstName` | string | when `registration.requireFirstAndLastName` | up to 100 characters |
36
+ | `lastName` | string | when `registration.requireFirstAndLastName` | up to 100 characters |
37
+ | `phoneNumber` | string | when `registration.requirePhoneNumber` | phone format |
38
+ | `acceptTerms` | boolean | must be `true` when `registration.requireTermsAcceptance` | |
39
+ | `acceptPrivacyPolicy` | boolean | must be `true` when `registration.requirePrivacyPolicyAcceptance` | |
40
+
41
+ **Response `201`, verification needed** (default settings):
42
+
43
+ ```json
44
+ {
45
+ "success": true,
46
+ "status": "verification_required",
47
+ "message": "Your account has been created. Please verify your details to continue.",
48
+ "pendingVerifications": ["email"]
49
+ }
50
+ ```
51
+
52
+ A verification email is sent. If the email is **already registered**, the response is exactly the same and no account is created; this protects users' privacy.
53
+
54
+ **Response `201`, no verification needed** (when the settings require none): the [sign-in result](README.md#sign-in-result).
55
+
56
+ **Errors**
57
+
58
+ | Status | `code` | When |
59
+ |---|---|---|
60
+ | 400 | `INVALID_EMAIL` | Not an email address |
61
+ | 400 | `INVALID_PHONE` | Phone number not in international form |
62
+ | 400 | `INVALID_PASSWORD` | Breaks a rule. `requirements` lists them, e.g. `["at least 10 characters"]`. Same code, different message, when the password is in a data breach |
63
+ | 400 | `VALIDATION_ERROR` | Names missing: `problems: ["firstName and lastName are required"]` |
64
+ | 400 | `TERMS_NOT_ACCEPTED` / `PRIVACY_POLICY_NOT_ACCEPTED` | Consent missing |
65
+ | 400 | `EMAIL_DOMAIN_NOT_ALLOWED` | `registration.allowedEmailDomains` excludes the domain |
66
+ | 403 | `SIGN_UP_CLOSED` | `registration.enabled` is `false` |
67
+ | 409 | `ACCOUNT_ALREADY_EXISTS` | The phone number belongs to another account |
68
+
69
+ ```json
70
+ {
71
+ "success": false,
72
+ "code": "INVALID_PASSWORD",
73
+ "message": "This password does not meet the password rules.",
74
+ "requirements": ["at least 10 characters", "a number"]
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## POST /auth/login
81
+
82
+ Signs in with email and password.
83
+
84
+ | | |
85
+ |---|---|
86
+ | Auth | Public |
87
+ | Rate limit | 30 per IP and 10 per email per 15 minutes |
88
+
89
+ **Request**
90
+
91
+ ```json
92
+ { "email": "asha@example.com", "password": "correct horse battery" }
93
+ ```
94
+
95
+ | Field | Type | Required |
96
+ |---|---|---|
97
+ | `email` | string | yes |
98
+ | `password` | string | yes |
99
+
100
+ **Response `200`:** the [sign-in result](README.md#sign-in-result) (`signed_in` or `two_factor_required`).
101
+
102
+ **Errors**
103
+
104
+ | Status | `code` | When |
105
+ |---|---|---|
106
+ | 401 | `INVALID_CREDENTIALS` | Wrong email **or** password (deliberately the same) |
107
+ | 403 | `ACCOUNT_NOT_VERIFIED` / `ACCOUNT_DEACTIVATED` / `ACCOUNT_SUSPENDED` | See [sign-in result](README.md#sign-in-result) |
108
+
109
+ ```json
110
+ { "success": false, "code": "INVALID_CREDENTIALS", "message": "Invalid email or password." }
111
+ ```
112
+
113
+ ---
114
+
115
+ ## POST /auth/email/send-verification
116
+
117
+ Sends (or re-sends) the verification link. Answers the same whether or not the email has an account.
118
+
119
+ | | |
120
+ |---|---|
121
+ | Auth | Public |
122
+ | Rate limit | 10 per IP and 3 per email per hour |
123
+
124
+ **Request**
125
+
126
+ ```json
127
+ { "email": "asha@example.com" }
128
+ ```
129
+
130
+ **Response `200`**
131
+
132
+ ```json
133
+ { "success": true, "message": "If this email needs verifying, a verification link is on its way." }
134
+ ```
135
+
136
+ ---
137
+
138
+ ## POST /auth/email/verify
139
+
140
+ Uses the token from the verification link. If nothing else is pending, the account becomes `ACTIVE`.
141
+
142
+ | | |
143
+ |---|---|
144
+ | Auth | Public |
145
+
146
+ **Request**
147
+
148
+ ```json
149
+ { "token": "ev_Q2x4bWZkY2V0ZXN0dG9rZW5leGFtcGxlMTIzNDU2Nzg5MDE" }
150
+ ```
151
+
152
+ **Response `200`**
153
+
154
+ ```json
155
+ {
156
+ "success": true,
157
+ "message": "Your email has been verified.",
158
+ "status": "ACTIVE",
159
+ "pendingVerifications": []
160
+ }
161
+ ```
162
+
163
+ If the settings also require a verified phone, `status` stays `PENDING_VERIFICATION` and `pendingVerifications` is `["phone"]`.
164
+
165
+ **Errors**
166
+
167
+ | Status | `code` | When |
168
+ |---|---|---|
169
+ | 400 | `INVALID_TOKEN` | Unknown, used, expired (`emailVerification.linkExpiresInHours`, 24 by default), or the account's email has changed since |
170
+
171
+ ---
172
+
173
+ ## POST /auth/password/forgot
174
+
175
+ Emails a password reset link. Answers the same whether or not the email has an account.
176
+
177
+ | | |
178
+ |---|---|
179
+ | Auth | Public |
180
+ | Rate limit | 10 per IP and 3 per email per hour |
181
+
182
+ **Request**
183
+
184
+ ```json
185
+ { "email": "asha@example.com" }
186
+ ```
187
+
188
+ **Response `200`**
189
+
190
+ ```json
191
+ { "success": true, "message": "If an account exists for this email, a password reset link is on its way." }
192
+ ```
193
+
194
+ ---
195
+
196
+ ## POST /auth/password/reset
197
+
198
+ Sets a new password using the token from the reset link (valid `passwordReset.linkExpiresInMinutes`, 60 by default). Signs the user out on every device and marks the email verified.
199
+
200
+ | | |
201
+ |---|---|
202
+ | Auth | Public |
203
+
204
+ **Request**
205
+
206
+ ```json
207
+ { "token": "pr_Q2x4bWZkY2V0ZXN0dG9rZW5leGFtcGxlMTIzNDU2Nzg5MDE", "newPassword": "a brand new passphrase" }
208
+ ```
209
+
210
+ | Field | Type | Required | Rules |
211
+ |---|---|---|---|
212
+ | `token` | string | yes | token from the link |
213
+ | `newPassword` | string | yes | password rules |
214
+
215
+ **Response `200`**
216
+
217
+ ```json
218
+ { "success": true, "message": "Your password has been reset. Please sign in with your new password." }
219
+ ```
220
+
221
+ **Errors**
222
+
223
+ | Status | `code` | When |
224
+ |---|---|---|
225
+ | 400 | `INVALID_PASSWORD` | Breaks a rule (`requirements`) |
226
+ | 400 | `INVALID_TOKEN` | Link unknown, used or expired |
227
+
228
+ ---
229
+
230
+ ## POST /auth/password/change
231
+
232
+ Changes the password of the signed-in user. Other devices are signed out; this one stays signed in. Users without a password (signed up with Google etc.) leave out `currentPassword` to set one.
233
+
234
+ | | |
235
+ |---|---|
236
+ | Auth | User (`Authorization: Bearer ...`) |
237
+ | Rate limit | 5 per user per 15 minutes |
238
+
239
+ **Request**
240
+
241
+ ```json
242
+ { "currentPassword": "correct horse battery", "newPassword": "a brand new passphrase" }
243
+ ```
244
+
245
+ | Field | Type | Required |
246
+ |---|---|---|
247
+ | `currentPassword` | string | yes, if the account has a password |
248
+ | `newPassword` | string | yes |
249
+
250
+ **Response `200`**
251
+
252
+ ```json
253
+ { "success": true, "message": "Your password has been changed. Other devices have been signed out." }
254
+ ```
255
+
256
+ **Errors**
257
+
258
+ | Status | `code` | When |
259
+ |---|---|---|
260
+ | 401 | `INVALID_CREDENTIALS` | `currentPassword` is wrong |
261
+ | 400 | `INVALID_PASSWORD` | Breaks a rule, or `requirements: ["different from your current password"]` |