@yielded/auth-persistence 0.1.0-beta.25 → 0.1.0-beta.26

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 (481) hide show
  1. package/README.md +6 -8
  2. package/dist/Adapter.d.mts +47 -33
  3. package/dist/Adapter.mjs +36 -23
  4. package/dist/OAuthPersistence.d.mts +35 -51
  5. package/dist/OAuthPersistence.mjs +60 -68
  6. package/dist/OAuthPersistence.mjs.map +1 -1
  7. package/dist/index.d.mts +2 -2
  8. package/dist/index.mjs +2 -2
  9. package/dist/internal/browser-login.d.mts +1 -1
  10. package/dist/internal/browser-login.mjs +29 -9
  11. package/dist/internal/browser-login.mjs.map +1 -1
  12. package/dist/internal/configuration.d.mts +13 -8
  13. package/dist/internal/configuration.mjs.map +1 -1
  14. package/dist/internal/crypto.d.mts +1 -0
  15. package/dist/internal/crypto.mjs +1 -1
  16. package/dist/internal/d1-planning.mjs +8 -0
  17. package/dist/internal/d1-planning.mjs.map +1 -0
  18. package/dist/internal/email-native-state.mjs +135 -0
  19. package/dist/internal/email-native-state.mjs.map +1 -0
  20. package/dist/internal/email-native.d.mts +17 -0
  21. package/dist/internal/email-native.mjs +245 -0
  22. package/dist/internal/email-native.mjs.map +1 -0
  23. package/dist/internal/email-policy.mjs +92 -0
  24. package/dist/internal/email-policy.mjs.map +1 -0
  25. package/dist/internal/email-registration-native.d.mts +38 -0
  26. package/dist/internal/email-registration-native.mjs +171 -0
  27. package/dist/internal/email-registration-native.mjs.map +1 -0
  28. package/dist/internal/models/email-model.d.mts +40 -104
  29. package/dist/internal/models/email-model.mjs +3 -5
  30. package/dist/internal/models/email-model.mjs.map +1 -1
  31. package/dist/internal/models/oauth-connected-model.d.mts +44 -213
  32. package/dist/internal/models/oauth-connected-model.mjs +6 -14
  33. package/dist/internal/models/oauth-connected-model.mjs.map +1 -1
  34. package/dist/internal/models/oauth-model.d.mts +95 -244
  35. package/dist/internal/models/oauth-model.mjs +28 -22
  36. package/dist/internal/models/oauth-model.mjs.map +1 -1
  37. package/dist/internal/models/passkey-model.d.mts +11 -147
  38. package/dist/internal/models/passkey-model.mjs +5 -24
  39. package/dist/internal/models/passkey-model.mjs.map +1 -1
  40. package/dist/internal/models/passkey-registration-ceremony-model.d.mts +5 -39
  41. package/dist/internal/models/passkey-write-model.d.mts +17 -75
  42. package/dist/internal/models/passkey-write-model.mjs +1 -7
  43. package/dist/internal/models/passkey-write-model.mjs.map +1 -1
  44. package/dist/internal/models/password-model.d.mts +14 -81
  45. package/dist/internal/models/password-model.mjs +1 -4
  46. package/dist/internal/models/password-model.mjs.map +1 -1
  47. package/dist/internal/models/phone-model.d.mts +86 -0
  48. package/dist/internal/models/phone-model.mjs +10 -0
  49. package/dist/internal/models/phone-model.mjs.map +1 -0
  50. package/dist/internal/models/proof-model.d.mts +43 -272
  51. package/dist/internal/models/proof-model.mjs +2 -10
  52. package/dist/internal/models/proof-model.mjs.map +1 -1
  53. package/dist/internal/models/session-model.d.mts +74 -93
  54. package/dist/internal/models/session-model.mjs +3 -11
  55. package/dist/internal/models/session-model.mjs.map +1 -1
  56. package/dist/internal/models/step-up-model.d.mts +12 -50
  57. package/dist/internal/models/step-up-model.mjs +2 -4
  58. package/dist/internal/models/step-up-model.mjs.map +1 -1
  59. package/dist/internal/models/totp-model.d.mts +76 -0
  60. package/dist/internal/models/totp-model.mjs +10 -0
  61. package/dist/internal/models/totp-model.mjs.map +1 -0
  62. package/dist/internal/native-sql-table.d.mts +21 -4
  63. package/dist/internal/native-sql-table.mjs +9 -5
  64. package/dist/internal/native-sql-table.mjs.map +1 -1
  65. package/dist/internal/oauth/native-accounts.d.mts +17 -0
  66. package/dist/internal/oauth/native-accounts.mjs +290 -0
  67. package/dist/internal/oauth/native-accounts.mjs.map +1 -0
  68. package/dist/internal/oauth/native-connected-state.d.mts +11 -0
  69. package/dist/internal/oauth/native-connected-state.mjs +146 -0
  70. package/dist/internal/oauth/native-connected-state.mjs.map +1 -0
  71. package/dist/internal/oauth/native-connected.d.mts +15 -0
  72. package/dist/internal/oauth/native-connected.mjs +462 -0
  73. package/dist/internal/oauth/native-connected.mjs.map +1 -0
  74. package/dist/internal/oauth/native-flow.mjs +138 -0
  75. package/dist/internal/oauth/native-flow.mjs.map +1 -0
  76. package/dist/internal/oauth/native-layers.d.mts +24 -0
  77. package/dist/internal/oauth/native-layers.mjs +13 -0
  78. package/dist/internal/oauth/native-layers.mjs.map +1 -0
  79. package/dist/internal/oauth/native-mutation.mjs +122 -0
  80. package/dist/internal/oauth/native-mutation.mjs.map +1 -0
  81. package/dist/internal/oauth/native-registration.d.mts +24 -0
  82. package/dist/internal/oauth/native-registration.mjs +328 -0
  83. package/dist/internal/oauth/native-registration.mjs.map +1 -0
  84. package/dist/internal/oauth/native-revocations.d.mts +17 -0
  85. package/dist/internal/oauth/native-revocations.mjs +106 -0
  86. package/dist/internal/oauth/native-revocations.mjs.map +1 -0
  87. package/dist/internal/oauth/native-sign-in.d.mts +17 -0
  88. package/dist/internal/oauth/native-sign-in.mjs +50 -0
  89. package/dist/internal/oauth/native-sign-in.mjs.map +1 -0
  90. package/dist/internal/oauth/native-state.d.mts +10 -0
  91. package/dist/internal/oauth/native-state.mjs +214 -0
  92. package/dist/internal/oauth/native-state.mjs.map +1 -0
  93. package/dist/internal/oauth/state.d.mts +10 -0
  94. package/dist/internal/oauth/state.mjs +3 -27
  95. package/dist/internal/oauth/state.mjs.map +1 -1
  96. package/dist/internal/passkey-actions.mjs +55 -0
  97. package/dist/internal/passkey-actions.mjs.map +1 -0
  98. package/dist/internal/passkey-inputs.mjs +73 -0
  99. package/dist/internal/passkey-inputs.mjs.map +1 -0
  100. package/dist/internal/passkey-managed.mjs +224 -0
  101. package/dist/internal/passkey-managed.mjs.map +1 -0
  102. package/dist/internal/passkey-native-ceremony.d.mts +35 -0
  103. package/dist/internal/passkey-native-ceremony.mjs +38 -0
  104. package/dist/internal/passkey-native-ceremony.mjs.map +1 -0
  105. package/dist/internal/passkey-native-flow.mjs +91 -0
  106. package/dist/internal/passkey-native-flow.mjs.map +1 -0
  107. package/dist/internal/passkey-native-registration.d.mts +10 -0
  108. package/dist/internal/passkey-native-registration.mjs +114 -0
  109. package/dist/internal/passkey-native-registration.mjs.map +1 -0
  110. package/dist/internal/passkey-native-state.d.mts +15 -0
  111. package/dist/internal/passkey-native-state.mjs +186 -0
  112. package/dist/internal/passkey-native-state.mjs.map +1 -0
  113. package/dist/internal/passkey-native-write.d.mts +82 -0
  114. package/dist/internal/passkey-native-write.mjs +330 -0
  115. package/dist/internal/passkey-native-write.mjs.map +1 -0
  116. package/dist/internal/passkey-native.d.mts +835 -0
  117. package/dist/internal/passkey-native.mjs +175 -0
  118. package/dist/internal/passkey-native.mjs.map +1 -0
  119. package/dist/internal/passkey-policy.mjs +115 -0
  120. package/dist/internal/passkey-policy.mjs.map +1 -0
  121. package/dist/internal/password-credentials.mjs +38 -9
  122. package/dist/internal/password-credentials.mjs.map +1 -1
  123. package/dist/internal/password-native.d.mts +14 -0
  124. package/dist/internal/password-native.mjs +292 -0
  125. package/dist/internal/password-native.mjs.map +1 -0
  126. package/dist/internal/password-policy.mjs +93 -0
  127. package/dist/internal/password-policy.mjs.map +1 -0
  128. package/dist/internal/password-registration-native.d.mts +22 -0
  129. package/dist/internal/password-registration-native.mjs +142 -0
  130. package/dist/internal/password-registration-native.mjs.map +1 -0
  131. package/dist/internal/persistence.d.mts +6 -19
  132. package/dist/internal/persistence.mjs +199 -166
  133. package/dist/internal/persistence.mjs.map +1 -1
  134. package/dist/internal/phone-composed.mjs +79 -0
  135. package/dist/internal/phone-composed.mjs.map +1 -0
  136. package/dist/internal/phone-native.d.mts +15 -0
  137. package/dist/internal/phone-native.mjs +354 -0
  138. package/dist/internal/phone-native.mjs.map +1 -0
  139. package/dist/internal/proof-native.d.mts +15 -0
  140. package/dist/internal/proof-native.mjs +289 -0
  141. package/dist/internal/proof-native.mjs.map +1 -0
  142. package/dist/internal/registration.mjs +50 -60
  143. package/dist/internal/registration.mjs.map +1 -1
  144. package/dist/internal/session-native-authority.d.mts +15 -0
  145. package/dist/internal/session-native-authority.mjs +91 -0
  146. package/dist/internal/session-native-authority.mjs.map +1 -0
  147. package/dist/internal/session-native-cleanup.d.mts +14 -0
  148. package/dist/internal/session-native-cleanup.mjs +48 -0
  149. package/dist/internal/session-native-cleanup.mjs.map +1 -0
  150. package/dist/internal/session-native-login.d.mts +15 -0
  151. package/dist/internal/session-native-login.mjs +87 -0
  152. package/dist/internal/session-native-login.mjs.map +1 -0
  153. package/dist/internal/session-native-pending-read.mjs +42 -0
  154. package/dist/internal/session-native-pending-read.mjs.map +1 -0
  155. package/dist/internal/session-native-pending.mjs +134 -0
  156. package/dist/internal/session-native-pending.mjs.map +1 -0
  157. package/dist/internal/session-native-record.mjs +79 -0
  158. package/dist/internal/session-native-record.mjs.map +1 -0
  159. package/dist/internal/session-native-state.mjs +130 -0
  160. package/dist/internal/session-native-state.mjs.map +1 -0
  161. package/dist/internal/session-native-stateful.d.mts +16 -0
  162. package/dist/internal/session-native-stateful.mjs +225 -0
  163. package/dist/internal/session-native-stateful.mjs.map +1 -0
  164. package/dist/internal/session-native-step-up.d.mts +15 -0
  165. package/dist/internal/session-native-step-up.mjs +212 -0
  166. package/dist/internal/session-native-step-up.mjs.map +1 -0
  167. package/dist/internal/session-native-validity.d.mts +15 -0
  168. package/dist/internal/session-native-validity.mjs +97 -0
  169. package/dist/internal/session-native-validity.mjs.map +1 -0
  170. package/dist/internal/session-policy.mjs +29 -0
  171. package/dist/internal/session-policy.mjs.map +1 -0
  172. package/dist/internal/sql-authority.d.mts +16 -0
  173. package/dist/internal/sql-authority.mjs +32 -0
  174. package/dist/internal/sql-authority.mjs.map +1 -0
  175. package/dist/internal/sql-change.mjs +21 -0
  176. package/dist/internal/sql-change.mjs.map +1 -0
  177. package/dist/internal/sql-cleanup.mjs +35 -0
  178. package/dist/internal/sql-cleanup.mjs.map +1 -0
  179. package/dist/internal/sql-commit.d.mts +72 -0
  180. package/dist/internal/sql-commit.mjs +176 -0
  181. package/dist/internal/sql-commit.mjs.map +1 -0
  182. package/dist/internal/sql-expression.d.mts +17 -0
  183. package/dist/internal/sql-expression.mjs +49 -0
  184. package/dist/internal/sql-expression.mjs.map +1 -0
  185. package/dist/internal/sql-oauth-connected-model.d.mts +7 -23
  186. package/dist/internal/sql-oauth-model.d.mts +27 -52
  187. package/dist/internal/sql-oauth-model.mjs +3 -4
  188. package/dist/internal/sql-oauth-model.mjs.map +1 -1
  189. package/dist/internal/sql-persistence.mjs +5 -9
  190. package/dist/internal/sql-persistence.mjs.map +1 -1
  191. package/dist/internal/sql-storage-validation.mjs +1 -2
  192. package/dist/internal/sql-storage-validation.mjs.map +1 -1
  193. package/dist/internal/sql-table.d.mts +3 -16
  194. package/dist/internal/sql-table.mjs +21 -42
  195. package/dist/internal/sql-table.mjs.map +1 -1
  196. package/dist/internal/standalone.mjs +2 -3
  197. package/dist/internal/standalone.mjs.map +1 -1
  198. package/dist/internal/step-up-state.mjs +5 -8
  199. package/dist/internal/step-up-state.mjs.map +1 -1
  200. package/dist/internal/storage-clock.mjs +44 -0
  201. package/dist/internal/storage-clock.mjs.map +1 -0
  202. package/dist/internal/storage-mapping.d.mts +11 -8
  203. package/dist/internal/storage-mapping.mjs +88 -302
  204. package/dist/internal/storage-mapping.mjs.map +1 -1
  205. package/dist/internal/storage-plans.mjs +15 -73
  206. package/dist/internal/storage-plans.mjs.map +1 -1
  207. package/dist/internal/storage-tables.d.mts +37 -520
  208. package/dist/internal/storage-tables.mjs +31 -211
  209. package/dist/internal/storage-tables.mjs.map +1 -1
  210. package/dist/internal/storage-validation.d.mts +1 -6
  211. package/dist/internal/storage-validation.mjs +1 -42
  212. package/dist/internal/storage-validation.mjs.map +1 -1
  213. package/dist/internal/table-model.d.mts +21 -0
  214. package/dist/internal/totp-native.d.mts +15 -0
  215. package/dist/internal/totp-native.mjs +401 -0
  216. package/dist/internal/totp-native.mjs.map +1 -0
  217. package/package.json +1 -1
  218. package/src/Adapter.ts +143 -199
  219. package/src/OAuthPersistence.ts +128 -271
  220. package/src/index.ts +1 -1
  221. package/src/internal/browser-login.ts +54 -24
  222. package/src/internal/configuration.ts +16 -31
  223. package/src/internal/d1-planning.ts +7 -0
  224. package/src/internal/email-native-state.ts +247 -0
  225. package/src/internal/email-native.ts +514 -0
  226. package/src/internal/email-policy.ts +235 -0
  227. package/src/internal/email-registration-native.ts +386 -0
  228. package/src/internal/models/email-model.ts +76 -214
  229. package/src/internal/models/oauth-connected-model.ts +50 -288
  230. package/src/internal/models/oauth-model.ts +147 -368
  231. package/src/internal/models/passkey-model.ts +11 -208
  232. package/src/internal/models/passkey-registration-ceremony-model.ts +6 -60
  233. package/src/internal/models/passkey-write-model.ts +14 -95
  234. package/src/internal/models/password-model.ts +26 -147
  235. package/src/internal/models/phone-model.ts +110 -0
  236. package/src/internal/models/proof-model.ts +48 -404
  237. package/src/internal/models/session-model.ts +102 -200
  238. package/src/internal/models/step-up-model.ts +17 -96
  239. package/src/internal/models/totp-model.ts +82 -0
  240. package/src/internal/native-sql-table.ts +32 -11
  241. package/src/internal/oauth/native-accounts.ts +576 -0
  242. package/src/internal/oauth/native-connected-state.ts +288 -0
  243. package/src/internal/oauth/native-connected.ts +888 -0
  244. package/src/internal/oauth/native-flow.ts +253 -0
  245. package/src/internal/oauth/native-layers.ts +83 -0
  246. package/src/internal/oauth/native-mutation.ts +222 -0
  247. package/src/internal/oauth/native-registration.ts +545 -0
  248. package/src/internal/oauth/native-revocations.ts +190 -0
  249. package/src/internal/oauth/native-sign-in.ts +111 -0
  250. package/src/internal/oauth/native-state.ts +346 -0
  251. package/src/internal/oauth/state.ts +2 -70
  252. package/src/internal/passkey-actions.ts +70 -0
  253. package/src/internal/passkey-inputs.ts +56 -0
  254. package/src/internal/passkey-managed.ts +360 -0
  255. package/src/internal/passkey-native-ceremony.ts +66 -0
  256. package/src/internal/passkey-native-flow.ts +140 -0
  257. package/src/internal/passkey-native-registration.ts +221 -0
  258. package/src/internal/passkey-native-state.ts +335 -0
  259. package/src/internal/passkey-native-write.ts +611 -0
  260. package/src/internal/passkey-native.ts +372 -0
  261. package/src/internal/passkey-policy.ts +246 -0
  262. package/src/internal/password-credentials.ts +52 -10
  263. package/src/internal/password-native.ts +571 -0
  264. package/src/internal/password-policy.ts +307 -0
  265. package/src/internal/password-registration-native.ts +322 -0
  266. package/src/internal/persistence.ts +336 -290
  267. package/src/internal/phone-composed.ts +86 -0
  268. package/src/internal/phone-native.ts +659 -0
  269. package/src/internal/proof-native.ts +448 -0
  270. package/src/internal/registration.ts +96 -143
  271. package/src/internal/session-native-authority.ts +247 -0
  272. package/src/internal/session-native-cleanup.ts +65 -0
  273. package/src/internal/session-native-login.ts +205 -0
  274. package/src/internal/session-native-pending-read.ts +68 -0
  275. package/src/internal/session-native-pending.ts +223 -0
  276. package/src/internal/session-native-record.ts +141 -0
  277. package/src/internal/session-native-state.ts +259 -0
  278. package/src/internal/session-native-stateful.ts +512 -0
  279. package/src/internal/session-native-step-up.ts +450 -0
  280. package/src/internal/session-native-validity.ts +208 -0
  281. package/src/internal/session-policy.ts +59 -0
  282. package/src/internal/sql-authority.ts +56 -0
  283. package/src/internal/sql-change.ts +38 -0
  284. package/src/internal/sql-cleanup.ts +56 -0
  285. package/src/internal/sql-commit.ts +435 -0
  286. package/src/internal/sql-expression.ts +94 -0
  287. package/src/internal/sql-metadata.ts +100 -0
  288. package/src/internal/sql-oauth-connected-model.ts +6 -95
  289. package/src/internal/sql-oauth-model.ts +31 -134
  290. package/src/internal/sql-persistence.ts +2 -6
  291. package/src/internal/sql-storage-validation.ts +1 -2
  292. package/src/internal/sql-table.ts +19 -52
  293. package/src/internal/standalone.ts +2 -11
  294. package/src/internal/step-up-state.ts +4 -17
  295. package/src/internal/storage-clock.ts +58 -0
  296. package/src/internal/storage-mapping.ts +125 -340
  297. package/src/internal/storage-plans.ts +14 -80
  298. package/src/internal/storage-tables.ts +28 -212
  299. package/src/internal/storage-validation.ts +0 -58
  300. package/src/internal/table-model.ts +24 -0
  301. package/src/internal/totp-native.ts +701 -0
  302. package/dist/internal/PasswordPreparedPostconditions.d.mts +0 -22
  303. package/dist/internal/PasswordPreparedPostconditions.mjs +0 -12
  304. package/dist/internal/PasswordPreparedPostconditions.mjs.map +0 -1
  305. package/dist/internal/authority-snapshot.mjs +0 -53
  306. package/dist/internal/authority-snapshot.mjs.map +0 -1
  307. package/dist/internal/email-kernel.d.mts +0 -276
  308. package/dist/internal/email-kernel.mjs +0 -497
  309. package/dist/internal/email-kernel.mjs.map +0 -1
  310. package/dist/internal/models/passkey-registration-ceremony-model.mjs +0 -18
  311. package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +0 -1
  312. package/dist/internal/oauth/accounts.d.mts +0 -8
  313. package/dist/internal/oauth/accounts.mjs +0 -379
  314. package/dist/internal/oauth/accounts.mjs.map +0 -1
  315. package/dist/internal/oauth/connected-access.d.mts +0 -9
  316. package/dist/internal/oauth/connected-access.mjs +0 -229
  317. package/dist/internal/oauth/connected-access.mjs.map +0 -1
  318. package/dist/internal/oauth/connected-collection.d.mts +0 -7
  319. package/dist/internal/oauth/connected-collection.mjs +0 -214
  320. package/dist/internal/oauth/connected-collection.mjs.map +0 -1
  321. package/dist/internal/oauth/connected-custody.d.mts +0 -6
  322. package/dist/internal/oauth/connected-custody.mjs +0 -165
  323. package/dist/internal/oauth/connected-custody.mjs.map +0 -1
  324. package/dist/internal/oauth/connected-flow.d.mts +0 -7
  325. package/dist/internal/oauth/connected-flow.mjs +0 -324
  326. package/dist/internal/oauth/connected-flow.mjs.map +0 -1
  327. package/dist/internal/oauth/connected-input.mjs +0 -122
  328. package/dist/internal/oauth/connected-input.mjs.map +0 -1
  329. package/dist/internal/oauth/connected-keys.d.mts +0 -2
  330. package/dist/internal/oauth/connected-keys.mjs +0 -22
  331. package/dist/internal/oauth/connected-keys.mjs.map +0 -1
  332. package/dist/internal/oauth/connected-maintenance.d.mts +0 -9
  333. package/dist/internal/oauth/connected-maintenance.mjs +0 -226
  334. package/dist/internal/oauth/connected-maintenance.mjs.map +0 -1
  335. package/dist/internal/oauth/connected-management.d.mts +0 -9
  336. package/dist/internal/oauth/connected-management.mjs +0 -243
  337. package/dist/internal/oauth/connected-management.mjs.map +0 -1
  338. package/dist/internal/oauth/connected-reference.mjs +0 -63
  339. package/dist/internal/oauth/connected-reference.mjs.map +0 -1
  340. package/dist/internal/oauth/connected-settlement.d.mts +0 -8
  341. package/dist/internal/oauth/connected-settlement.mjs +0 -141
  342. package/dist/internal/oauth/connected-settlement.mjs.map +0 -1
  343. package/dist/internal/oauth/connected-sign-in.d.mts +0 -10
  344. package/dist/internal/oauth/connected-sign-in.mjs +0 -281
  345. package/dist/internal/oauth/connected-sign-in.mjs.map +0 -1
  346. package/dist/internal/oauth/connected-state.d.mts +0 -14
  347. package/dist/internal/oauth/connected-state.mjs +0 -469
  348. package/dist/internal/oauth/connected-state.mjs.map +0 -1
  349. package/dist/internal/oauth/connected-target.d.mts +0 -21
  350. package/dist/internal/oauth/connected-target.mjs +0 -117
  351. package/dist/internal/oauth/connected-target.mjs.map +0 -1
  352. package/dist/internal/oauth/flow.d.mts +0 -5
  353. package/dist/internal/oauth/flow.mjs +0 -279
  354. package/dist/internal/oauth/flow.mjs.map +0 -1
  355. package/dist/internal/oauth/input.mjs +0 -127
  356. package/dist/internal/oauth/input.mjs.map +0 -1
  357. package/dist/internal/oauth/owner.d.mts +0 -10
  358. package/dist/internal/oauth/owner.mjs +0 -10
  359. package/dist/internal/oauth/owner.mjs.map +0 -1
  360. package/dist/internal/oauth/query-compiler.d.mts +0 -7
  361. package/dist/internal/oauth/query-compiler.mjs +0 -9
  362. package/dist/internal/oauth/query-compiler.mjs.map +0 -1
  363. package/dist/internal/oauth/registration.d.mts +0 -7
  364. package/dist/internal/oauth/registration.mjs +0 -393
  365. package/dist/internal/oauth/registration.mjs.map +0 -1
  366. package/dist/internal/oauth/target.d.mts +0 -31
  367. package/dist/internal/oauth/target.mjs +0 -273
  368. package/dist/internal/oauth/target.mjs.map +0 -1
  369. package/dist/internal/oauth-kernel.d.mts +0 -1423
  370. package/dist/internal/oauth-kernel.mjs +0 -187
  371. package/dist/internal/oauth-kernel.mjs.map +0 -1
  372. package/dist/internal/passkey/admission.d.mts +0 -6
  373. package/dist/internal/passkey/admission.mjs +0 -220
  374. package/dist/internal/passkey/admission.mjs.map +0 -1
  375. package/dist/internal/passkey/credentials.d.mts +0 -14
  376. package/dist/internal/passkey/credentials.mjs +0 -249
  377. package/dist/internal/passkey/credentials.mjs.map +0 -1
  378. package/dist/internal/passkey/enrollment.mjs +0 -138
  379. package/dist/internal/passkey/enrollment.mjs.map +0 -1
  380. package/dist/internal/passkey/flow.d.mts +0 -21
  381. package/dist/internal/passkey/flow.mjs +0 -452
  382. package/dist/internal/passkey/flow.mjs.map +0 -1
  383. package/dist/internal/passkey/management.mjs +0 -231
  384. package/dist/internal/passkey/management.mjs.map +0 -1
  385. package/dist/internal/passkey/registration-ceremony.d.mts +0 -9
  386. package/dist/internal/passkey/registration-ceremony.mjs +0 -129
  387. package/dist/internal/passkey/registration-ceremony.mjs.map +0 -1
  388. package/dist/internal/passkey/registration-custody.d.mts +0 -2
  389. package/dist/internal/passkey/registration-custody.mjs +0 -52
  390. package/dist/internal/passkey/registration-custody.mjs.map +0 -1
  391. package/dist/internal/passkey/registration-write.mjs +0 -211
  392. package/dist/internal/passkey/registration-write.mjs.map +0 -1
  393. package/dist/internal/passkey/state.d.mts +0 -10
  394. package/dist/internal/passkey/state.mjs +0 -234
  395. package/dist/internal/passkey/state.mjs.map +0 -1
  396. package/dist/internal/passkey/target.d.mts +0 -20
  397. package/dist/internal/passkey/target.mjs +0 -200
  398. package/dist/internal/passkey/target.mjs.map +0 -1
  399. package/dist/internal/passkey/write-state.d.mts +0 -17
  400. package/dist/internal/passkey/write-state.mjs +0 -354
  401. package/dist/internal/passkey/write-state.mjs.map +0 -1
  402. package/dist/internal/passkey/write-target.mjs +0 -192
  403. package/dist/internal/passkey/write-target.mjs.map +0 -1
  404. package/dist/internal/passkey-kernel.d.mts +0 -6045
  405. package/dist/internal/passkey-kernel.mjs +0 -49
  406. package/dist/internal/passkey-kernel.mjs.map +0 -1
  407. package/dist/internal/passkeys.d.mts +0 -7
  408. package/dist/internal/passkeys.mjs +0 -387
  409. package/dist/internal/passkeys.mjs.map +0 -1
  410. package/dist/internal/password-credentials.d.mts +0 -11
  411. package/dist/internal/password-kernel.d.mts +0 -278
  412. package/dist/internal/password-kernel.mjs +0 -508
  413. package/dist/internal/password-kernel.mjs.map +0 -1
  414. package/dist/internal/phone-kernel.d.mts +0 -162
  415. package/dist/internal/phone-kernel.mjs +0 -437
  416. package/dist/internal/phone-kernel.mjs.map +0 -1
  417. package/dist/internal/proof-kernel.d.mts +0 -94
  418. package/dist/internal/proof-kernel.mjs +0 -624
  419. package/dist/internal/proof-kernel.mjs.map +0 -1
  420. package/dist/internal/query-operations.d.mts +0 -68
  421. package/dist/internal/session-kernel.d.mts +0 -62
  422. package/dist/internal/session-kernel.mjs +0 -820
  423. package/dist/internal/session-kernel.mjs.map +0 -1
  424. package/dist/internal/sql-oauth-connected-model.mjs +0 -7
  425. package/dist/internal/sql-oauth-connected-model.mjs.map +0 -1
  426. package/dist/internal/sql-query.d.mts +0 -12
  427. package/dist/internal/sql-query.mjs +0 -206
  428. package/dist/internal/sql-query.mjs.map +0 -1
  429. package/dist/internal/step-up-state.d.mts +0 -49
  430. package/dist/internal/transaction-execution-kernel.d.mts +0 -37
  431. package/dist/internal/transaction-execution-kernel.mjs +0 -120
  432. package/dist/internal/transaction-execution-kernel.mjs.map +0 -1
  433. package/dist/internal/transaction-kernel.d.mts +0 -81
  434. package/dist/internal/transaction-kernel.mjs +0 -264
  435. package/dist/internal/transaction-kernel.mjs.map +0 -1
  436. package/src/internal/PasswordPreparedPostconditions.ts +0 -30
  437. package/src/internal/authority-snapshot.ts +0 -91
  438. package/src/internal/email-kernel.ts +0 -1556
  439. package/src/internal/oauth/accounts.ts +0 -839
  440. package/src/internal/oauth/connected-access.ts +0 -388
  441. package/src/internal/oauth/connected-collection.ts +0 -345
  442. package/src/internal/oauth/connected-custody.ts +0 -308
  443. package/src/internal/oauth/connected-flow.ts +0 -562
  444. package/src/internal/oauth/connected-input.ts +0 -147
  445. package/src/internal/oauth/connected-keys.ts +0 -31
  446. package/src/internal/oauth/connected-maintenance.ts +0 -379
  447. package/src/internal/oauth/connected-management.ts +0 -391
  448. package/src/internal/oauth/connected-reference.ts +0 -135
  449. package/src/internal/oauth/connected-settlement.ts +0 -278
  450. package/src/internal/oauth/connected-sign-in.ts +0 -472
  451. package/src/internal/oauth/connected-state.ts +0 -924
  452. package/src/internal/oauth/connected-target.ts +0 -307
  453. package/src/internal/oauth/flow.ts +0 -505
  454. package/src/internal/oauth/input.ts +0 -201
  455. package/src/internal/oauth/owner.ts +0 -22
  456. package/src/internal/oauth/query-compiler.ts +0 -15
  457. package/src/internal/oauth/registration.ts +0 -676
  458. package/src/internal/oauth/target.ts +0 -803
  459. package/src/internal/oauth-kernel.ts +0 -294
  460. package/src/internal/passkey/admission.ts +0 -368
  461. package/src/internal/passkey/credentials.ts +0 -511
  462. package/src/internal/passkey/enrollment.ts +0 -326
  463. package/src/internal/passkey/flow.ts +0 -1062
  464. package/src/internal/passkey/management.ts +0 -426
  465. package/src/internal/passkey/registration-ceremony.ts +0 -283
  466. package/src/internal/passkey/registration-custody.ts +0 -91
  467. package/src/internal/passkey/registration-write.ts +0 -426
  468. package/src/internal/passkey/state.ts +0 -351
  469. package/src/internal/passkey/target.ts +0 -598
  470. package/src/internal/passkey/write-state.ts +0 -692
  471. package/src/internal/passkey/write-target.ts +0 -450
  472. package/src/internal/passkey-kernel.ts +0 -112
  473. package/src/internal/passkeys.ts +0 -652
  474. package/src/internal/password-kernel.ts +0 -1523
  475. package/src/internal/phone-kernel.ts +0 -872
  476. package/src/internal/proof-kernel.ts +0 -1992
  477. package/src/internal/query-operations.ts +0 -91
  478. package/src/internal/session-kernel.ts +0 -2406
  479. package/src/internal/sql-query.ts +0 -304
  480. package/src/internal/transaction-execution-kernel.ts +0 -335
  481. package/src/internal/transaction-kernel.ts +0 -603
