@yielded/auth-persistence 0.1.0-beta.10

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 (182) hide show
  1. package/README.md +27 -0
  2. package/dist/Adapter.d.mts +30 -0
  3. package/dist/Adapter.mjs +25 -0
  4. package/dist/index.d.mts +7 -0
  5. package/dist/index.mjs +7 -0
  6. package/dist/internal/PasswordPreparedPostconditions.d.mts +22 -0
  7. package/dist/internal/PasswordPreparedPostconditions.mjs +12 -0
  8. package/dist/internal/PasswordPreparedPostconditions.mjs.map +1 -0
  9. package/dist/internal/configuration.d.mts +174 -0
  10. package/dist/internal/configuration.mjs +7 -0
  11. package/dist/internal/configuration.mjs.map +1 -0
  12. package/dist/internal/email-kernel.d.mts +275 -0
  13. package/dist/internal/email-kernel.mjs +487 -0
  14. package/dist/internal/email-kernel.mjs.map +1 -0
  15. package/dist/internal/mapping-error.d.mts +10 -0
  16. package/dist/internal/mapping-error.mjs +30 -0
  17. package/dist/internal/mapping-error.mjs.map +1 -0
  18. package/dist/internal/models/common.d.mts +12 -0
  19. package/dist/internal/models/email-model.d.mts +353 -0
  20. package/dist/internal/models/email-model.mjs +21 -0
  21. package/dist/internal/models/email-model.mjs.map +1 -0
  22. package/dist/internal/models/passkey-model.d.mts +266 -0
  23. package/dist/internal/models/passkey-model.mjs +34 -0
  24. package/dist/internal/models/passkey-model.mjs.map +1 -0
  25. package/dist/internal/models/passkey-registration-ceremony-model.d.mts +49 -0
  26. package/dist/internal/models/passkey-registration-ceremony-model.mjs +18 -0
  27. package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +1 -0
  28. package/dist/internal/models/passkey-write-model.d.mts +189 -0
  29. package/dist/internal/models/passkey-write-model.mjs +13 -0
  30. package/dist/internal/models/passkey-write-model.mjs.map +1 -0
  31. package/dist/internal/models/password-model.d.mts +328 -0
  32. package/dist/internal/models/password-model.mjs +23 -0
  33. package/dist/internal/models/password-model.mjs.map +1 -0
  34. package/dist/internal/models/proof-model.d.mts +285 -0
  35. package/dist/internal/models/proof-model.mjs +17 -0
  36. package/dist/internal/models/proof-model.mjs.map +1 -0
  37. package/dist/internal/models/session-model.d.mts +199 -0
  38. package/dist/internal/models/session-model.mjs +20 -0
  39. package/dist/internal/models/session-model.mjs.map +1 -0
  40. package/dist/internal/models/step-up-model.d.mts +66 -0
  41. package/dist/internal/models/step-up-model.mjs +9 -0
  42. package/dist/internal/models/step-up-model.mjs.map +1 -0
  43. package/dist/internal/oauth-app.d.mts +11 -0
  44. package/dist/internal/oauth-app.mjs +57 -0
  45. package/dist/internal/oauth-app.mjs.map +1 -0
  46. package/dist/internal/oauth-server.d.mts +10 -0
  47. package/dist/internal/oauth-server.mjs +66 -0
  48. package/dist/internal/oauth-server.mjs.map +1 -0
  49. package/dist/internal/passkey/admission.d.mts +6 -0
  50. package/dist/internal/passkey/admission.mjs +216 -0
  51. package/dist/internal/passkey/admission.mjs.map +1 -0
  52. package/dist/internal/passkey/credentials.d.mts +14 -0
  53. package/dist/internal/passkey/credentials.mjs +249 -0
  54. package/dist/internal/passkey/credentials.mjs.map +1 -0
  55. package/dist/internal/passkey/enrollment.mjs +138 -0
  56. package/dist/internal/passkey/enrollment.mjs.map +1 -0
  57. package/dist/internal/passkey/flow.d.mts +21 -0
  58. package/dist/internal/passkey/flow.mjs +452 -0
  59. package/dist/internal/passkey/flow.mjs.map +1 -0
  60. package/dist/internal/passkey/management.mjs +231 -0
  61. package/dist/internal/passkey/management.mjs.map +1 -0
  62. package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
  63. package/dist/internal/passkey/registration-ceremony.mjs +129 -0
  64. package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
  65. package/dist/internal/passkey/registration-custody.d.mts +2 -0
  66. package/dist/internal/passkey/registration-custody.mjs +52 -0
  67. package/dist/internal/passkey/registration-custody.mjs.map +1 -0
  68. package/dist/internal/passkey/registration-write.mjs +211 -0
  69. package/dist/internal/passkey/registration-write.mjs.map +1 -0
  70. package/dist/internal/passkey/state.d.mts +10 -0
  71. package/dist/internal/passkey/state.mjs +234 -0
  72. package/dist/internal/passkey/state.mjs.map +1 -0
  73. package/dist/internal/passkey/target.d.mts +19 -0
  74. package/dist/internal/passkey/target.mjs +190 -0
  75. package/dist/internal/passkey/target.mjs.map +1 -0
  76. package/dist/internal/passkey/write-state.d.mts +17 -0
  77. package/dist/internal/passkey/write-state.mjs +345 -0
  78. package/dist/internal/passkey/write-state.mjs.map +1 -0
  79. package/dist/internal/passkey/write-target.mjs +192 -0
  80. package/dist/internal/passkey/write-target.mjs.map +1 -0
  81. package/dist/internal/passkey-kernel.d.mts +5592 -0
  82. package/dist/internal/passkey-kernel.mjs +49 -0
  83. package/dist/internal/passkey-kernel.mjs.map +1 -0
  84. package/dist/internal/passkeys.d.mts +7 -0
  85. package/dist/internal/passkeys.mjs +368 -0
  86. package/dist/internal/passkeys.mjs.map +1 -0
  87. package/dist/internal/password-kernel.d.mts +290 -0
  88. package/dist/internal/password-kernel.mjs +704 -0
  89. package/dist/internal/password-kernel.mjs.map +1 -0
  90. package/dist/internal/persistence.d.mts +25 -0
  91. package/dist/internal/persistence.mjs +291 -0
  92. package/dist/internal/persistence.mjs.map +1 -0
  93. package/dist/internal/phone-kernel.d.mts +162 -0
  94. package/dist/internal/phone-kernel.mjs +434 -0
  95. package/dist/internal/phone-kernel.mjs.map +1 -0
  96. package/dist/internal/proof-kernel.d.mts +93 -0
  97. package/dist/internal/proof-kernel.mjs +619 -0
  98. package/dist/internal/proof-kernel.mjs.map +1 -0
  99. package/dist/internal/query-operations.d.mts +61 -0
  100. package/dist/internal/registration-contract.d.mts +16 -0
  101. package/dist/internal/registration.mjs +66 -0
  102. package/dist/internal/registration.mjs.map +1 -0
  103. package/dist/internal/session-kernel.d.mts +48 -0
  104. package/dist/internal/session-kernel.mjs +737 -0
  105. package/dist/internal/session-kernel.mjs.map +1 -0
  106. package/dist/internal/sql-persistence.d.mts +10 -0
  107. package/dist/internal/sql-persistence.mjs +37 -0
  108. package/dist/internal/sql-persistence.mjs.map +1 -0
  109. package/dist/internal/sql-query.mjs +207 -0
  110. package/dist/internal/sql-query.mjs.map +1 -0
  111. package/dist/internal/sql-table.d.mts +36 -0
  112. package/dist/internal/sql-table.mjs +81 -0
  113. package/dist/internal/sql-table.mjs.map +1 -0
  114. package/dist/internal/standalone.d.mts +9 -0
  115. package/dist/internal/standalone.mjs +13 -0
  116. package/dist/internal/standalone.mjs.map +1 -0
  117. package/dist/internal/step-up-state.d.mts +49 -0
  118. package/dist/internal/step-up-state.mjs +32 -0
  119. package/dist/internal/step-up-state.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +686 -0
  121. package/dist/internal/storage-mapping.mjs.map +1 -0
  122. package/dist/internal/storage-tables.d.mts +913 -0
  123. package/dist/internal/storage-tables.mjs +387 -0
  124. package/dist/internal/storage-tables.mjs.map +1 -0
  125. package/dist/internal/storage-validation.mjs +58 -0
  126. package/dist/internal/storage-validation.mjs.map +1 -0
  127. package/dist/internal/transaction-execution-kernel.d.mts +38 -0
  128. package/dist/internal/transaction-execution-kernel.mjs +116 -0
  129. package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
  130. package/dist/internal/transaction-kernel.d.mts +77 -0
  131. package/dist/internal/transaction-kernel.mjs +260 -0
  132. package/dist/internal/transaction-kernel.mjs.map +1 -0
  133. package/package.json +1 -0
  134. package/src/Adapter.ts +291 -0
  135. package/src/index.ts +6 -0
  136. package/src/internal/PasswordPreparedPostconditions.ts +30 -0
  137. package/src/internal/configuration.ts +282 -0
  138. package/src/internal/email-kernel.ts +1535 -0
  139. package/src/internal/mapping-error.ts +36 -0
  140. package/src/internal/models/common.ts +11 -0
  141. package/src/internal/models/email-model.ts +559 -0
  142. package/src/internal/models/passkey-model.ts +390 -0
  143. package/src/internal/models/passkey-registration-ceremony-model.ts +72 -0
  144. package/src/internal/models/passkey-write-model.ts +297 -0
  145. package/src/internal/models/password-model.ts +513 -0
  146. package/src/internal/models/proof-model.ts +424 -0
  147. package/src/internal/models/session-model.ts +375 -0
  148. package/src/internal/models/step-up-model.ts +135 -0
  149. package/src/internal/oauth-app.ts +82 -0
  150. package/src/internal/oauth-server.ts +89 -0
  151. package/src/internal/passkey/admission.ts +361 -0
  152. package/src/internal/passkey/credentials.ts +511 -0
  153. package/src/internal/passkey/enrollment.ts +326 -0
  154. package/src/internal/passkey/flow.ts +1062 -0
  155. package/src/internal/passkey/management.ts +421 -0
  156. package/src/internal/passkey/registration-ceremony.ts +283 -0
  157. package/src/internal/passkey/registration-custody.ts +91 -0
  158. package/src/internal/passkey/registration-write.ts +426 -0
  159. package/src/internal/passkey/state.ts +353 -0
  160. package/src/internal/passkey/target.ts +586 -0
  161. package/src/internal/passkey/write-state.ts +681 -0
  162. package/src/internal/passkey/write-target.ts +452 -0
  163. package/src/internal/passkey-kernel.ts +104 -0
  164. package/src/internal/passkeys.ts +606 -0
  165. package/src/internal/password-kernel.ts +2124 -0
  166. package/src/internal/persistence.ts +588 -0
  167. package/src/internal/phone-kernel.ts +846 -0
  168. package/src/internal/proof-kernel.ts +1954 -0
  169. package/src/internal/query-operations.ts +81 -0
  170. package/src/internal/registration-contract.ts +22 -0
  171. package/src/internal/registration.ts +161 -0
  172. package/src/internal/session-kernel.ts +2217 -0
  173. package/src/internal/sql-persistence.ts +45 -0
  174. package/src/internal/sql-query.ts +298 -0
  175. package/src/internal/sql-table.ts +104 -0
  176. package/src/internal/standalone.ts +30 -0
  177. package/src/internal/step-up-state.ts +139 -0
  178. package/src/internal/storage-mapping.ts +708 -0
  179. package/src/internal/storage-tables.ts +408 -0
  180. package/src/internal/storage-validation.ts +117 -0
  181. package/src/internal/transaction-execution-kernel.ts +313 -0
  182. package/src/internal/transaction-kernel.ts +593 -0
