@yielded/auth-persistence-drizzle 0.1.0-beta.23 → 0.1.0-beta.25

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 (189) hide show
  1. package/README.md +3 -0
  2. package/dist/D1.d.mts +9 -9
  3. package/dist/D1.mjs +2 -1
  4. package/dist/D1.mjs.map +1 -1
  5. package/dist/Libsql.d.mts +48 -64
  6. package/dist/Libsql.mjs +2 -1
  7. package/dist/Libsql.mjs.map +1 -1
  8. package/dist/Mysql2.d.mts +26 -27
  9. package/dist/Pglite.d.mts +28 -28
  10. package/dist/Pglite.mjs +2 -1
  11. package/dist/Pglite.mjs.map +1 -1
  12. package/dist/Postgres.d.mts +29 -29
  13. package/dist/Postgres.mjs +2 -1
  14. package/dist/Postgres.mjs.map +1 -1
  15. package/dist/SqliteBun.d.mts +49 -65
  16. package/dist/SqliteBun.mjs +2 -1
  17. package/dist/SqliteBun.mjs.map +1 -1
  18. package/dist/SqliteDo.d.mts +47 -63
  19. package/dist/SqliteDo.mjs +2 -1
  20. package/dist/SqliteDo.mjs.map +1 -1
  21. package/dist/SqliteNode.d.mts +51 -67
  22. package/dist/SqliteNode.mjs +2 -1
  23. package/dist/SqliteNode.mjs.map +1 -1
  24. package/dist/SqliteWasm.d.mts +47 -63
  25. package/dist/SqliteWasm.mjs +2 -1
  26. package/dist/SqliteWasm.mjs.map +1 -1
  27. package/dist/drizzle/crypto.mjs +1 -1
  28. package/dist/drizzle/d1-emails.d.mts +1 -1
  29. package/dist/drizzle/d1-oauth.d.mts +15 -15
  30. package/dist/drizzle/d1-passkey.d.mts +1 -1
  31. package/dist/drizzle/d1-password-prepared.d.mts +4 -4
  32. package/dist/drizzle/d1-password-prepared.mjs +1 -1
  33. package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
  34. package/dist/drizzle/d1-passwords.d.mts +17 -33
  35. package/dist/drizzle/d1-passwords.mjs +57 -199
  36. package/dist/drizzle/d1-passwords.mjs.map +1 -1
  37. package/dist/drizzle/d1-phone.d.mts +1 -1
  38. package/dist/drizzle/d1-proofs.d.mts +1 -1
  39. package/dist/drizzle/d1-sessions.d.mts +2 -2
  40. package/dist/drizzle/d1-sessions.mjs +11 -6
  41. package/dist/drizzle/d1-sessions.mjs.map +1 -1
  42. package/dist/drizzle/d1-totp.d.mts +1 -1
  43. package/dist/drizzle/mysql-password-prepared.d.mts +5 -5
  44. package/dist/drizzle/mysql-password-prepared.mjs.map +1 -1
  45. package/dist/drizzle/mysql-passwords.d.mts +16 -32
  46. package/dist/drizzle/mysql-passwords.mjs.map +1 -1
  47. package/dist/drizzle/mysql-sessions.d.mts +1 -1
  48. package/dist/drizzle/native-sql-table.mjs +26 -1
  49. package/dist/drizzle/native-sql-table.mjs.map +1 -1
  50. package/dist/drizzle/oauth-connected-model.d.mts +20 -281
  51. package/dist/drizzle/oauth-connected-model.mjs +3 -17
  52. package/dist/drizzle/oauth-connected-model.mjs.map +1 -1
  53. package/dist/drizzle/oauth-connected-reference.d.mts +1 -3
  54. package/dist/drizzle/oauth-connected-reference.mjs +4 -53
  55. package/dist/drizzle/oauth-connected-reference.mjs.map +1 -1
  56. package/dist/drizzle/oauth-connected-target.d.mts +3 -990
  57. package/dist/drizzle/oauth-connected-target.mjs +8 -92
  58. package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
  59. package/dist/drizzle/oauth-kernel.mjs +11 -0
  60. package/dist/drizzle/oauth-kernel.mjs.map +1 -0
  61. package/dist/drizzle/oauth-model.d.mts +35 -438
  62. package/dist/drizzle/oauth-model.mjs +6 -44
  63. package/dist/drizzle/oauth-model.mjs.map +1 -1
  64. package/dist/drizzle/oauth-mysql.mjs +2 -2
  65. package/dist/drizzle/oauth-mysql.mjs.map +1 -1
  66. package/dist/drizzle/oauth-proxy.d.mts +10 -0
  67. package/dist/drizzle/oauth-proxy.mjs +54 -0
  68. package/dist/drizzle/oauth-proxy.mjs.map +1 -0
  69. package/dist/drizzle/oauth-target.d.mts +6 -107
  70. package/dist/drizzle/oauth-target.mjs +15 -245
  71. package/dist/drizzle/oauth-target.mjs.map +1 -1
  72. package/dist/drizzle/password-model.d.mts +5 -6
  73. package/dist/drizzle/password-prepared-model.d.mts +5 -5
  74. package/dist/drizzle/password-prepared-model.mjs.map +1 -1
  75. package/dist/drizzle/password-prepared-sql.mjs +1 -1
  76. package/dist/drizzle/password-prepared-sql.mjs.map +1 -1
  77. package/dist/drizzle/password-prepared-state.mjs +1 -1
  78. package/dist/drizzle/pg-password-prepared.d.mts +5 -5
  79. package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
  80. package/dist/drizzle/pg-passwords.d.mts +16 -32
  81. package/dist/drizzle/pg-passwords.mjs.map +1 -1
  82. package/dist/drizzle/pg-sessions.d.mts +1 -1
  83. package/dist/drizzle/phone-target.d.mts +1 -1
  84. package/dist/drizzle/phone-target.mjs +1 -1
  85. package/dist/drizzle/query-operations.mjs +3 -1
  86. package/dist/drizzle/query-operations.mjs.map +1 -1
  87. package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
  88. package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
  89. package/dist/drizzle/storage-validation.mjs +10 -185
  90. package/dist/drizzle/storage-validation.mjs.map +1 -1
  91. package/dist/drizzle/totp-target.d.mts +2 -2
  92. package/dist/drizzle/totp-target.mjs +2 -2
  93. package/dist/drizzle/transaction-execution.d.mts +2 -2
  94. package/dist/drizzle/transaction-owner.d.mts +1 -2
  95. package/dist/index.d.mts +3 -2
  96. package/dist/internal/drizzle-postgres-oauth-proxy.d.mts +138 -0
  97. package/dist/internal/drizzle-postgres-oauth-proxy.mjs +29 -0
  98. package/dist/internal/drizzle-postgres-oauth-proxy.mjs.map +1 -0
  99. package/dist/internal/drizzle-sqlite-oauth-proxy.d.mts +146 -0
  100. package/dist/internal/drizzle-sqlite-oauth-proxy.mjs +29 -0
  101. package/dist/internal/drizzle-sqlite-oauth-proxy.mjs.map +1 -0
  102. package/package.json +1 -1
  103. package/src/D1.ts +2 -0
  104. package/src/Libsql.ts +2 -0
  105. package/src/Pglite.ts +2 -0
  106. package/src/Postgres.ts +2 -0
  107. package/src/SqliteBun.ts +2 -0
  108. package/src/SqliteDo.ts +2 -0
  109. package/src/SqliteNode.ts +2 -0
  110. package/src/SqliteWasm.ts +2 -0
  111. package/src/drizzle/d1-password-prepared.ts +4 -6
  112. package/src/drizzle/d1-passwords.ts +125 -459
  113. package/src/drizzle/d1-sessions.ts +21 -6
  114. package/src/drizzle/mysql-password-prepared.ts +4 -8
  115. package/src/drizzle/mysql-passwords.ts +5 -10
  116. package/src/drizzle/native-sql-table.ts +30 -0
  117. package/src/drizzle/oauth-connected-model.ts +97 -315
  118. package/src/drizzle/oauth-connected-reference.ts +13 -93
  119. package/src/drizzle/oauth-connected-target.ts +14 -271
  120. package/src/drizzle/oauth-kernel.ts +10 -0
  121. package/src/drizzle/oauth-model.ts +188 -541
  122. package/src/drizzle/oauth-mysql.ts +2 -2
  123. package/src/drizzle/oauth-proxy.ts +101 -0
  124. package/src/drizzle/oauth-target.ts +33 -737
  125. package/src/drizzle/password-model.ts +0 -13
  126. package/src/drizzle/password-prepared-model.ts +0 -6
  127. package/src/drizzle/password-prepared-sql.ts +2 -1
  128. package/src/drizzle/pg-password-prepared.ts +4 -8
  129. package/src/drizzle/pg-passwords.ts +5 -10
  130. package/src/drizzle/query-operations.ts +4 -0
  131. package/src/drizzle/sqlite-password-prepared.ts +4 -8
  132. package/src/drizzle/sqlite-passwords.ts +7 -14
  133. package/src/drizzle/storage-validation.ts +13 -214
  134. package/src/index.ts +2 -4
  135. package/src/internal/drizzle-postgres-oauth-proxy.ts +37 -0
  136. package/src/internal/drizzle-sqlite-oauth-proxy.ts +44 -0
  137. package/dist/_virtual/_rolldown/runtime.mjs +0 -13
  138. package/dist/drizzle/oauth-accounts.mjs +0 -359
  139. package/dist/drizzle/oauth-accounts.mjs.map +0 -1
  140. package/dist/drizzle/oauth-connected-access.mjs +0 -212
  141. package/dist/drizzle/oauth-connected-access.mjs.map +0 -1
  142. package/dist/drizzle/oauth-connected-collection.mjs +0 -202
  143. package/dist/drizzle/oauth-connected-collection.mjs.map +0 -1
  144. package/dist/drizzle/oauth-connected-custody.mjs +0 -147
  145. package/dist/drizzle/oauth-connected-custody.mjs.map +0 -1
  146. package/dist/drizzle/oauth-connected-flow.mjs +0 -297
  147. package/dist/drizzle/oauth-connected-flow.mjs.map +0 -1
  148. package/dist/drizzle/oauth-connected-input.mjs +0 -122
  149. package/dist/drizzle/oauth-connected-input.mjs.map +0 -1
  150. package/dist/drizzle/oauth-connected-maintenance.mjs +0 -214
  151. package/dist/drizzle/oauth-connected-maintenance.mjs.map +0 -1
  152. package/dist/drizzle/oauth-connected-management.mjs +0 -229
  153. package/dist/drizzle/oauth-connected-management.mjs.map +0 -1
  154. package/dist/drizzle/oauth-connected-settlement.mjs +0 -128
  155. package/dist/drizzle/oauth-connected-settlement.mjs.map +0 -1
  156. package/dist/drizzle/oauth-connected-sign-in.mjs +0 -265
  157. package/dist/drizzle/oauth-connected-sign-in.mjs.map +0 -1
  158. package/dist/drizzle/oauth-connected-state.d.mts +0 -2062
  159. package/dist/drizzle/oauth-connected-state.mjs +0 -430
  160. package/dist/drizzle/oauth-connected-state.mjs.map +0 -1
  161. package/dist/drizzle/oauth-flow.d.mts +0 -22
  162. package/dist/drizzle/oauth-flow.mjs +0 -253
  163. package/dist/drizzle/oauth-flow.mjs.map +0 -1
  164. package/dist/drizzle/oauth-input.mjs +0 -127
  165. package/dist/drizzle/oauth-input.mjs.map +0 -1
  166. package/dist/drizzle/oauth-owner.d.mts +0 -10
  167. package/dist/drizzle/oauth-owner.mjs +0 -10
  168. package/dist/drizzle/oauth-owner.mjs.map +0 -1
  169. package/dist/drizzle/oauth-registration.d.mts +0 -5
  170. package/dist/drizzle/oauth-registration.mjs +0 -374
  171. package/dist/drizzle/oauth-registration.mjs.map +0 -1
  172. package/dist/drizzle/oauth-state.mjs +0 -107
  173. package/dist/drizzle/oauth-state.mjs.map +0 -1
  174. package/src/drizzle/oauth-accounts.ts +0 -805
  175. package/src/drizzle/oauth-connected-access.ts +0 -361
  176. package/src/drizzle/oauth-connected-collection.ts +0 -324
  177. package/src/drizzle/oauth-connected-custody.ts +0 -279
  178. package/src/drizzle/oauth-connected-flow.ts +0 -529
  179. package/src/drizzle/oauth-connected-input.ts +0 -147
  180. package/src/drizzle/oauth-connected-maintenance.ts +0 -346
  181. package/src/drizzle/oauth-connected-management.ts +0 -365
  182. package/src/drizzle/oauth-connected-settlement.ts +0 -251
  183. package/src/drizzle/oauth-connected-sign-in.ts +0 -443
  184. package/src/drizzle/oauth-connected-state.ts +0 -872
  185. package/src/drizzle/oauth-flow.ts +0 -462
  186. package/src/drizzle/oauth-input.ts +0 -201
  187. package/src/drizzle/oauth-owner.ts +0 -29
  188. package/src/drizzle/oauth-registration.ts +0 -640
  189. package/src/drizzle/oauth-state.ts +0 -218