@@ -0,0 +1,701 @@
1
+ import type { LifecycleHooks } from "@yielded/auth/Hooks";
2
+ import { AuthenticationRequirement, SecurityRevision } from "@yielded/auth/Sessions";
3
+ import {
4
+ TotpUnavailable,
5
+ TotpMutation,
6
+ TotpPolicy,
7
+ TotpRecord,
8
+ TotpSnapshot,
9
+ type TotpDecision,
10
+ type TotpPersistence,
11
+ } from "@yielded/auth/Totp";
12
+ import { Crypto, DateTime, Effect, Option, Schema } from "effect";
13
+ import { SqlClient } from "effect/sql/SqlClient";
14
+ import type { Fragment } from "effect/sql/Statement";
15
+
16
+ import { digest, randomId } from "./crypto";
17
+ import { sqlBatchAssertion } from "./d1-planning";
18
+ import { PersistenceMappingError } from "./mapping-error";
19
+ import type { TotpMapping } from "./models/totp-model";
20
+ import type { NativeSqlTables, SqlTable } from "./native-sql-table";
21
+ import { makeNativeSessionPending, prepareNativeSession } from "./session-native-pending";
22
+ import { makeConditionalSqlInsert } from "./session-native-record";
23
+ import { assessSessionAt, sameSessionRevision } from "./session-native-state";
24
+ import { exactSqlText, executeSqlChange } from "./sql-change";
25
+ import {
26
+ CurrentSqlCommit,
27
+ makeSqlCommitExecutor,
28
+ SqlBatchCommit,
29
+ appendSqlBatchStatement,
30
+ registerSqlBatchPostcondition,
31
+ registerSqlPostcondition,
32
+ } from "./sql-commit";
33
+ import type { AnyTableModel } from "./table-model";
34
+
35
+ export type NativeTotpMapping = TotpMapping<
36
+ AnyTableModel,
37
+ AnyTableModel,
38
+ AnyTableModel,
39
+ unknown,
40
+ AnyTableModel
41
+ >;
42
+
43
+ const unavailable = () => TotpUnavailable.make({});
44
+
45
+ const invariant: (value: unknown) => asserts value = (value) => {
46
+ if (!value)
47
+ throw PersistenceMappingError.make({ operation: "decode", cause: "Invalid TOTP authority" });
48
+ };
49
+
50
+ const codec = Schema.fromJsonString(TotpRecord);
51
+ const encodeTotpRecord = Schema.encodeSync(codec);
52
+ const reject: TotpDecision = { _tag: "Rejected" };
53
+
54
+ export const makeNativeTotpServices = Effect.fnUntraced(function* (
55
+ tables: NativeSqlTables,
56
+ mapping: NativeTotpMapping,
57
+ ): Effect.fn.Return<
58
+ { readonly totpPersistence: TotpPersistence["Service"] },
59
+ never,
60
+ SqlClient | LifecycleHooks | Crypto.Crypto | SqlBatchCommit
61
+ > {
62
+ const batch = yield* SqlBatchCommit;
63
+ const conditionalInsert = yield* makeConditionalSqlInsert();
64
+
65
+ const sql = (yield* SqlClient).withoutTransforms(),
66
+ crypto = yield* Crypto.Crypto,
67
+ executor = yield* makeSqlCommitExecutor(unavailable),
68
+ external = yield* Effect.serviceOption(CurrentSqlCommit);
69
+
70
+ const s = mapping.subject,
71
+ f = mapping.factor,
72
+ c = mapping.credential,
73
+ subject = tables(s.table),
74
+ factor = tables(f.table),
75
+ credential = tables(c.table),
76
+ engineNow = tables.expression(mapping.engineNowMillis);
77
+
78
+ const active = tables.expression(s.activeCondition),
79
+ activeCredential = tables.expression(c.activeCondition);
80
+
81
+ const lock = sql.onDialectOrElse({ sqlite: () => sql``, orElse: () => sql`for update` });
82
+
83
+ const nativeLocks =
84
+ batch === undefined && sql.onDialectOrElse({ sqlite: () => false, orElse: () => true });
85
+
86
+ const exact = (table: SqlTable, key: string, value: unknown) =>
87
+ exactSqlText(sql, table.column(key), table.value(key, value));
88
+
89
+ const id = (table: SqlTable, key: string, value: unknown) =>
90
+ sql`${table.column(key)} = ${table.value(key, value)}`;
91
+
92
+ const pending =
93
+ mapping.pending === undefined
94
+ ? undefined
95
+ : yield* makeNativeSessionPending(
96
+ tables,
97
+ {
98
+ ...mapping.pending,
99
+ subjectId: {
100
+ toNative: (id) => Effect.sync(() => mapping.subjectIds.toNative(id)),
101
+ toSubject: (id) => Effect.sync(() => mapping.subjectIds.toSubject(id)),
102
+ equals: Object.is,
103
+ },
104
+ },
105
+ batch !== undefined,
106
+ );
107
+
108
+ const provide = <A, E, R>(work: Effect.Effect<A, E, R>) =>
109
+ work.pipe(Effect.provideService(Crypto.Crypto, crypto));
110
+
111
+ const run = <A, E, R>(work: Effect.Effect<A, E, R>) =>
112
+ batch === undefined
113
+ ? executor.run(provide(work))
114
+ : executor.batch(provide(work)).pipe(Effect.provideService(SqlBatchCommit, batch));
115
+
116
+ const stage = Effect.fnUntraced(function* (statement: Fragment, expected = 1) {
117
+ if (batch !== undefined) {
118
+ yield* appendSqlBatchStatement(sql`${statement}`);
119
+ yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = ${expected}`));
120
+ } else invariant((yield* executeSqlChange(sql, statement)) === expected);
121
+ });
122
+
123
+ const capture = Effect.fnUntraced(function* (
124
+ subjectId: TotpRecord["subjectId"],
125
+ locking = false,
126
+ ) {
127
+ const nativeId = mapping.subjectIds.toNative(subjectId);
128
+
129
+ invariant(mapping.subjectIds.toSubject(nativeId) === subjectId);
130
+
131
+ const scope = yield* digest(
132
+ `effect-auth/totp/scope/v1/${mapping.moduleId.length}:${mapping.moduleId}/${subjectId}`,
133
+ );
134
+
135
+ if (locking && nativeLocks) {
136
+ const owners =
137
+ yield* sql`select ${subject.column(s.id)} from ${subject.name} where ${id(subject, s.id, nativeId)} and ${active} limit 2 ${lock}`;
138
+
139
+ invariant(owners.length <= 1);
140
+ if (owners[0] === undefined) return undefined;
141
+ }
142
+ // The distinct subject lock always precedes the ordered authority locks.
143
+ // Advisory snapshots use this same bounded join without transaction control.
144
+ const currentCredentials = sql`select ${credential.fields("c_")} from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${activeCredential} order by ${credential.column(c.id)} limit 65 ${locking && nativeLocks ? lock : sql``}`;
145
+
146
+ const rows =
147
+ yield* sql`with current_credentials as ${sql.onDialectOrElse({ pg: () => sql`materialized`, orElse: () => sql`` })} (${currentCredentials})
148
+ select ${subject.fields("s_")}, ${factor.fields("f_")}, current_credentials.*, ${engineNow} as engine_now
149
+ from ${subject.name} left join ${factor.name} on ${exact(factor, f.scope, scope)} left join current_credentials on 1 = 1
150
+ where ${id(subject, s.id, nativeId)} and ${active} limit 65`;
151
+
152
+ invariant(rows.length <= 64);
153
+ if (rows[0] === undefined) return undefined;
154
+
155
+ const subjectRow = subject.decode(rows[0], "s_"),
156
+ decoded = factor.decode(rows[0], "f_");
157
+
158
+ const factorRow =
159
+ decoded[f.scope] === null || decoded[f.scope] === undefined ? undefined : decoded;
160
+
161
+ const now = Number(rows[0].engine_now);
162
+ const credentials: { credentialId: unknown; revision: unknown }[] = [];
163
+
164
+ for (const row of rows) {
165
+ const value = credential.decode(row, "c_");
166
+
167
+ if (value[c.id] === null || value[c.id] === undefined) continue;
168
+ invariant(mapping.subjectIds.toSubject(value[c.subjectId]) === subjectId);
169
+ invariant(!credentials.some((entry) => entry.credentialId === value[c.id]));
170
+ credentials.push({ credentialId: value[c.id], revision: value[c.revision] });
171
+ }
172
+
173
+ const revision = yield* Schema.decodeUnknownEffect(TotpSnapshot.fields.revision)({
174
+ subjectId,
175
+ securityRevision: subjectRow[s.securityRevision],
176
+ credentials,
177
+ });
178
+
179
+ const ordered = {
180
+ ...revision,
181
+ credentials: [...revision.credentials].sort((left, right) =>
182
+ left.credentialId.localeCompare(right.credentialId),
183
+ ),
184
+ };
185
+
186
+ invariant(
187
+ mapping.subjectIds.toSubject(subjectRow[s.id]) === subjectId && Number.isSafeInteger(now),
188
+ );
189
+
190
+ const record =
191
+ factorRow === undefined ? null : yield* Schema.decodeUnknownEffect(codec)(factorRow[f.state]);
192
+
193
+ invariant(
194
+ record === null ||
195
+ (record.moduleId === mapping.moduleId &&
196
+ record.subjectId === subjectId &&
197
+ record.version === factorRow?.[f.version] &&
198
+ (record.secret === null || record.secret.revision === record.revision) &&
199
+ (record.pending === null || record.pending.secret.revision === record.pending.revision)),
200
+ );
201
+
202
+ invariant(
203
+ record === null ||
204
+ record.secret === null ||
205
+ ordered.credentials.some(
206
+ (entry) =>
207
+ entry.credentialId === record.credentialId && entry.revision === record.revision,
208
+ ),
209
+ );
210
+
211
+ const snapshot = yield* Schema.decodeEffect(Schema.toType(TotpSnapshot))({
212
+ revision: ordered,
213
+ requirement: mapping.subject.decodeRequirement(subjectRow),
214
+ record,
215
+ });
216
+
217
+ return { nativeId, subjectRow, factorRow, scope, snapshot, now };
218
+ });
219
+
220
+ const mutate = Effect.fnUntraced(function* (input: TotpMutation) {
221
+ invariant(
222
+ input.moduleId === mapping.moduleId &&
223
+ Schema.encodeSync(Schema.fromJsonString(TotpPolicy))(input.policy) ===
224
+ Schema.encodeSync(Schema.fromJsonString(TotpPolicy))(mapping.policy),
225
+ );
226
+ const captured = yield* capture(input.subjectId, true);
227
+
228
+ if (captured === undefined) return reject;
229
+
230
+ const { snapshot, subjectRow, factorRow, scope, nativeId, now } = captured,
231
+ current = snapshot.record,
232
+ policy = input.policy,
233
+ action = input.action;
234
+
235
+ if (
236
+ snapshot.revision.securityRevision !== input.snapshot.revision.securityRevision ||
237
+ (current?.version ?? null) !== (input.snapshot.record?.version ?? null) ||
238
+ (current === null) !== (input.snapshot.record === null) ||
239
+ (current !== null && encodeTotpRecord(current) !== encodeTotpRecord(input.snapshot.record!))
240
+ )
241
+ return reject;
242
+
243
+ let expectedCount: number | undefined,
244
+ countGuardIndex = -1;
245
+
246
+ const guards: Fragment[] = [],
247
+ timeGuards: Fragment[] = [],
248
+ credentialGuards: { credentialId: string; condition: Fragment }[] = [];
249
+
250
+ const timeBound = (start: number, end: number) => {
251
+ timeGuards.push(sql`${engineNow} >= ${start} and ${engineNow} < ${end}`);
252
+ };
253
+
254
+ if (
255
+ action._tag === "Enroll" ||
256
+ action._tag === "Confirm" ||
257
+ action._tag === "Disable" ||
258
+ action._tag === "Regenerate"
259
+ ) {
260
+ const authorization = input.authorization;
261
+
262
+ if (authorization === undefined) return reject;
263
+ const { challenge, evidence } = authorization;
264
+
265
+ const expectedAction = {
266
+ Enroll: "enroll",
267
+ Confirm: "confirm",
268
+ Disable: "disable",
269
+ Regenerate: "regenerate",
270
+ }[action._tag];
271
+
272
+ if (
273
+ challenge.action !== expectedAction ||
274
+ challenge.moduleId !== mapping.moduleId ||
275
+ challenge.revision.subjectId !== input.subjectId ||
276
+ challenge.revision.securityRevision !== snapshot.revision.securityRevision ||
277
+ evidence.flowId !== challenge.flowId ||
278
+ evidence.bindingDigest !== challenge.bindingDigest ||
279
+ evidence.revision.subjectId !== input.subjectId ||
280
+ evidence.revision.securityRevision !== snapshot.revision.securityRevision
281
+ )
282
+ return reject;
283
+ for (const revision of evidence.revision.credentials)
284
+ credentialGuards.push({
285
+ credentialId: revision.credentialId,
286
+ condition: sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${exact(credential, c.id, revision.credentialId)} and ${exact(credential, c.revision, revision.revision)} and ${activeCredential})`,
287
+ });
288
+ expectedCount = evidence.revision.credentials.length;
289
+ countGuardIndex = guards.length;
290
+ guards.push(
291
+ sql`(select count(*) from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${activeCredential}) = ${evidence.revision.credentials.length}`,
292
+ );
293
+
294
+ const requirements = [authorization.requirement, snapshot.requirement],
295
+ maximumAge = Math.min(
296
+ policy.maximumEvidenceAgeMillis,
297
+ ...requirements.map((r) => r.maximumAgeMillis),
298
+ );
299
+
300
+ for (const requirement of requirements) {
301
+ const assessed = yield* assessSessionAt(
302
+ evidence,
303
+ { ...requirement, maximumAgeMillis: maximumAge },
304
+ now,
305
+ ).pipe(Effect.result);
306
+
307
+ if (assessed._tag === "Failure" || !assessed.success.satisfied) return reject;
308
+ }
309
+ for (const proof of evidence.proofs) {
310
+ const verified = DateTime.toEpochMillis(proof.verifiedAt);
311
+
312
+ if (verified > now || now - verified >= maximumAge) return reject;
313
+ timeBound(verified, verified + maximumAge);
314
+ }
315
+ if (
316
+ (current !== null && current.secret !== null) ||
317
+ action._tag === "Disable" ||
318
+ action._tag === "Regenerate"
319
+ ) {
320
+ const factors = new Set(evidence.proofs.flatMap((p) => p.factors));
321
+
322
+ if (
323
+ !(
324
+ evidence.proofs.some(
325
+ (p) => p.userVerified && p.phishingResistant && p.factors.includes("possession"),
326
+ ) ||
327
+ (factors.has("knowledge") &&
328
+ factors.has("possession") &&
329
+ new Set(evidence.proofs.map((p) => p.credentialId)).size >= 2)
330
+ )
331
+ )
332
+ return reject;
333
+ }
334
+ }
335
+ if (action._tag === "Recovery" && action.reset) {
336
+ const target = action.pending;
337
+
338
+ if (
339
+ target === undefined ||
340
+ pending === undefined ||
341
+ mapping.pending === undefined ||
342
+ target.revision.subjectId !== input.subjectId ||
343
+ target.revision.securityRevision !== snapshot.revision.securityRevision ||
344
+ target.expiresAtMillis <= now
345
+ )
346
+ return reject;
347
+ const selected = yield* pending.read("Login", target.digest);
348
+
349
+ if (selected === undefined) return reject;
350
+ const original = yield* mapping.pending.login.decode(selected.record.snapshot);
351
+
352
+ if (
353
+ selected.record.flowId !== target.flowId ||
354
+ selected.record.bindingDigest !== target.bindingDigest ||
355
+ selected.record.subjectId !== input.subjectId ||
356
+ selected.record.expiresAtMillis !== target.expiresAtMillis ||
357
+ original.digest !== target.digest ||
358
+ !sameSessionRevision(original.evidence.revision, target.revision)
359
+ )
360
+ return reject;
361
+ guards.push(
362
+ sql`exists(select 1 from ${pending.table.name} where ${pending.predicate(pending.table, "Login", target.digest)} and ${exact(pending.table, pending.p.snapshot, selected.record.snapshot)} and ${exact(pending.table, pending.p.version, original.version)})`,
363
+ );
364
+ for (const revision of target.revision.credentials)
365
+ credentialGuards.push({
366
+ credentialId: revision.credentialId,
367
+ condition: sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${exact(credential, c.id, revision.credentialId)} and ${exact(credential, c.revision, revision.revision)} and ${activeCredential})`,
368
+ });
369
+ timeBound(0, target.expiresAtMillis);
370
+ }
371
+
372
+ let next: TotpRecord,
373
+ semantic = false,
374
+ accepted = true;
375
+
376
+ if (action._tag === "Enroll") {
377
+ const candidate = action.record;
378
+
379
+ if (
380
+ candidate.subjectId !== input.subjectId ||
381
+ candidate.moduleId !== input.moduleId ||
382
+ candidate.pending === null ||
383
+ candidate.pending.expiresAtMillis <= now ||
384
+ candidate.pending.expiresAtMillis > now + policy.enrollmentLifetimeMillis ||
385
+ candidate.pending.failedAttempts !== 0 ||
386
+ candidate.pending.secret.revision !== candidate.pending.revision
387
+ )
388
+ return reject;
389
+ // Reissuing cannot erase the confirmation budget during its current enrollment lifetime.
390
+ if (current !== null && current.pending !== null && current.pending.expiresAtMillis > now)
391
+ return reject;
392
+ if (
393
+ current !== null &&
394
+ (candidate.credentialId !== current.credentialId ||
395
+ encodeTotpRecord({ ...candidate, pending: current.pending }) !==
396
+ encodeTotpRecord(current))
397
+ )
398
+ return reject;
399
+ if (
400
+ current === null &&
401
+ (candidate.secret !== null ||
402
+ candidate.recoveryDigests.length !== 0 ||
403
+ candidate.acceptedStep !== -1 ||
404
+ candidate.failedAttempts !== 0)
405
+ )
406
+ return reject;
407
+ next = candidate;
408
+ timeBound(0, candidate.pending.expiresAtMillis);
409
+ } else {
410
+ if (current === null) return reject;
411
+ next = { ...current };
412
+ if (action._tag === "Confirm") {
413
+ const pending = current.pending;
414
+
415
+ if (
416
+ pending === null ||
417
+ pending.enrollmentId !== action.enrollmentId ||
418
+ pending.expiresAtMillis <= now ||
419
+ pending.failedAttempts >= policy.attemptLimit
420
+ )
421
+ return reject;
422
+ timeBound(0, pending.expiresAtMillis);
423
+ if (
424
+ action.matchedStep === null ||
425
+ Math.abs(Math.floor(now / 30000) - action.matchedStep) > policy.clockSkewSteps
426
+ ) {
427
+ next = {
428
+ ...current,
429
+ pending: { ...pending, failedAttempts: pending.failedAttempts + 1 },
430
+ };
431
+ accepted = false;
432
+ } else {
433
+ if (new Set(action.recoveryDigests).size !== 10) return reject;
434
+ next = {
435
+ ...current,
436
+ secret: pending.secret,
437
+ revision: pending.secret.revision,
438
+ pending: null,
439
+ recoveryDigests: action.recoveryDigests,
440
+ acceptedStep: action.matchedStep,
441
+ attemptWindow: now,
442
+ failedAttempts: 0,
443
+ };
444
+ semantic = true;
445
+ timeBound(
446
+ Math.max(0, (action.matchedStep - policy.clockSkewSteps) * 30000),
447
+ (action.matchedStep + policy.clockSkewSteps + 1) * 30000,
448
+ );
449
+ }
450
+ } else if (action._tag === "Verify" || action._tag === "Recovery") {
451
+ if (current.secret === null) return reject;
452
+
453
+ const expired = now >= current.attemptWindow + policy.attemptWindowMillis,
454
+ attempts = expired ? 0 : current.failedAttempts,
455
+ start = expired ? now : current.attemptWindow;
456
+
457
+ if (attempts >= policy.attemptLimit) return reject;
458
+ next = { ...current, attemptWindow: start, failedAttempts: attempts + 1 };
459
+ if (action._tag === "Verify") {
460
+ if (
461
+ action.matchedStep === null ||
462
+ action.matchedStep <= current.acceptedStep ||
463
+ Math.abs(Math.floor(now / 30000) - action.matchedStep) > policy.clockSkewSteps
464
+ )
465
+ accepted = false;
466
+ else {
467
+ next = { ...next, acceptedStep: action.matchedStep, failedAttempts: 0 };
468
+ timeBound(
469
+ Math.max(0, (action.matchedStep - policy.clockSkewSteps) * 30000),
470
+ (action.matchedStep + policy.clockSkewSteps + 1) * 30000,
471
+ );
472
+ }
473
+ } else {
474
+ if (
475
+ (action.reset
476
+ ? policy.lostFactorRecovery !== "reset-with-recovery-code"
477
+ : !policy.allowRecoveryCodeForPending) ||
478
+ !current.recoveryDigests.includes(action.digest)
479
+ )
480
+ accepted = false;
481
+ else {
482
+ next = {
483
+ ...next,
484
+ recoveryDigests: current.recoveryDigests.filter((value) => value !== action.digest),
485
+ failedAttempts: 0,
486
+ };
487
+ if (action.reset) {
488
+ next = {
489
+ ...next,
490
+ secret: null,
491
+ pending: null,
492
+ recoveryDigests: [],
493
+ revision: yield* randomId,
494
+ };
495
+ semantic = true;
496
+ }
497
+ }
498
+ }
499
+ } else {
500
+ if (current.secret === null) return reject;
501
+ semantic = action._tag === "Disable";
502
+ if (action._tag === "Disable")
503
+ next = {
504
+ ...current,
505
+ secret: null,
506
+ pending: null,
507
+ recoveryDigests: [],
508
+ revision: yield* randomId,
509
+ };
510
+ else {
511
+ if (new Set(action.recoveryDigests).size !== 10) return reject;
512
+ next = { ...current, recoveryDigests: action.recoveryDigests };
513
+ }
514
+ }
515
+ }
516
+ next = { ...next, version: yield* randomId };
517
+
518
+ const ownerKey = id(subject, s.id, nativeId);
519
+ const initialAuthority = sql`exists(select 1 from ${subject.name} where ${ownerKey} and ${exact(subject, s.securityRevision, snapshot.revision.securityRevision)} and ${active})`;
520
+
521
+ const initialFactor =
522
+ factorRow === undefined
523
+ ? sql`not exists(select 1 from ${factor.name} where ${exact(factor, f.scope, scope)})`
524
+ : sql`exists(select 1 from ${factor.name} where ${exact(factor, f.scope, scope)} and ${exact(factor, f.version, current!.version)})`;
525
+
526
+ const policyGuard = sql.and(
527
+ (s.requirementColumns ?? []).map((key) =>
528
+ subjectRow[key] === null
529
+ ? sql`${subject.column(key)} is null`
530
+ : sql`${subject.column(key)} = ${subject.value(key, subjectRow[key])}`,
531
+ ),
532
+ );
533
+
534
+ const policyCondition = sql`exists(select 1 from ${subject.name} where ${ownerKey} and ${policyGuard})`;
535
+ const fresh = sql.and([...timeGuards, sql`${engineNow} >= ${now}`]);
536
+
537
+ // A factor UPDATE pins scope and version itself, and MySQL rejects a subquery
538
+ // on the table being updated (error 1093), so only inserts add initialFactor.
539
+ const unchangedFactor = sql.and([
540
+ initialAuthority,
541
+ policyCondition,
542
+ fresh,
543
+ ...guards,
544
+ ...credentialGuards.map((g) => g.condition),
545
+ ]);
546
+
547
+ let nextRevision = snapshot.revision.securityRevision;
548
+
549
+ if (semantic) {
550
+ nextRevision = SecurityRevision.make(yield* randomId);
551
+ // Subject is the first lock and first graph mutation. The conditional write
552
+ // validates original evidence before changing the TOTP authority vector.
553
+ yield* stage(
554
+ sql`${subject.update({ [s.securityRevision]: nextRevision, [s.factorEnabled]: s.encodeEnabled(next.secret !== null) })} where ${ownerKey} and ${exact(subject, s.securityRevision, snapshot.revision.securityRevision)} and ${active} and ${fresh} and ${initialFactor} and ${policyGuard} and ${sql.and([...guards, ...credentialGuards.map((g) => g.condition)])}`,
555
+ );
556
+
557
+ const values = {
558
+ ...c.encodeInsert({
559
+ credentialId: next.credentialId,
560
+ subjectId: nativeId,
561
+ revision: next.revision,
562
+ active: next.secret !== null,
563
+ }),
564
+ [c.id]: next.credentialId,
565
+ [c.subjectId]: nativeId,
566
+ [c.revision]: next.revision,
567
+ [c.status]: c.encodeStatus(next.secret !== null),
568
+ };
569
+
570
+ const insert = credential.insert(values);
571
+
572
+ const changes = {
573
+ [c.revision]: next.revision,
574
+ [c.status]: c.encodeStatus(next.secret !== null),
575
+ };
576
+
577
+ const upsert = sql.onDialectOrElse({
578
+ mysql: () =>
579
+ sql`${insert} on duplicate key update ${credential.columnName(c.revision)} = case when ${id(credential, c.subjectId, nativeId)} then ${credential.value(c.revision, next.revision)} else ${credential.column(c.revision)} end, ${credential.columnName(c.status)} = case when ${id(credential, c.subjectId, nativeId)} then ${credential.value(c.status, c.encodeStatus(next.secret !== null))} else ${credential.column(c.status)} end`,
580
+ orElse: () =>
581
+ sql`${insert} on conflict (${credential.columnName(c.id)}) do update set ${sql.join(", ", false)(Object.entries(changes).map(([key, value]) => sql`${credential.columnName(key)} = ${credential.value(key, value)}`))} where ${id(credential, c.subjectId, nativeId)}`,
582
+ });
583
+
584
+ if (sql.onDialectOrElse({ mysql: () => true, orElse: () => false }) && batch === undefined) {
585
+ yield* sql`${upsert}`;
586
+ invariant(
587
+ (yield* sql`select 1 from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${exact(credential, c.id, next.credentialId)} and ${exact(credential, c.revision, next.revision)} and ${id(credential, c.status, c.encodeStatus(next.secret !== null))}`)
588
+ .length === 1,
589
+ );
590
+ } else yield* stage(upsert);
591
+ }
592
+
593
+ const encoded = encodeTotpRecord(next),
594
+ values = { [f.state]: encoded, [f.version]: next.version };
595
+
596
+ const afterAuthority = sql`exists(select 1 from ${subject.name} where ${ownerKey} and ${exact(subject, s.securityRevision, nextRevision)} and ${active})`;
597
+ const writeGuard = semantic ? sql`${afterAuthority} and ${fresh}` : unchangedFactor;
598
+
599
+ if (factorRow === undefined) {
600
+ const insert = {
601
+ ...f.encodeInsert({ scope, state: encoded, version: next.version }),
602
+ [f.scope]: scope,
603
+ ...values,
604
+ };
605
+
606
+ yield* stage(
607
+ conditionalInsert(
608
+ factor,
609
+ insert,
610
+ semantic ? writeGuard : sql.and([writeGuard, initialFactor]),
611
+ ),
612
+ );
613
+ } else
614
+ yield* stage(
615
+ sql`${factor.update(values)} where ${exact(factor, f.scope, scope)} and ${exact(factor, f.version, current!.version)} and ${writeGuard}`,
616
+ );
617
+
618
+ const expectedPolicy = semantic
619
+ ? { ...subjectRow, [s.factorEnabled]: s.encodeEnabled(next.secret !== null) }
620
+ : subjectRow;
621
+
622
+ const finalPolicy = sql`exists(select 1 from ${subject.name} where ${ownerKey} and ${sql.and((s.requirementColumns ?? []).map((key) => (expectedPolicy[key] === null ? sql`${subject.column(key)} is null` : sql`${subject.column(key)} = ${subject.value(key, expectedPolicy[key])}`)))})`;
623
+
624
+ const finalCondition = sql.and([
625
+ afterAuthority,
626
+ finalPolicy,
627
+ fresh,
628
+ sql`exists(select 1 from ${factor.name} where ${exact(factor, f.scope, scope)} and ${exact(factor, f.version, next.version)} and ${exact(factor, f.state, encoded)})`,
629
+ ...guards.filter((_, index) => expectedCount === undefined || index !== countGuardIndex),
630
+ ...credentialGuards
631
+ .filter((g) => !semantic || g.credentialId !== next.credentialId)
632
+ .map((g) => g.condition),
633
+ ...(semantic
634
+ ? [
635
+ sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${exact(credential, c.id, next.credentialId)} and ${exact(credential, c.revision, next.revision)} and ${id(credential, c.status, c.encodeStatus(next.secret !== null))})`,
636
+ ]
637
+ : []),
638
+ ...(expectedCount === undefined
639
+ ? []
640
+ : [
641
+ sql`(select count(*) from ${credential.name} where ${id(credential, c.subjectId, nativeId)} and ${activeCredential}) = ${expectedCount + (semantic ? (next.secret === null ? 0 : 1) - (current?.secret === null || current === null ? 0 : 1) : 0)}`,
642
+ ]),
643
+ ]);
644
+
645
+ if (batch !== undefined)
646
+ yield* registerSqlBatchPostcondition({
647
+ name: "totp-factor-and-authority",
648
+ statement: sqlBatchAssertion(sql, finalCondition),
649
+ });
650
+ else if (Option.isSome(external))
651
+ yield* registerSqlPostcondition({
652
+ name: "totp-factor-and-authority",
653
+ check: Effect.gen(function* () {
654
+ invariant((yield* sql`select 1 where ${finalCondition}`).length === 1);
655
+ if (input.authorization !== undefined) {
656
+ const final = yield* capture(input.subjectId);
657
+
658
+ invariant(final !== undefined && final.now >= now);
659
+ const expected = mapping.subject.decodeRequirement(expectedPolicy);
660
+
661
+ const encodedRequirement = Schema.encodeSync(
662
+ Schema.fromJsonString(AuthenticationRequirement),
663
+ );
664
+
665
+ invariant(
666
+ encodedRequirement(final.snapshot.requirement) === encodedRequirement(expected),
667
+ );
668
+ }
669
+ }).pipe(
670
+ Effect.provideService(Crypto.Crypto, crypto),
671
+ Effect.mapError((cause) => PersistenceMappingError.make({ operation: "decode", cause })),
672
+ ),
673
+ });
674
+
675
+ return accepted ? { _tag: "Accepted" as const, record: next } : reject;
676
+ });
677
+
678
+ const totpPersistence: TotpPersistence["Service"] = {
679
+ snapshot: (input) =>
680
+ executor.read(
681
+ provide(
682
+ Effect.gen(function* () {
683
+ invariant(input.moduleId === mapping.moduleId);
684
+
685
+ return (yield* capture(input.subjectId))?.snapshot;
686
+ }),
687
+ ),
688
+ ),
689
+ mutate: (original, project) =>
690
+ run(
691
+ Effect.gen(function* () {
692
+ const input = yield* Schema.decodeEffect(Schema.toType(TotpMutation))(original);
693
+ const decision = yield* mutate(input);
694
+
695
+ return yield* prepareNativeSession(decision, project);
696
+ }),
697
+ ),
698
+ };
699
+
700
+ return { totpPersistence };
701
+ });