@@ -0,0 +1,211 @@
1
+ import { CurrentPasskeyTransaction } from "./state.mjs";
2
+ import { Effect, Schema } from "effect";
3
+ import { PasskeyLabel, PasskeyRevision } from "@yielded/auth/Passkey";
4
+ import { SubjectId } from "@yielded/auth/Schema";
5
+ //#region src/internal/passkey/registration-write.ts
6
+ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials, flow, registrationCustody, state, writeState, transactions) => {
7
+ const { sql } = operations;
8
+ const { lockAdmission, readCharges, guardChargeSet } = admission;
9
+ const { readModule, readPolicyGuards } = credentials;
10
+ const { compatiblePolicy, exactClaim, issueAssertion, liveCondition, readFlow, terminalFlow } = flow;
11
+ const { releaseRegistrationCustody } = registrationCustody;
12
+ const { ceremonyStorage, credentialKey, equal, handleKey } = state;
13
+ const invariant = state.invariant;
14
+ const { digest, insertCredential, jsonStorage, validRegistration } = writeState;
15
+ const { both } = transactions;
16
+ const registrationData = (mapping, registration) => {
17
+ const codec = jsonStorage(mapping.registration.schema);
18
+ const encoded = codec.encode(registration);
19
+ invariant(new TextEncoder().encode(encoded).length <= 1048576);
20
+ return {
21
+ encoded,
22
+ registration: codec.decode(encoded),
23
+ fingerprint: digest(mapping.registration.schema, registration)
24
+ };
25
+ };
26
+ const describe = (mapping, registration) => {
27
+ const data = registrationData(mapping, registration);
28
+ const labels = Schema.decodeSync(Schema.Struct({
29
+ name: PasskeyLabel,
30
+ displayName: PasskeyLabel
31
+ }))(mapping.registration.describe(data.registration));
32
+ return {
33
+ ...data,
34
+ ...labels
35
+ };
36
+ };
37
+ return {
38
+ inspectRegistration: Effect.fn("passkey.inspectRegistration")(function* (mapping, registration) {
39
+ const owner = yield* CurrentPasskeyTransaction;
40
+ const value = describe(mapping, registration);
41
+ const current = yield* readModule(mapping);
42
+ yield* readPolicyGuards(mapping);
43
+ const eligible = current !== void 0 && (yield* owner.check(mapping.registration.eligible(value.registration)));
44
+ return {
45
+ fingerprint: value.fingerprint,
46
+ name: value.name,
47
+ displayName: value.displayName,
48
+ eligible
49
+ };
50
+ }),
51
+ issueRegistration: Effect.fn("passkey.issueRegistration")(function* (mapping, input) {
52
+ const owner = yield* CurrentPasskeyTransaction;
53
+ const ceremony = input.ceremony;
54
+ if (ceremony.moduleId !== mapping.moduleId || ceremony.purpose !== "registration" || ceremony.context._tag !== "Registration" || !ceremony.profile.primarySignIn || ceremony.profile.userVerification !== "required" || ceremony.profile.residentKey !== "required" || ceremony.allowedCredentials.length !== 0) return { _tag: "Rejected" };
55
+ const value = describe(mapping, input.registration);
56
+ if (value.fingerprint !== ceremony.context.fingerprint || value.name !== ceremony.context.name || value.displayName !== ceremony.context.displayName) return { _tag: "Rejected" };
57
+ if ((yield* readModule(mapping)) === void 0) return { _tag: "Rejected" };
58
+ yield* lockAdmission(mapping);
59
+ yield* readPolicyGuards(mapping);
60
+ const eligible = mapping.registration.eligible(value.registration);
61
+ if (!(yield* owner.check(eligible))) return { _tag: "Rejected" };
62
+ const handle = mapping.handle;
63
+ const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
64
+ const absentHandle = yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: hashed }), { limit: 1 });
65
+ const intent = mapping.intent;
66
+ const absentCommand = yield* owner.read(intent.table, equal(intent.table, {
67
+ [intent.moduleId]: mapping.moduleId,
68
+ [intent.commandId]: ceremony.commandId
69
+ }), { limit: 1 });
70
+ const absentFlow = yield* owner.read(intent.table, equal(intent.table, {
71
+ [intent.moduleId]: mapping.moduleId,
72
+ [intent.flowId]: ceremony.flowId
73
+ }), { limit: 1 });
74
+ if (absentHandle.rows.length !== 0 || absentCommand.rows.length !== 0 || absentFlow.rows.length !== 0) return { _tag: "Rejected" };
75
+ const issued = yield* issueAssertion(mapping, ceremony, input.policy);
76
+ if (issued._tag !== "Issued") return issued;
77
+ absentHandle.rows = (yield* owner.insert(handle.table, {
78
+ ...handle.encodeInsert({
79
+ ceremony,
80
+ reservationId: owner.marker
81
+ }),
82
+ [handle.handleKey]: hashed,
83
+ [handle.rpId]: ceremony.profile.rpId,
84
+ [handle.userHandle]: ceremony.context.userHandle,
85
+ [handle.state]: handle.reservedState,
86
+ [handle.version]: owner.marker,
87
+ [handle.reservationId]: owner.marker,
88
+ [mapping.read.handleOwnership.subjectId]: null
89
+ }, { [handle.handleKey]: hashed })).rows;
90
+ const inserted = yield* owner.insert(intent.table, {
91
+ ...intent.encodeInsert({
92
+ ceremony,
93
+ registration: value.registration,
94
+ reservationId: owner.marker
95
+ }),
96
+ [intent.moduleId]: mapping.moduleId,
97
+ [intent.flowId]: ceremony.flowId,
98
+ [intent.commandId]: ceremony.commandId,
99
+ [intent.state]: intent.pendingState,
100
+ [intent.version]: owner.marker,
101
+ [intent.fingerprint]: value.fingerprint,
102
+ [intent.handleKey]: hashed,
103
+ [intent.reservationId]: owner.marker,
104
+ [intent.ceremonySnapshot]: ceremonyStorage.encode(ceremony),
105
+ [intent.applicationSnapshot]: value.encoded
106
+ }, {
107
+ [intent.moduleId]: mapping.moduleId,
108
+ [intent.flowId]: ceremony.flowId
109
+ });
110
+ absentCommand.rows = inserted.rows;
111
+ absentFlow.rows = inserted.rows;
112
+ owner.postconditions.push(eligible);
113
+ return issued;
114
+ }),
115
+ completeRegistration: Effect.fn("passkey.completeRegistration")(function* (mapping, input) {
116
+ const owner = yield* CurrentPasskeyTransaction;
117
+ const ceremony = input.claim.ceremony;
118
+ if (ceremony.moduleId !== mapping.moduleId || ceremony.purpose !== "registration" || ceremony.context._tag !== "Registration") return { _tag: "Rejected" };
119
+ const current = yield* readModule(mapping);
120
+ if (current === void 0) return { _tag: "Rejected" };
121
+ yield* lockAdmission(mapping);
122
+ yield* readPolicyGuards(mapping);
123
+ const tuple = mapping.read.credentialOwnership;
124
+ const absentCredential = yield* owner.read(tuple.table, equal(tuple.table, { [tuple.credentialKey]: credentialKey(ceremony.profile.rpId, input.verified.protocolCredentialId) }), {
125
+ limit: 1,
126
+ observe: false
127
+ });
128
+ const handle = mapping.handle;
129
+ const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
130
+ const held = (yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: hashed }), { limit: 1 })).rows[0];
131
+ const intent = mapping.intent;
132
+ const row = (yield* owner.read(intent.table, equal(intent.table, {
133
+ [intent.moduleId]: mapping.moduleId,
134
+ [intent.flowId]: ceremony.flowId
135
+ }), { limit: 1 })).rows[0];
136
+ const read = yield* readFlow(mapping, ceremony.flowId);
137
+ if (read === void 0 || !exactClaim(read, input.claim)) return { _tag: "Rejected" };
138
+ const reject = Effect.gen(function* () {
139
+ yield* releaseRegistrationCustody(mapping, ceremony);
140
+ yield* terminalFlow(mapping, read, "Rejected");
141
+ return { _tag: "Rejected" };
142
+ });
143
+ if (absentCredential.rows.length !== 0 || held === void 0 || row === void 0 || !handle.isReservedState(held[handle.state]) || !intent.isPendingState(row[intent.state]) || row[intent.commandId] !== ceremony.commandId || row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) || row[intent.fingerprint] !== ceremony.context.fingerprint || row[intent.handleKey] !== hashed || held[handle.reservationId] !== row[intent.reservationId] || held[handle.rpId] !== ceremony.profile.rpId || held[handle.userHandle] !== ceremony.context.userHandle || !compatiblePolicy(ceremony, read.policy, current) || !validRegistration(ceremony, input.verified) || !input.verified.userVerified || !ceremony.profile.primarySignIn || !(yield* owner.check(liveCondition(mapping, ceremony, input.claim)))) return yield* reject;
144
+ const data = describe(mapping, jsonStorage(mapping.registration.schema).decode(row[intent.applicationSnapshot]));
145
+ if (data.encoded !== row[intent.applicationSnapshot] || data.fingerprint !== ceremony.context.fingerprint || data.name !== ceremony.context.name || data.displayName !== ceremony.context.displayName) return yield* reject;
146
+ const eligible = mapping.registration.eligible(data.registration);
147
+ if (!(yield* owner.check(eligible))) return yield* reject;
148
+ const provision = mapping.registration.subject({
149
+ registration: data.registration,
150
+ ceremony,
151
+ marker: owner.marker
152
+ });
153
+ const nativeId = provision.subjectId;
154
+ const subjectId = SubjectId.make(mapping.read.subjectIds.toSubject(nativeId));
155
+ invariant(mapping.read.subjectIds.equals(mapping.read.subjectIds.toNative(subjectId), nativeId));
156
+ const table = mapping.read.subject;
157
+ const absentSubject = yield* owner.read(table.table, equal(table.table, { [table.id]: nativeId }), { limit: 1 });
158
+ if (absentSubject.rows.length !== 0) return yield* reject;
159
+ const values = {
160
+ ...provision.values,
161
+ [table.id]: nativeId,
162
+ [table.status]: mapping.registration.activeStatus,
163
+ [table.securityRevision]: owner.marker
164
+ };
165
+ absentSubject.rows = (yield* owner.insert(table.table, values, { [table.id]: nativeId })).rows;
166
+ const subject = {
167
+ subjectId,
168
+ nativeId,
169
+ row: values,
170
+ revision: PasskeyRevision.make({
171
+ subjectId,
172
+ securityRevision: PasskeyRevision.fields.securityRevision.make(owner.marker),
173
+ credentials: []
174
+ })
175
+ };
176
+ yield* owner.update(handle.table, { [handle.handleKey]: hashed }, {
177
+ [mapping.read.handleOwnership.subjectId]: nativeId,
178
+ [handle.state]: mapping.write.handleOwnership.ownedState,
179
+ [handle.version]: owner.marker,
180
+ [handle.reservationId]: null
181
+ });
182
+ yield* readCharges(mapping, ceremony, read.policy);
183
+ yield* guardChargeSet(mapping, ceremony);
184
+ yield* insertCredential(mapping, subject, ceremony, input.verified, yield* owner.now(mapping.clock));
185
+ yield* owner.update(intent.table, {
186
+ [intent.moduleId]: mapping.moduleId,
187
+ [intent.flowId]: ceremony.flowId
188
+ }, {
189
+ [intent.state]: intent.acceptedState,
190
+ [intent.version]: owner.marker
191
+ });
192
+ const flow = mapping.flow;
193
+ yield* owner.update(flow.table, {
194
+ [flow.moduleId]: mapping.moduleId,
195
+ [flow.flowId]: ceremony.flowId
196
+ }, {
197
+ [flow.state]: flow.states.RegistrationAccepted,
198
+ [flow.version]: owner.marker
199
+ });
200
+ owner.postconditions.push(liveCondition(mapping, ceremony, input.claim), mapping.registration.finalEligibility({
201
+ registration: data.registration,
202
+ subjectId: nativeId
203
+ }), sql`exists(select 1 from ${table.table} where ${both(owner.exact(table.table, values), table.activeCondition)})`);
204
+ return { _tag: "RegistrationAccepted" };
205
+ })
206
+ };
207
+ };
208
+ //#endregion
209
+ export { makePasskeyRegistrationWriteKernel };
210
+
211
+ //# sourceMappingURL=registration-write.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration-write.mjs","names":[],"sources":["../../../src/internal/passkey/registration-write.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyLabel,\n PasskeyRevision,\n type PasskeyCeremony,\n type PasskeyClaim,\n type PasskeyRegistrationVerified,\n type PasskeyMethodPolicy,\n} from \"@yielded/auth/Passkey\";\nimport { SubjectId } from \"@yielded/auth/Schema\";\nimport { Effect, Schema } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport type { makePasskeyAdmissionKernel } from \"./admission\";\nimport type { makePasskeyCredentialsKernel } from \"./credentials\";\nimport type { makePasskeyFlowKernel } from \"./flow\";\nimport type { makePasskeyRegistrationCustodyKernel } from \"./registration-custody\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\nimport type { makePasskeyWriteStateKernel } from \"./write-state\";\n\nexport const makePasskeyRegistrationWriteKernel = (\n operations: QueryOperations,\n admission: Pick<\n ReturnType<typeof makePasskeyAdmissionKernel>,\n \"lockAdmission\" | \"readCharges\" | \"guardChargeSet\"\n >,\n credentials: Pick<\n ReturnType<typeof makePasskeyCredentialsKernel>,\n \"readModule\" | \"readPolicyGuards\"\n >,\n flow: Pick<\n ReturnType<typeof makePasskeyFlowKernel>,\n | \"compatiblePolicy\"\n | \"exactClaim\"\n | \"issueAssertion\"\n | \"liveCondition\"\n | \"readFlow\"\n | \"terminalFlow\"\n >,\n registrationCustody: Pick<\n ReturnType<typeof makePasskeyRegistrationCustodyKernel>,\n \"releaseRegistrationCustody\"\n >,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"credentialKey\" | \"equal\" | \"handleKey\" | \"invariant\"\n >,\n writeState: Pick<\n ReturnType<typeof makePasskeyWriteStateKernel>,\n \"digest\" | \"insertCredential\" | \"jsonStorage\" | \"validRegistration\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n const { lockAdmission, readCharges, guardChargeSet } = admission;\n const { readModule, readPolicyGuards } = credentials;\n\n const { compatiblePolicy, exactClaim, issueAssertion, liveCondition, readFlow, terminalFlow } =\n flow;\n\n const { releaseRegistrationCustody } = registrationCustody;\n const { ceremonyStorage, credentialKey, equal, handleKey } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { digest, insertCredential, jsonStorage, validRegistration } = writeState;\n const { both } = transactions;\n\n const registrationData = (mapping: any, registration: any) => {\n const codec = jsonStorage(mapping.registration.schema);\n const encoded = codec.encode(registration);\n\n invariant(new TextEncoder().encode(encoded).length <= 1048576);\n\n return {\n encoded,\n registration: codec.decode(encoded),\n fingerprint: digest(mapping.registration.schema, registration),\n };\n };\n\n const describe = (mapping: any, registration: any) => {\n const data = registrationData(mapping, registration);\n\n const labels = Schema.decodeSync(\n Schema.Struct({ name: PasskeyLabel, displayName: PasskeyLabel }),\n )(mapping.registration.describe(data.registration));\n\n return { ...data, ...labels };\n };\n\n const inspectRegistration = Effect.fn(\"passkey.inspectRegistration\")(function* (\n mapping: any,\n registration: any,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const value = describe(mapping, registration);\n const current = yield* readModule(mapping);\n\n yield* readPolicyGuards(mapping);\n\n const eligible =\n current !== undefined &&\n (yield* owner.check(mapping.registration.eligible(value.registration)));\n\n return {\n fingerprint: value.fingerprint,\n name: value.name,\n displayName: value.displayName,\n eligible,\n };\n });\n\n const issueRegistration = Effect.fn(\"passkey.issueRegistration\")(function* (\n mapping: any,\n input: {\n readonly ceremony: PasskeyCeremony;\n readonly policy: PasskeyMethodPolicy;\n readonly registration: any;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ceremony = input.ceremony;\n\n if (\n ceremony.moduleId !== mapping.moduleId ||\n ceremony.purpose !== \"registration\" ||\n ceremony.context._tag !== \"Registration\" ||\n !ceremony.profile.primarySignIn ||\n ceremony.profile.userVerification !== \"required\" ||\n ceremony.profile.residentKey !== \"required\" ||\n ceremony.allowedCredentials.length !== 0\n )\n return { _tag: \"Rejected\" } as const;\n const value = describe(mapping, input.registration);\n\n if (\n value.fingerprint !== ceremony.context.fingerprint ||\n value.name !== ceremony.context.name ||\n value.displayName !== ceremony.context.displayName\n )\n return { _tag: \"Rejected\" } as const;\n if ((yield* readModule(mapping)) === undefined) return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const eligible = mapping.registration.eligible(value.registration);\n\n if (!(yield* owner.check(eligible))) return { _tag: \"Rejected\" } as const;\n const handle = mapping.handle;\n const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const absentHandle = yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n );\n\n const intent = mapping.intent;\n\n const absentCommand = yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.commandId]: ceremony.commandId,\n }),\n { limit: 1 },\n );\n\n const absentFlow = yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n );\n\n if (\n absentHandle.rows.length !== 0 ||\n absentCommand.rows.length !== 0 ||\n absentFlow.rows.length !== 0\n )\n return { _tag: \"Rejected\" } as const;\n const issued = yield* issueAssertion(mapping, ceremony, input.policy);\n\n if (issued._tag !== \"Issued\") return issued;\n\n const reserved = yield* owner.insert(\n handle.table,\n {\n ...handle.encodeInsert({ ceremony, reservationId: owner.marker }),\n [handle.handleKey]: hashed,\n [handle.rpId]: ceremony.profile.rpId,\n [handle.userHandle]: ceremony.context.userHandle,\n [handle.state]: handle.reservedState,\n [handle.version]: owner.marker,\n [handle.reservationId]: owner.marker,\n [mapping.read.handleOwnership.subjectId]: null,\n },\n { [handle.handleKey]: hashed },\n );\n\n absentHandle.rows = reserved.rows;\n\n const inserted = yield* owner.insert(\n intent.table,\n {\n ...intent.encodeInsert({\n ceremony,\n registration: value.registration,\n reservationId: owner.marker,\n }),\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n [intent.commandId]: ceremony.commandId,\n [intent.state]: intent.pendingState,\n [intent.version]: owner.marker,\n [intent.fingerprint]: value.fingerprint,\n [intent.handleKey]: hashed,\n [intent.reservationId]: owner.marker,\n [intent.ceremonySnapshot]: ceremonyStorage.encode(ceremony),\n [intent.applicationSnapshot]: value.encoded,\n },\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n );\n\n absentCommand.rows = inserted.rows;\n absentFlow.rows = inserted.rows;\n owner.postconditions.push(eligible);\n\n return issued;\n });\n\n const completeRegistration = Effect.fn(\"passkey.completeRegistration\")(function* (\n mapping: any,\n input: {\n readonly claim: PasskeyClaim;\n readonly verified: PasskeyRegistrationVerified;\n readonly nowMillis: number;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ceremony = input.claim.ceremony;\n\n if (\n ceremony.moduleId !== mapping.moduleId ||\n ceremony.purpose !== \"registration\" ||\n ceremony.context._tag !== \"Registration\"\n )\n return { _tag: \"Rejected\" } as const;\n const current = yield* readModule(mapping);\n\n if (current === undefined) return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const tuple = mapping.read.credentialOwnership;\n\n const absentCredential = yield* owner.read(\n tuple.table,\n equal(tuple.table, {\n [tuple.credentialKey]: credentialKey(\n ceremony.profile.rpId,\n input.verified.protocolCredentialId,\n ),\n }),\n { limit: 1, observe: false },\n );\n\n const handle = mapping.handle;\n const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n )).rows[0];\n\n const intent = mapping.intent;\n\n const row = (yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n )).rows[0];\n\n const read = yield* readFlow(mapping, ceremony.flowId);\n\n if (read === undefined || !exactClaim(read, input.claim)) return { _tag: \"Rejected\" } as const;\n\n const reject = Effect.gen(function* () {\n yield* releaseRegistrationCustody(mapping, ceremony);\n yield* terminalFlow(mapping, read, \"Rejected\");\n\n return { _tag: \"Rejected\" } as const;\n });\n\n if (\n absentCredential.rows.length !== 0 ||\n held === undefined ||\n row === undefined ||\n !handle.isReservedState(held[handle.state]) ||\n !intent.isPendingState(row[intent.state]) ||\n row[intent.commandId] !== ceremony.commandId ||\n row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) ||\n row[intent.fingerprint] !== ceremony.context.fingerprint ||\n row[intent.handleKey] !== hashed ||\n held[handle.reservationId] !== row[intent.reservationId] ||\n held[handle.rpId] !== ceremony.profile.rpId ||\n held[handle.userHandle] !== ceremony.context.userHandle ||\n !compatiblePolicy(ceremony, read.policy, current) ||\n !validRegistration(ceremony, input.verified) ||\n !input.verified.userVerified ||\n !ceremony.profile.primarySignIn ||\n !(yield* owner.check(liveCondition(mapping, ceremony, input.claim)))\n )\n return yield* reject;\n\n const data = describe(\n mapping,\n jsonStorage(mapping.registration.schema).decode(row[intent.applicationSnapshot]),\n );\n\n if (\n data.encoded !== row[intent.applicationSnapshot] ||\n data.fingerprint !== ceremony.context.fingerprint ||\n data.name !== ceremony.context.name ||\n data.displayName !== ceremony.context.displayName\n )\n return yield* reject;\n const eligible = mapping.registration.eligible(data.registration);\n\n if (!(yield* owner.check(eligible))) return yield* reject;\n\n const provision = mapping.registration.subject({\n registration: data.registration,\n ceremony,\n marker: owner.marker,\n });\n\n const nativeId = provision.subjectId;\n const subjectId = SubjectId.make(mapping.read.subjectIds.toSubject(nativeId));\n\n invariant(\n mapping.read.subjectIds.equals(mapping.read.subjectIds.toNative(subjectId), nativeId),\n );\n const table = mapping.read.subject;\n\n const absentSubject = yield* owner.read(\n table.table,\n equal(table.table, { [table.id]: nativeId }),\n { limit: 1 },\n );\n\n if (absentSubject.rows.length !== 0) return yield* reject;\n\n const values = {\n ...provision.values,\n [table.id]: nativeId,\n [table.status]: mapping.registration.activeStatus,\n [table.securityRevision]: owner.marker,\n };\n\n const inserted = yield* owner.insert(table.table, values, { [table.id]: nativeId });\n\n absentSubject.rows = inserted.rows;\n\n const subject = {\n subjectId,\n nativeId,\n row: values,\n revision: PasskeyRevision.make({\n subjectId,\n securityRevision: PasskeyRevision.fields.securityRevision.make(owner.marker),\n credentials: [],\n }),\n };\n\n yield* owner.update(\n handle.table,\n { [handle.handleKey]: hashed },\n {\n [mapping.read.handleOwnership.subjectId]: nativeId,\n [handle.state]: mapping.write.handleOwnership.ownedState,\n [handle.version]: owner.marker,\n [handle.reservationId]: null,\n },\n );\n yield* readCharges(mapping, ceremony, read.policy);\n yield* guardChargeSet(mapping, ceremony);\n yield* insertCredential(\n mapping,\n subject,\n ceremony,\n input.verified,\n yield* owner.now(mapping.clock),\n );\n yield* owner.update(\n intent.table,\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n { [intent.state]: intent.acceptedState, [intent.version]: owner.marker },\n );\n const flow = mapping.flow;\n\n yield* owner.update(\n flow.table,\n { [flow.moduleId]: mapping.moduleId, [flow.flowId]: ceremony.flowId },\n { [flow.state]: flow.states.RegistrationAccepted, [flow.version]: owner.marker },\n );\n owner.postconditions.push(\n liveCondition(mapping, ceremony, input.claim),\n mapping.registration.finalEligibility({\n registration: data.registration,\n subjectId: nativeId,\n }),\n sql`exists(select 1 from ${table.table} where ${both(owner.exact(table.table, values), table.activeCondition)})`,\n );\n\n return { _tag: \"RegistrationAccepted\" } as const;\n });\n\n return { inspectRegistration, issueRegistration, completeRegistration };\n};\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import "../query-operations.mjs";
2
+ import { TransactionOwner } from "../transaction-kernel.mjs";
3
+ import { Context, Effect } from "effect";
4
+ import { PasskeyCeremony, PasskeyCredential, PasskeyRevision, PasskeyUnavailable } from "@yielded/auth/Passkey";
5
+ //#region src/internal/passkey/state.d.ts
6
+ export type PasskeyOwner = TransactionOwner<PasskeyUnavailable>;
7
+ declare const CurrentPasskeyTransaction_base: Context.ServiceClass<CurrentPasskeyTransaction, "effect-auth/drizzle/CurrentPasskeyTransaction", PasskeyOwner>;
8
+ export declare class CurrentPasskeyTransaction extends CurrentPasskeyTransaction_base {}
9
+ //#endregion
10
+ //# sourceMappingURL=state.d.mts.map
@@ -0,0 +1,234 @@
1
+ import { requiredPasskeyCredentialConstraints, requiredPasskeyPersistenceConstraints } from "../models/passkey-model.mjs";
2
+ import { requiredPasskeyRegistrationCeremonyConstraints } from "../models/passkey-registration-ceremony-model.mjs";
3
+ import { Context, Effect, Encoding, Schema } from "effect";
4
+ import { PasskeyCeremony, PasskeyConfigurationError, PasskeyCredential, PasskeyMethodPolicy, PasskeyModuleId, PasskeyProfile, PasskeyRevision, PasskeyUnavailable, snapshotPasskeySync } from "@yielded/auth/Passkey";
5
+ import { sha256 } from "@noble/hashes/sha2.js";
6
+ import { randomBytes } from "@noble/hashes/utils.js";
7
+ //#region src/internal/passkey/state.ts
8
+ var CurrentPasskeyTransaction = class extends Context.Service()("effect-auth/drizzle/CurrentPasskeyTransaction") {};
9
+ const makePasskeyStateKernel = (operations, transactions) => {
10
+ const { getTableColumns, sql } = operations;
11
+ const { both, makeTransactionRows } = transactions;
12
+ const unavailable = () => PasskeyUnavailable.make({});
13
+ const invariant = (value) => {
14
+ if (!value) throw unavailable();
15
+ };
16
+ const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);
17
+ const nonce = () => Encoding.encodeBase64Url(randomBytes(32));
18
+ const encoder = new TextEncoder();
19
+ const decoder = new TextDecoder("utf-8", { fatal: true });
20
+ /** RP tuples and admission scopes have no collation-dependent string identity. */
21
+ const key = (kind, fields) => {
22
+ const values = ["effect-auth/passkey/" + kind + "/v1", ...fields].map((field) => {
23
+ const bytes = encoder.encode(field);
24
+ invariant(decoder.decode(bytes) === field);
25
+ return bytes;
26
+ });
27
+ const packed = new Uint8Array(values.reduce((size, bytes) => size + 4 + bytes.length, 0));
28
+ const view = new DataView(packed.buffer);
29
+ let offset = 0;
30
+ for (const bytes of values) {
31
+ view.setUint32(offset, bytes.length, false);
32
+ packed.set(bytes, offset + 4);
33
+ offset += 4 + bytes.length;
34
+ }
35
+ return "v1:" + Array.from(sha256(packed), (value) => value.toString(16).padStart(2, "0")).join("");
36
+ };
37
+ const credentialKey = (rpId, id) => key("credential", [rpId, id]);
38
+ const handleKey = (rpId, handle) => key("handle", [rpId, handle]);
39
+ const subjectScope = (subjectId) => key("subject", [subjectId]);
40
+ const targetScope = (ceremony) => {
41
+ const context = ceremony.context;
42
+ if (!("target" in context)) return null;
43
+ const target = context.target;
44
+ return key("target", [
45
+ target.moduleId,
46
+ target.kind,
47
+ target.flowId,
48
+ target.commandId,
49
+ target.bindingDigest,
50
+ target.revision.subjectId
51
+ ]);
52
+ };
53
+ /** Private storage is decoded Type data, never a consumer wire transform. */
54
+ const storage = (schema, maximumBytes) => {
55
+ const codec = Schema.fromJsonString(Schema.toCodecJson(Schema.toType(schema)));
56
+ const encode = (value) => {
57
+ const text = "v1:" + Schema.encodeSync(codec)(snapshotPasskeySync(schema, value));
58
+ invariant(encoder.encode(text).length <= maximumBytes);
59
+ return text;
60
+ };
61
+ return {
62
+ encode,
63
+ decode: (text) => {
64
+ invariant(typeof text === "string" && text.startsWith("v1:"));
65
+ invariant(encoder.encode(text).length <= maximumBytes);
66
+ const value = snapshotPasskeySync(schema, Schema.decodeSync(codec)(text.slice(3)));
67
+ invariant(encode(value) === text);
68
+ return value;
69
+ }
70
+ };
71
+ };
72
+ const policyStorage = storage(PasskeyMethodPolicy, 8388608);
73
+ const ceremonyStorage = storage(PasskeyCeremony, 524288);
74
+ const credentialStorage = storage(PasskeyCredential, 524288);
75
+ const profileStorage = storage(PasskeyProfile, 262144);
76
+ const revisionStorage = storage(PasskeyRevision, 262144);
77
+ const sameCredential = (left, right) => {
78
+ const semantic = (value) => ({
79
+ ...value,
80
+ counter: 0,
81
+ maximumCounter: 0,
82
+ backupState: false,
83
+ revision: {
84
+ ...value.revision,
85
+ credentials: []
86
+ }
87
+ });
88
+ return credentialStorage.encode(semantic(left)) === credentialStorage.encode(semantic(right));
89
+ };
90
+ const sameRevision = (left, right) => {
91
+ const pairs = new Map(left.credentials.map((item) => [item.credentialId, item.revision]));
92
+ return left.subjectId === right.subjectId && left.securityRevision === right.securityRevision && pairs.size === left.credentials.length && new Set(right.credentials.map((item) => item.credentialId)).size === right.credentials.length && pairs.size === right.credentials.length && right.credentials.every((item) => pairs.get(item.credentialId) === item.revision);
93
+ };
94
+ const semanticCredentialColumns = (mapping) => [
95
+ "credentialId",
96
+ "subjectId",
97
+ "rpId",
98
+ "protocolCredentialId",
99
+ "credentialKey",
100
+ "handleKey",
101
+ "userHandle",
102
+ "publicKey",
103
+ "algorithm",
104
+ "profile",
105
+ "credentialRevision",
106
+ "status",
107
+ "primarySignIn",
108
+ "enrollmentUserVerified",
109
+ "backupEligible"
110
+ ].map((name) => mapping[name]);
111
+ const mappedColumns = (mapping) => Object.entries(mapping).filter(([name, value]) => ![
112
+ "table",
113
+ "reservedState",
114
+ "pendingState",
115
+ "acceptedState",
116
+ "rejectedState"
117
+ ].includes(name) && typeof value === "string").map(([, value]) => value);
118
+ const observeSemantic = (mapping, where, row) => Effect.map(CurrentPasskeyTransaction, (owner) => {
119
+ owner.observations.push({
120
+ table: mapping.table,
121
+ where,
122
+ rows: [Object.fromEntries(semanticCredentialColumns(mapping).map((column) => [column, row[column]]))]
123
+ });
124
+ });
125
+ /** Capture descriptor graphs, preserving native SQL expressions and table objects. */
126
+ const captureMapping = (input) => {
127
+ const seen = /* @__PURE__ */ new Map();
128
+ const copy = (value) => {
129
+ if (value === null || typeof value !== "object" || Effect.isEffect(value)) return value;
130
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return value;
131
+ const prior = seen.get(value);
132
+ if (prior !== void 0) return prior;
133
+ const result = Array.isArray(value) ? [] : {};
134
+ seen.set(value, result);
135
+ for (const [name, child] of Object.entries(value)) result[name] = copy(child);
136
+ return Object.freeze(result);
137
+ };
138
+ return copy(input);
139
+ };
140
+ const constraints = (actual, expected) => {
141
+ for (const [name, columns] of Object.entries(expected)) invariant(JSON.stringify(actual?.[name]) === JSON.stringify(columns));
142
+ };
143
+ const descriptor = (value) => {
144
+ const columns = getTableColumns(value.table);
145
+ for (const name of mappedColumns(value)) invariant(columns[name] !== void 0);
146
+ };
147
+ const validateMapping = (mapping, kind) => {
148
+ try {
149
+ const read = kind === "read" ? mapping : kind !== "registration" ? mapping.read : void 0;
150
+ if (read !== void 0) {
151
+ for (const name of [
152
+ "subject",
153
+ "credential",
154
+ "authority",
155
+ "credentialOwnership",
156
+ "handleOwnership"
157
+ ]) descriptor(read[name]);
158
+ constraints(read.constraints, requiredPasskeyCredentialConstraints);
159
+ }
160
+ if (kind === "read") return;
161
+ Schema.decodeUnknownSync(PasskeyModuleId)(mapping.moduleId);
162
+ descriptor(mapping.module);
163
+ invariant(mapping.module.policyColumns.length > 0);
164
+ for (const name of mapping.module.policyColumns) col(mapping.module.table, name);
165
+ const scopes = /* @__PURE__ */ new Set();
166
+ for (const guard of mapping.module.guards ?? []) {
167
+ invariant(typeof guard.scope === "string" && guard.scope.length > 0 && guard.scope.length <= 256);
168
+ invariant(!scopes.has(guard.scope) && guard.columns.length > 0);
169
+ scopes.add(guard.scope);
170
+ for (const name of guard.columns) col(guard.table, name);
171
+ }
172
+ invariant(scopes.size <= 32);
173
+ if (kind === "context") return;
174
+ invariant(typeof mapping.authorityScope === "string" && mapping.authorityScope.length > 0 && mapping.authorityScope.length <= 256);
175
+ for (const name of [
176
+ "flow",
177
+ "admission",
178
+ "charge"
179
+ ]) descriptor(mapping[name]);
180
+ if (kind === "registration") {
181
+ descriptor(mapping.intent);
182
+ descriptor(mapping.handle);
183
+ } else col(mapping.read.credential.table, mapping.telemetry.lastUsedAt);
184
+ constraints(mapping.constraints, kind === "registration" ? requiredPasskeyRegistrationCeremonyConstraints : requiredPasskeyPersistenceConstraints);
185
+ const states = Object.values(mapping.flow.states);
186
+ invariant(states.length === 7 && new Set(states.map((value) => JSON.stringify(value))).size === 7);
187
+ for (const value of [
188
+ 0,
189
+ 1,
190
+ 123456789,
191
+ 864e10
192
+ ]) invariant(mapping.clock.decodeInstant(mapping.clock.encodeInstant(value)) === value);
193
+ } catch {
194
+ throw PasskeyConfigurationError.make({});
195
+ }
196
+ };
197
+ const assertNow = (condition) => Effect.flatMap(CurrentPasskeyTransaction, (owner) => Effect.gen(function* () {
198
+ invariant(yield* owner.check(condition));
199
+ owner.postconditions.push(condition);
200
+ }));
201
+ const existsExact = (table, values, condition) => Effect.map(CurrentPasskeyTransaction, (owner) => sql`exists(select 1 from ${table} where ${both(owner.exact(table, values), condition)})`);
202
+ return {
203
+ unavailable,
204
+ invariant,
205
+ col,
206
+ equal,
207
+ copiedRow,
208
+ matchesNativeRow,
209
+ nonce,
210
+ key,
211
+ credentialKey,
212
+ handleKey,
213
+ subjectScope,
214
+ targetScope,
215
+ policyStorage,
216
+ ceremonyStorage,
217
+ credentialStorage,
218
+ profileStorage,
219
+ revisionStorage,
220
+ sameCredential,
221
+ sameRevision,
222
+ semanticCredentialColumns,
223
+ mappedColumns,
224
+ observeSemantic,
225
+ captureMapping,
226
+ validateMapping,
227
+ assertNow,
228
+ existsExact
229
+ };
230
+ };
231
+ //#endregion
232
+ export { CurrentPasskeyTransaction, makePasskeyStateKernel };
233
+
234
+ //# sourceMappingURL=state.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.mjs","names":[],"sources":["../../../src/internal/passkey/state.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\n// oxlint-disable-next-line import/extensions -- Noble exposes its explicit .js subpaths.\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n// oxlint-disable-next-line import/extensions -- Noble exposes its explicit .js subpaths.\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport {\n PasskeyConfigurationError,\n PasskeyUnavailable,\n PasskeyCeremony,\n PasskeyCredential,\n PasskeyModuleId,\n PasskeyProfile,\n PasskeyRevision,\n PasskeyMethodPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport { Context, Effect, Encoding, Schema } from \"effect\";\n\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"../models/passkey-model\";\nimport { requiredPasskeyRegistrationCeremonyConstraints } from \"../models/passkey-registration-ceremony-model\";\nimport type { SqlExpression as SQL, QueryOperations } from \"../query-operations\";\nimport type { TransactionOwner, makeTransactionKernel } from \"../transaction-kernel\";\n\nexport type PasskeyOwner = TransactionOwner<PasskeyUnavailable>;\n\nexport class CurrentPasskeyTransaction extends Context.Service<\n CurrentPasskeyTransaction,\n PasskeyOwner\n>()(\"effect-auth/drizzle/CurrentPasskeyTransaction\") {}\n\nexport const makePasskeyStateKernel = (\n operations: QueryOperations,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\" | \"makeTransactionRows\">,\n) => {\n const { getTableColumns, sql } = operations;\n const { both, makeTransactionRows } = transactions;\n\n const unavailable = () => PasskeyUnavailable.make({});\n\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);\n\n const nonce = () => Encoding.encodeBase64Url(randomBytes(32));\n\n const encoder = new TextEncoder();\n\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n\n /** RP tuples and admission scopes have no collation-dependent string identity. */\n const key = (kind: string, fields: ReadonlyArray<string>) => {\n const values = [\"effect-auth/passkey/\" + kind + \"/v1\", ...fields].map((field) => {\n const bytes = encoder.encode(field);\n\n invariant(decoder.decode(bytes) === field);\n\n return bytes;\n });\n\n const packed = new Uint8Array(values.reduce((size, bytes) => size + 4 + bytes.length, 0));\n const view = new DataView(packed.buffer);\n let offset = 0;\n\n for (const bytes of values) {\n view.setUint32(offset, bytes.length, false);\n packed.set(bytes, offset + 4);\n offset += 4 + bytes.length;\n }\n\n return (\n \"v1:\" + Array.from(sha256(packed), (value) => value.toString(16).padStart(2, \"0\")).join(\"\")\n );\n };\n\n const credentialKey = (rpId: string, id: string) => key(\"credential\", [rpId, id]);\n\n const handleKey = (rpId: string, handle: string) => key(\"handle\", [rpId, handle]);\n\n const subjectScope = (subjectId: string) => key(\"subject\", [subjectId]);\n\n const targetScope = (ceremony: PasskeyCeremony) => {\n const context = ceremony.context;\n\n if (!(\"target\" in context)) return null;\n const target = context.target;\n\n return key(\"target\", [\n target.moduleId,\n target.kind,\n target.flowId,\n target.commandId,\n target.bindingDigest,\n target.revision.subjectId,\n ]);\n };\n\n /** Private storage is decoded Type data, never a consumer wire transform. */\n const storage = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n maximumBytes: number,\n ) => {\n const codec = Schema.fromJsonString(Schema.toCodecJson(Schema.toType(schema)));\n\n const encode = (value: S[\"Type\"]) => {\n const text = \"v1:\" + Schema.encodeSync(codec)(snapshotPasskeySync(schema, value));\n\n invariant(encoder.encode(text).length <= maximumBytes);\n\n return text;\n };\n\n return {\n encode,\n decode: (text: unknown): S[\"Type\"] => {\n invariant(typeof text === \"string\" && text.startsWith(\"v1:\"));\n invariant(encoder.encode(text).length <= maximumBytes);\n const value = snapshotPasskeySync(schema, Schema.decodeSync(codec)(text.slice(3)));\n\n invariant(encode(value) === text);\n\n return value;\n },\n };\n };\n\n // Legal 32 x 16 x 2048 origins require more than one MiB before JSON escaping.\n const policyStorage = storage(PasskeyMethodPolicy, 8 * 1024 * 1024);\n\n const ceremonyStorage = storage(PasskeyCeremony, 512 * 1024);\n\n const credentialStorage = storage(PasskeyCredential, 512 * 1024);\n\n const profileStorage = storage(PasskeyProfile, 256 * 1024);\n\n const revisionStorage = storage(PasskeyRevision, 256 * 1024);\n\n const sameCredential = (left: PasskeyCredential, right: PasskeyCredential) => {\n const semantic = (value: PasskeyCredential) => ({\n ...value,\n counter: 0,\n maximumCounter: 0,\n backupState: false,\n revision: { ...value.revision, credentials: [] },\n });\n\n return credentialStorage.encode(semantic(left)) === credentialStorage.encode(semantic(right));\n };\n\n const sameRevision = (left: typeof PasskeyRevision.Type, right: typeof PasskeyRevision.Type) => {\n const pairs = new Map(left.credentials.map((item) => [item.credentialId, item.revision]));\n\n return (\n left.subjectId === right.subjectId &&\n left.securityRevision === right.securityRevision &&\n pairs.size === left.credentials.length &&\n new Set(right.credentials.map((item) => item.credentialId)).size ===\n right.credentials.length &&\n pairs.size === right.credentials.length &&\n right.credentials.every((item) => pairs.get(item.credentialId) === item.revision)\n );\n };\n\n const semanticCredentialColumns = (mapping: any): string[] =>\n [\n \"credentialId\",\n \"subjectId\",\n \"rpId\",\n \"protocolCredentialId\",\n \"credentialKey\",\n \"handleKey\",\n \"userHandle\",\n \"publicKey\",\n \"algorithm\",\n \"profile\",\n \"credentialRevision\",\n \"status\",\n \"primarySignIn\",\n \"enrollmentUserVerified\",\n \"backupEligible\",\n ].map((name) => mapping[name]);\n\n const mappedColumns = (mapping: any): string[] =>\n Object.entries(mapping)\n .filter(\n ([name, value]) =>\n ![\"table\", \"reservedState\", \"pendingState\", \"acceptedState\", \"rejectedState\"].includes(\n name,\n ) && typeof value === \"string\",\n )\n .map(([, value]) => value as string);\n\n const observeSemantic = (mapping: any, where: SQL, row: any) =>\n Effect.map(CurrentPasskeyTransaction, (owner) => {\n owner.observations.push({\n table: mapping.table,\n where,\n rows: [\n Object.fromEntries(\n semanticCredentialColumns(mapping).map((column) => [column, row[column]]),\n ),\n ],\n });\n });\n\n /** Capture descriptor graphs, preserving native SQL expressions and table objects. */\n const captureMapping = <A>(input: A): A => {\n const seen = new Map<object, unknown>();\n\n const copy = (value: unknown): unknown => {\n if (value === null || typeof value !== \"object\" || Effect.isEffect(value)) return value;\n if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return value;\n const prior = seen.get(value);\n\n if (prior !== undefined) return prior;\n const result: any = Array.isArray(value) ? [] : {};\n\n seen.set(value, result);\n for (const [name, child] of Object.entries(value)) result[name] = copy(child);\n\n return Object.freeze(result);\n };\n\n return copy(input) as A;\n };\n\n const constraints = (actual: any, expected: any) => {\n for (const [name, columns] of Object.entries(expected))\n // oxlint-disable-next-line no-restricted-properties -- exact configured constraint descriptor fingerprint.\n invariant(JSON.stringify(actual?.[name]) === JSON.stringify(columns));\n };\n\n const descriptor = (value: any) => {\n const columns = getTableColumns(value.table);\n\n for (const name of mappedColumns(value)) invariant(columns[name] !== undefined);\n };\n\n const validateMapping = (\n mapping: any,\n kind: \"read\" | \"context\" | \"assertion\" | \"registration\",\n ) => {\n try {\n const read = kind === \"read\" ? mapping : kind !== \"registration\" ? mapping.read : undefined;\n\n if (read !== undefined) {\n for (const name of [\n \"subject\",\n \"credential\",\n \"authority\",\n \"credentialOwnership\",\n \"handleOwnership\",\n ])\n descriptor(read[name]);\n constraints(read.constraints, requiredPasskeyCredentialConstraints);\n }\n if (kind === \"read\") return;\n // oxlint-disable-next-line no-restricted-properties -- validate the untyped captured configuration boundary.\n Schema.decodeUnknownSync(PasskeyModuleId)(mapping.moduleId);\n descriptor(mapping.module);\n invariant(mapping.module.policyColumns.length > 0);\n for (const name of mapping.module.policyColumns) col(mapping.module.table, name);\n const scopes = new Set<string>();\n\n for (const guard of mapping.module.guards ?? []) {\n invariant(\n typeof guard.scope === \"string\" && guard.scope.length > 0 && guard.scope.length <= 256,\n );\n invariant(!scopes.has(guard.scope) && guard.columns.length > 0);\n scopes.add(guard.scope);\n for (const name of guard.columns) col(guard.table, name);\n }\n invariant(scopes.size <= 32);\n if (kind === \"context\") return;\n invariant(\n typeof mapping.authorityScope === \"string\" &&\n mapping.authorityScope.length > 0 &&\n mapping.authorityScope.length <= 256,\n );\n for (const name of [\"flow\", \"admission\", \"charge\"]) descriptor(mapping[name]);\n if (kind === \"registration\") {\n descriptor(mapping.intent);\n descriptor(mapping.handle);\n } else col(mapping.read.credential.table, mapping.telemetry.lastUsedAt);\n constraints(\n mapping.constraints,\n kind === \"registration\"\n ? requiredPasskeyRegistrationCeremonyConstraints\n : requiredPasskeyPersistenceConstraints,\n );\n const states = Object.values(mapping.flow.states);\n\n // oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.\n invariant(\n // oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.\n states.length === 7 && new Set(states.map((value) => JSON.stringify(value))).size === 7,\n );\n for (const value of [0, 1, 123456789, 8640000000000])\n invariant(mapping.clock.decodeInstant(mapping.clock.encodeInstant(value)) === value);\n } catch {\n throw PasskeyConfigurationError.make({});\n }\n };\n\n const assertNow = (condition: SQL) =>\n Effect.flatMap(CurrentPasskeyTransaction, (owner) =>\n Effect.gen(function* () {\n invariant(yield* owner.check(condition));\n owner.postconditions.push(condition);\n }),\n );\n\n const existsExact = (table: any, values: any, condition?: SQL) =>\n Effect.map(\n CurrentPasskeyTransaction,\n (owner) =>\n sql`exists(select 1 from ${table} where ${both(owner.exact(table, values), condition)})`,\n );\n\n return {\n unavailable,\n invariant,\n col,\n equal,\n copiedRow,\n matchesNativeRow,\n nonce,\n key,\n credentialKey,\n handleKey,\n subjectScope,\n targetScope,\n policyStorage,\n ceremonyStorage,\n credentialStorage,\n profileStorage,\n revisionStorage,\n sameCredential,\n sameRevision,\n semanticCredentialColumns,\n mappedColumns,\n observeSemantic,\n captureMapping,\n validateMapping,\n assertNow,\n existsExact,\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { PersistenceMappingError } from "../mapping-error.mjs";
2
+ import "../models/passkey-model.mjs";
3
+ import "../models/passkey-registration-ceremony-model.mjs";
4
+ import "../transaction-kernel.mjs";
5
+ import { CurrentPasskeyTransaction } from "./state.mjs";
6
+ import "./credentials.mjs";
7
+ import "./flow.mjs";
8
+ import { TransactionCoordinatorError, TransactionExecution, TransactionTargetConfiguration } from "../transaction-execution-kernel.mjs";
9
+ import "./registration-ceremony.mjs";
10
+ import { Effect, Schema } from "effect";
11
+ import { PasskeyConfigurationError, PasskeyPersistence, PasskeyUnavailable, PreparePasskeyCommit } from "@yielded/auth/Passkey";
12
+ import { LifecycleHooks, PreparedCommit } from "@yielded/auth/Hooks";
13
+ import { Statement } from "effect/unstable/sql/Statement";
14
+ //#region src/internal/passkey/target.d.ts
15
+ export type PasskeyTargetConfiguration = TransactionTargetConfiguration<PasskeyUnavailable>;
16
+ export type PasskeyCoordinatorError<E> = TransactionCoordinatorError<E, PasskeyUnavailable> | PasskeyConfigurationError | PersistenceMappingError;
17
+ export type PasskeyExecution = TransactionExecution<PasskeyUnavailable, CurrentPasskeyTransaction, never>;
18
+ //#endregion
19
+ //# sourceMappingURL=target.d.mts.map