@@ -1,640 +0,0 @@
1
- import {
2
- OAuthRegistrationAccess,
3
- OAuthRegistrationDecision,
4
- OAuthRegistrationFingerprint,
5
- OAuthRegistrationInspection,
6
- OAuthRegistrationIntent,
7
- OAuthCredentialSnapshot,
8
- OAuthDisplayProfile,
9
- OAuthSignInTransactionContext,
10
- type OAuthExternalIdentity,
11
- snapshotOAuthSync,
12
- } from "@yielded/auth/OAuth";
13
- import type { SecurityRevision } from "@yielded/auth/Sessions";
14
- /* oxlint-disable no-explicit-any -- private owner kernel preserves Registration at the exported constructor. */
15
- import { eq, sql } from "drizzle-orm";
16
- import { Effect, Schema } from "effect";
17
-
18
- import type { PersistenceMappingError, SubjectIdCodec } from "./model";
19
- import { discoverOwned, exactClaim, resolveOwned, terminalFlow } from "./oauth-flow";
20
- import type { OAuthRegistrationBase } from "./oauth-model";
21
- import { both, col, equal, matchesNativeRow, CurrentOAuthTransaction } from "./oauth-owner";
22
- import { invariant, oauthIdentityKey, sameIdentity, storage } from "./oauth-state";
23
-
24
- export const intentStorage = storage(OAuthRegistrationIntent);
25
- const decisionStorage = storage(OAuthRegistrationDecision);
26
- const contextStorage = storage(OAuthSignInTransactionContext);
27
- const profileStorage = storage(Schema.NullOr(OAuthDisplayProfile));
28
-
29
- const reservationStorage = storage(
30
- Schema.Struct({
31
- moduleId: Schema.String,
32
- reference: Schema.String,
33
- commandId: Schema.String,
34
- fingerprint: Schema.String,
35
- provisioningIdentity: Schema.String,
36
- }),
37
- );
38
-
39
- export const readTuple = (ownership: any, identity: typeof OAuthExternalIdentity.Type) =>
40
- Effect.flatMap(CurrentOAuthTransaction, (owner) =>
41
- Effect.gen(function* () {
42
- const t = ownership.tuple,
43
- key = yield* oauthIdentityKey(identity);
44
-
45
- const where = { [t.identityKey]: key };
46
- const observed = yield* owner.read(t.table, equal(t.table, where), { limit: 1 });
47
-
48
- if (observed.rows.length === 0) {
49
- const values = {
50
- ...t.encodeInsert({ identityKey: key, identity }),
51
- ...where,
52
- [t.provider]: identity.provider,
53
- [t.issuer]: identity.issuer,
54
- [t.externalSubject]: identity.subject,
55
- [t.state]: "Unowned",
56
- [t.version]: owner.marker,
57
- [t.subjectId]: null,
58
- [t.reservation]: null,
59
- };
60
-
61
- const inserted = yield* owner.insert(t.table, values, where, true);
62
-
63
- observed.rows = inserted.rows;
64
- }
65
- const row = observed.rows[0]!;
66
-
67
- invariant(
68
- row[t.provider] === identity.provider &&
69
- row[t.issuer] === identity.issuer &&
70
- row[t.externalSubject] === identity.subject &&
71
- ["Unowned", "Reserved", "Owned"].includes(row[t.state]),
72
- );
73
- invariant(
74
- row[t.state] === "Owned"
75
- ? row[t.subjectId] !== null
76
- : row[t.subjectId] === null &&
77
- (row[t.state] === "Reserved"
78
- ? typeof row[t.reservation] === "string"
79
- : row[t.reservation] === null),
80
- );
81
- if (ownership.mode === "separate") {
82
- const o = ownership.external;
83
-
84
- const external = yield* owner.read(o.table, equal(o.table, { [o.identityKey]: key }), {
85
- limit: 1,
86
- });
87
-
88
- if (row[t.state] === "Owned") {
89
- const linked = external.rows[0];
90
-
91
- invariant(
92
- linked !== undefined &&
93
- linked[o.provider] === identity.provider &&
94
- linked[o.issuer] === identity.issuer &&
95
- linked[o.externalSubject] === identity.subject &&
96
- matchesNativeRow(o.table, linked, { [o.subjectId]: row[t.subjectId] }),
97
- );
98
- const owned = sql`exists(select 1 from ${o.table} where ${both(equal(o.table, { [o.identityKey]: key }), o.ownedCondition)})`;
99
-
100
- invariant(yield* owner.check(owned));
101
- owner.postconditions.push(owned);
102
- } else invariant(external.rows.length === 0);
103
- }
104
-
105
- return { observed, row, key };
106
- }),
107
- );
108
-
109
- export const acquireTuple = (
110
- ownership: any,
111
- identity: typeof OAuthExternalIdentity.Type,
112
- key: string,
113
- subjectId: unknown,
114
- ) =>
115
- Effect.flatMap(CurrentOAuthTransaction, (owner) =>
116
- Effect.gen(function* () {
117
- const t = ownership.tuple;
118
-
119
- yield* owner.update(
120
- t.table,
121
- { [t.identityKey]: key },
122
- {
123
- [t.state]: "Owned",
124
- [t.subjectId]: subjectId,
125
- [t.reservation]: null,
126
- [t.version]: owner.marker,
127
- },
128
- );
129
- if (ownership.mode === "separate") {
130
- const o = ownership.external;
131
-
132
- const values = {
133
- ...o.encodeInsert({ identity, identityKey: key, subjectId }),
134
- [o.identityKey]: key,
135
- [o.provider]: identity.provider,
136
- [o.issuer]: identity.issuer,
137
- [o.externalSubject]: identity.subject,
138
- [o.subjectId]: subjectId,
139
- };
140
-
141
- yield* owner.insert(o.table, values, { [o.identityKey]: key });
142
- }
143
- }),
144
- );
145
-
146
- export const settleRegistrationIntent = (mapping: any, input: any) =>
147
- Effect.flatMap(CurrentOAuthTransaction, (owner) =>
148
- Effect.gen(function* () {
149
- const identity = input.identity.identity,
150
- context = input.claim.flow.context;
151
-
152
- invariant(identity.provider === context.provider && identity.issuer === context.issuer);
153
- const found = yield* discoverOwned(mapping.signIn, identity);
154
- const tuple = found.owned ? undefined : yield* readTuple(mapping.ownership, identity);
155
- const exact = yield* exactClaim(mapping.signIn, input.claim, "sign-in");
156
-
157
- if (exact === undefined) return { _tag: "Rejected" } as const;
158
- const credential = resolveOwned(mapping.signIn, context.moduleId, identity, found);
159
-
160
- if (credential !== undefined) {
161
- yield* terminalFlow(mapping.signIn, input.claim.flow, "Verified");
162
-
163
- return { _tag: "Verified", credential } as const;
164
- }
165
- let decision: any = { _tag: "Rejected" };
166
-
167
- if (
168
- !found.owned &&
169
- tuple?.row[mapping.ownership.tuple.state] === "Unowned" &&
170
- input.intent !== undefined
171
- ) {
172
- const intent = snapshotOAuthSync(OAuthRegistrationIntent, input.intent);
173
- const c = intent.context;
174
-
175
- invariant(
176
- sameIdentity(intent.identity, identity) &&
177
- profileStorage.encode(intent.profile ?? null) ===
178
- profileStorage.encode(input.identity.profile ?? null) &&
179
- intent.claimId === input.claim.claimId &&
180
- intent.claimedAtMillis === input.claim.claimedAtMillis &&
181
- contextStorage.encode(c) === contextStorage.encode(context),
182
- );
183
-
184
- const allowed =
185
- intent.issuedAtMillis >= intent.claimedAtMillis &&
186
- intent.verifiedAtMillis <= intent.issuedAtMillis &&
187
- intent.issuedAtMillis <= exact.now &&
188
- exact.now < intent.expiresAtMillis &&
189
- intent.expiresAtMillis <= c.requestBindingExpiresAtMillis &&
190
- intent.retentionUntilMillis >= intent.expiresAtMillis &&
191
- (yield* owner.check(mapping.eligibility.condition({ intent })));
192
-
193
- if (allowed) {
194
- const i = mapping.intent,
195
- encode = mapping.signIn.clock.encodeInstant;
196
-
197
- const key = { [i.moduleId]: c.moduleId, [i.reference]: intent.reference };
198
-
199
- const values = {
200
- ...i.encodeInsert(snapshotOAuthSync(OAuthRegistrationIntent, intent)),
201
- ...key,
202
- [i.flowId]: c.flowId,
203
- [i.claimId]: intent.claimId,
204
- [i.identityKey]: tuple.key,
205
- [i.version]: owner.marker,
206
- [i.state]: "Unbound",
207
- [i.snapshot]: intentStorage.encode(intent),
208
- [i.commandId]: null,
209
- [i.fingerprint]: null,
210
- [i.pendingReference]: null,
211
- [i.expiresAt]: encode(intent.expiresAtMillis),
212
- [i.retentionUntil]: encode(intent.retentionUntilMillis),
213
- };
214
-
215
- yield* owner.insert(i.table, values, key);
216
- owner.postconditions.push(
217
- mapping.eligibility.condition({ intent }),
218
- sql`${mapping.signIn.clock.engineNowMillis} >= ${intent.issuedAtMillis} and
219
- ${mapping.signIn.clock.engineNowMillis} < ${intent.expiresAtMillis}`,
220
- );
221
- decision = { _tag: "RegistrationIssued", intent };
222
- }
223
- }
224
- yield* terminalFlow(mapping.signIn, input.claim.flow, decision._tag);
225
-
226
- return decision;
227
- }),
228
- );
229
-
230
- export const inspectIntent = (
231
- mapping: any,
232
- original: typeof OAuthRegistrationAccess.Type,
233
- discovery = false,
234
- ) =>
235
- Effect.flatMap(CurrentOAuthTransaction, (owner) =>
236
- Effect.gen(function* () {
237
- const access = snapshotOAuthSync(OAuthRegistrationAccess, original),
238
- i = mapping.intent;
239
-
240
- const observed = yield* owner.read(
241
- i.table,
242
- equal(i.table, {
243
- [i.moduleId]: access.moduleId,
244
- [i.reference]: access.reference,
245
- }),
246
- { limit: 1, ...(discovery ? { lock: false, observe: false } : {}) },
247
- );
248
-
249
- const row = observed.rows[0];
250
-
251
- if (row === undefined) return undefined;
252
-
253
- const intent = intentStorage.decode(row[i.snapshot]),
254
- c = intent.context,
255
- now = yield* owner.now(mapping.clock);
256
-
257
- invariant(
258
- row[i.moduleId] === c.moduleId &&
259
- row[i.reference] === intent.reference &&
260
- row[i.flowId] === c.flowId &&
261
- row[i.claimId] === intent.claimId &&
262
- row[i.identityKey] === (yield* oauthIdentityKey(intent.identity)) &&
263
- mapping.clock.decodeInstant(row[i.expiresAt]) === intent.expiresAtMillis &&
264
- mapping.clock.decodeInstant(row[i.retentionUntil]) === intent.retentionUntilMillis,
265
- );
266
- if (
267
- access.moduleId !== c.moduleId ||
268
- access.reference !== intent.reference ||
269
- access.flowId !== c.flowId ||
270
- access.requestBindingVerifier !== c.requestBindingVerifier ||
271
- access.requestBindingExpiresAtMillis !== c.requestBindingExpiresAtMillis ||
272
- access.credentialDigest !== intent.credentialDigest ||
273
- now < intent.issuedAtMillis ||
274
- now >= intent.expiresAtMillis
275
- )
276
- return undefined;
277
- let application: any;
278
-
279
- if (row[i.state] === "Unbound") {
280
- invariant(
281
- row[i.commandId] === null &&
282
- row[i.fingerprint] === null &&
283
- row[i.pendingReference] === null,
284
- );
285
- application = { _tag: "Unbound" };
286
- } else {
287
- application = {
288
- _tag: row[i.state],
289
- commandId: row[i.commandId],
290
- fingerprint: row[i.fingerprint],
291
- ...(row[i.state] === "ProvisioningPending" ? { reference: row[i.pendingReference] } : {}),
292
- };
293
- }
294
- const inspection = snapshotOAuthSync(OAuthRegistrationInspection, { intent, application });
295
-
296
- owner.postconditions.push(sql`${mapping.clock.engineNowMillis} >= ${intent.issuedAtMillis} and
297
- ${mapping.clock.engineNowMillis} < ${intent.expiresAtMillis}`);
298
-
299
- return { observed, row, inspection };
300
- }),
301
- );
302
-
303
- export interface RegistrationResources {
304
- readonly provisioningIdentity: string;
305
- readonly pendingReference?: string;
306
- readonly subjectId?: unknown;
307
- readonly credentialId?: string;
308
- readonly revision?: string;
309
- }
310
-
311
- /** Public mapping callbacks are closed Effects; erasing native row shapes must
312
- * not erase their error or requirements channels. */
313
- export type RegistrationCallbacks<Registration> = Pick<
314
- OAuthRegistrationBase<Registration, any, any, any, any, unknown>,
315
- "snapshot" | "snapshotSync" | "inspect" | "inspectSync"
316
- >;
317
-
318
- export const registrationResources = (mapping: any) =>
319
- Effect.gen(function* () {
320
- const provisioningIdentity = yield* mapping.allocateProvisioningIdentity as Effect.Effect<
321
- string,
322
- PersistenceMappingError
323
- >;
324
-
325
- invariant(
326
- typeof provisioningIdentity === "string" &&
327
- provisioningIdentity.length > 0 &&
328
- provisioningIdentity.length <= 256,
329
- );
330
- if (mapping.mode === "pending") {
331
- const pendingReference = yield* mapping.allocatePendingReference as Effect.Effect<
332
- string,
333
- PersistenceMappingError
334
- >;
335
-
336
- invariant(
337
- typeof pendingReference === "string" &&
338
- pendingReference.length > 0 &&
339
- pendingReference.length <= 256,
340
- );
341
-
342
- return { provisioningIdentity, pendingReference } as RegistrationResources;
343
- }
344
-
345
- const subjectId = yield* mapping.allocateSubjectId as Effect.Effect<
346
- unknown,
347
- PersistenceMappingError
348
- >;
349
-
350
- const publicId = yield* (mapping.subjectId as SubjectIdCodec<unknown>).toSubject(subjectId);
351
-
352
- invariant(
353
- mapping.subjectId.equals(
354
- subjectId,
355
- yield* (mapping.subjectId as SubjectIdCodec<unknown>).toNative(publicId),
356
- ),
357
- );
358
-
359
- const credentialId = snapshotOAuthSync(
360
- OAuthCredentialSnapshot.fields.credentialId,
361
- yield* mapping.allocateCredentialId as Effect.Effect<string, PersistenceMappingError>,
362
- );
363
-
364
- const revision = snapshotOAuthSync(
365
- OAuthCredentialSnapshot.fields.credentialRevision,
366
- yield* mapping.allocateRevision as Effect.Effect<SecurityRevision, PersistenceMappingError>,
367
- );
368
-
369
- return { provisioningIdentity, subjectId, credentialId, revision } as RegistrationResources;
370
- });
371
-
372
- export const registrationData = <Registration>(
373
- mapping: any,
374
- input: {
375
- readonly intent: OAuthRegistrationIntent;
376
- readonly registration: Registration;
377
- readonly fingerprint: string;
378
- },
379
- bound: boolean,
380
- ) =>
381
- Effect.gen(function* () {
382
- const snapshot = bound
383
- ? (invariant(mapping.snapshotSync !== undefined), mapping.snapshotSync(input.registration))
384
- : yield* (mapping as RegistrationCallbacks<Registration>).snapshot(input.registration);
385
-
386
- const encoded = mapping.application.encode(snapshot);
387
-
388
- invariant(typeof encoded === "string" && new TextEncoder().encode(encoded).length <= 1048576);
389
- const detached = mapping.application.decode(encoded);
390
-
391
- invariant(mapping.application.encode(detached) === encoded);
392
-
393
- const checked = bound
394
- ? (invariant(mapping.inspectSync !== undefined),
395
- mapping.inspectSync({
396
- intent: snapshotOAuthSync(OAuthRegistrationIntent, input.intent),
397
- registration: mapping.application.decode(encoded),
398
- }))
399
- : yield* (mapping as RegistrationCallbacks<Registration>).inspect({
400
- intent: snapshotOAuthSync(OAuthRegistrationIntent, input.intent),
401
- registration: mapping.application.decode(encoded),
402
- });
403
-
404
- const fingerprint = snapshotOAuthSync(OAuthRegistrationFingerprint, checked.fingerprint);
405
-
406
- invariant(typeof checked.eligible === "boolean");
407
-
408
- return {
409
- encoded,
410
- registration: detached as Registration,
411
- fingerprint,
412
- eligible: checked.eligible && fingerprint === input.fingerprint,
413
- };
414
- });
415
-
416
- export const register = (
417
- mapping: any,
418
- input: any,
419
- data: { readonly encoded: string; readonly fingerprint: string; readonly eligible: boolean },
420
- resources: RegistrationResources,
421
- ) =>
422
- Effect.flatMap(CurrentOAuthTransaction, (owner) =>
423
- Effect.gen(function* () {
424
- const intent = snapshotOAuthSync(OAuthRegistrationIntent, input.intent),
425
- c = intent.context;
426
-
427
- const discovered = yield* inspectIntent(mapping, input.access, true);
428
-
429
- if (
430
- discovered === undefined ||
431
- intentStorage.encode(discovered.inspection.intent) !== intentStorage.encode(intent)
432
- )
433
- return { _tag: "Rejected" } as const;
434
- const nativeSubjectId = resources.subjectId;
435
-
436
- const policyInput = () => ({
437
- intent: snapshotOAuthSync(OAuthRegistrationIntent, intent),
438
- registration: mapping.application.decode(data.encoded),
439
- ...(mapping.mode === "atomic" ? { nativeSubjectId } : {}),
440
- });
441
-
442
- invariant((mapping.eligibility.guards?.length ?? 0) <= 32);
443
- for (const guard of mapping.eligibility.guards ?? []) {
444
- const locked = yield* owner.read(guard.table, guard.condition(policyInput()), {
445
- orderBy: col(guard.table, guard.orderBy),
446
- admissionOnly: true,
447
- });
448
-
449
- invariant(locked.rows.length > 0);
450
- }
451
- const tuple = yield* readTuple(mapping.ownership, intent.identity);
452
- const inspected = yield* inspectIntent(mapping, input.access);
453
-
454
- if (
455
- inspected === undefined ||
456
- intentStorage.encode(inspected.inspection.intent) !== intentStorage.encode(intent)
457
- )
458
- return { _tag: "Rejected" } as const;
459
-
460
- const r = mapping.command,
461
- i = mapping.intent,
462
- t = mapping.ownership.tuple;
463
-
464
- const commandKey = { [r.moduleId]: c.moduleId, [r.commandId]: input.commandId };
465
- const command = yield* owner.read(r.table, equal(r.table, commandKey), { limit: 1 });
466
- const previous = command.rows[0];
467
-
468
- if (previous !== undefined) {
469
- if (
470
- previous[r.reference] !== intent.reference ||
471
- previous[r.identityKey] !== tuple.key ||
472
- previous[r.fingerprint] !== data.fingerprint ||
473
- previous[r.intentSnapshot] !== intentStorage.encode(intent) ||
474
- previous[r.applicationSnapshot] !== data.encoded
475
- )
476
- return { _tag: "Conflict" } as const;
477
- invariant(
478
- mapping.application.encode(
479
- mapping.application.decode(previous[r.applicationSnapshot]),
480
- ) === data.encoded,
481
- );
482
- const old = decisionStorage.decode(previous[r.decision]);
483
-
484
- invariant(inspected.inspection.application._tag === old._tag);
485
-
486
- return old._tag === "Registered" || old._tag === "ProvisioningPending"
487
- ? { ...old, replayed: true }
488
- : old;
489
- }
490
- if (inspected.inspection.application._tag !== "Unbound" || tuple.row[t.state] !== "Unowned")
491
- return { _tag: "Conflict" } as const;
492
- if (data.fingerprint !== input.fingerprint) return { _tag: "Rejected" } as const;
493
-
494
- const admitted =
495
- data.eligible && (yield* owner.check(mapping.eligibility.admission(policyInput())));
496
-
497
- let decision: typeof OAuthRegistrationDecision.Type;
498
-
499
- if (!admitted) decision = { _tag: "Rejected" };
500
- else if (mapping.mode === "pending") {
501
- invariant(resources.pendingReference !== undefined);
502
-
503
- const reservation = reservationStorage.encode({
504
- moduleId: c.moduleId,
505
- reference: intent.reference,
506
- commandId: input.commandId,
507
- fingerprint: data.fingerprint,
508
- provisioningIdentity: resources.provisioningIdentity,
509
- });
510
-
511
- yield* owner.update(
512
- t.table,
513
- { [t.identityKey]: tuple.key },
514
- {
515
- [t.state]: "Reserved",
516
- [t.version]: owner.marker,
517
- [t.reservation]: reservation,
518
- },
519
- );
520
- decision = {
521
- _tag: "ProvisioningPending",
522
- reference: resources.pendingReference,
523
- replayed: false,
524
- };
525
- } else {
526
- invariant(resources.credentialId !== undefined && resources.revision !== undefined);
527
-
528
- const s = mapping.subject,
529
- cr = mapping.credential,
530
- a = mapping.authority;
531
-
532
- const subjectValues = {
533
- ...mapping.encodeSubjectInsert(policyInput(), {
534
- subjectId: nativeSubjectId,
535
- securityRevision: resources.revision,
536
- }),
537
- [s.id]: nativeSubjectId,
538
- [s.securityRevision]: resources.revision,
539
- };
540
-
541
- yield* owner.insert(s.table, subjectValues, { [s.id]: nativeSubjectId });
542
-
543
- const credentialValues = {
544
- ...cr.encodeInsert({
545
- moduleId: c.moduleId,
546
- subjectId: nativeSubjectId,
547
- identityKey: tuple.key,
548
- credentialId: resources.credentialId,
549
- credentialRevision: resources.revision,
550
- }),
551
- [cr.moduleId]: c.moduleId,
552
- [cr.subjectId]: nativeSubjectId,
553
- [cr.identityKey]: tuple.key,
554
- [cr.credentialId]: resources.credentialId,
555
- [cr.credentialRevision]: resources.revision,
556
- };
557
-
558
- invariant(
559
- cr.isActiveStatus(credentialValues[cr.status]) &&
560
- s.isActiveStatus(subjectValues[s.status]),
561
- );
562
- yield* owner.insert(cr.table, credentialValues, {
563
- [cr.credentialId]: resources.credentialId,
564
- });
565
-
566
- const authorityValues = {
567
- ...a.encodeInsert({
568
- subjectId: nativeSubjectId,
569
- credentialId: resources.credentialId,
570
- revision: resources.revision,
571
- }),
572
- [a.subjectId]: nativeSubjectId,
573
- [a.credentialId]: resources.credentialId,
574
- [a.revision]: resources.revision,
575
- };
576
-
577
- invariant(a.isActiveStatus(authorityValues[a.status]));
578
- yield* owner.insert(a.table, authorityValues, {
579
- [a.subjectId]: nativeSubjectId,
580
- [a.credentialId]: resources.credentialId,
581
- });
582
- yield* acquireTuple(mapping.ownership, intent.identity, tuple.key, nativeSubjectId);
583
- owner.postconditions.push(
584
- sql`exists(select 1 from ${s.table} where ${both(
585
- eq(col(s.table, s.id), nativeSubjectId),
586
- s.activeCondition,
587
- )})`,
588
- sql`exists(select 1 from ${cr.table} where ${both(eq(col(cr.table, cr.credentialId), resources.credentialId), cr.activeCondition)})`,
589
- sql`exists(select 1 from ${a.table} where ${both(
590
- eq(col(a.table, a.credentialId), resources.credentialId),
591
- eq(col(a.table, a.subjectId), nativeSubjectId),
592
- a.activeCondition,
593
- )})`,
594
- );
595
- decision = { _tag: "Registered", replayed: false };
596
- }
597
- const now = yield* owner.now(mapping.clock);
598
-
599
- invariant(Number.isSafeInteger(mapping.retentionMillis) && mapping.retentionMillis >= 120000);
600
-
601
- const values = {
602
- ...r.encodeInsert({
603
- intent: snapshotOAuthSync(OAuthRegistrationIntent, intent),
604
- commandId: input.commandId,
605
- fingerprint: data.fingerprint,
606
- registration: mapping.application.decode(data.encoded),
607
- provisioningIdentity: resources.provisioningIdentity,
608
- }),
609
- ...commandKey,
610
- [r.reference]: intent.reference,
611
- [r.identityKey]: tuple.key,
612
- [r.fingerprint]: data.fingerprint,
613
- [r.intentSnapshot]: intentStorage.encode(intent),
614
- [r.applicationSnapshot]: data.encoded,
615
- [r.provisioningIdentity]: resources.provisioningIdentity,
616
- [r.decision]: decisionStorage.encode(decision),
617
- [r.retentionUntil]: mapping.clock.encodeInstant(
618
- Math.max(intent.retentionUntilMillis, now + mapping.retentionMillis),
619
- ),
620
- };
621
-
622
- const inserted = yield* owner.insert(r.table, values, commandKey);
623
-
624
- command.rows = inserted.rows;
625
- yield* owner.update(
626
- i.table,
627
- { [i.moduleId]: c.moduleId, [i.reference]: intent.reference },
628
- {
629
- [i.state]: decision._tag,
630
- [i.commandId]: input.commandId,
631
- [i.fingerprint]: data.fingerprint,
632
- [i.version]: owner.marker,
633
- [i.pendingReference]: decision._tag === "ProvisioningPending" ? decision.reference : null,
634
- },
635
- );
636
- if (admitted) owner.postconditions.push(mapping.eligibility.postcondition(policyInput()));
637
-
638
- return decision;
639
- }),
640
- );