@yielded/auth-persistence-drizzle 0.1.0-beta.13 → 0.1.0-beta.15
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.
- package/dist/D1.d.mts +123 -9
- package/dist/Libsql.d.mts +143 -29
- package/dist/Mysql2.d.mts +130 -16
- package/dist/Pglite.d.mts +130 -16
- package/dist/Postgres.d.mts +131 -17
- package/dist/SqliteBun.d.mts +141 -27
- package/dist/SqliteDo.d.mts +145 -31
- package/dist/SqliteNode.d.mts +135 -21
- package/dist/SqliteWasm.d.mts +145 -31
- package/dist/drizzle/d1-emails.d.mts +3 -3
- package/dist/drizzle/d1-emails.mjs +56 -11
- package/dist/drizzle/d1-emails.mjs.map +1 -1
- package/dist/drizzle/d1-identity.mjs +9 -2
- package/dist/drizzle/d1-identity.mjs.map +1 -1
- package/dist/drizzle/d1-oauth.d.mts +8 -8
- package/dist/drizzle/d1-oauth.mjs +8 -8
- package/dist/drizzle/d1-oauth.mjs.map +1 -1
- package/dist/drizzle/d1-passkey.d.mts +2 -1
- package/dist/drizzle/d1-passkey.mjs +10 -8
- package/dist/drizzle/d1-passkey.mjs.map +1 -1
- package/dist/drizzle/d1-password-prepared.d.mts +1 -1
- package/dist/drizzle/d1-password-prepared.mjs +16 -5
- package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
- package/dist/drizzle/d1-passwords.d.mts +2 -2
- package/dist/drizzle/d1-passwords.mjs +19 -6
- package/dist/drizzle/d1-passwords.mjs.map +1 -1
- package/dist/drizzle/d1-phone.d.mts +2 -1
- package/dist/drizzle/d1-phone.mjs +4 -2
- package/dist/drizzle/d1-phone.mjs.map +1 -1
- package/dist/drizzle/d1-proofs.d.mts +1 -1
- package/dist/drizzle/d1-proofs.mjs +18 -7
- package/dist/drizzle/d1-proofs.mjs.map +1 -1
- package/dist/drizzle/d1-sessions.d.mts +6 -5
- package/dist/drizzle/d1-sessions.mjs +29 -19
- package/dist/drizzle/d1-sessions.mjs.map +1 -1
- package/dist/drizzle/d1-totp.d.mts +2 -1
- package/dist/drizzle/d1-totp.mjs +4 -2
- package/dist/drizzle/d1-totp.mjs.map +1 -1
- package/dist/drizzle/email-registration.d.mts +5 -1
- package/dist/drizzle/email-registration.mjs +48 -4
- package/dist/drizzle/email-registration.mjs.map +1 -1
- package/dist/drizzle/email-target.d.mts +1 -0
- package/dist/drizzle/email-target.mjs +9 -0
- package/dist/drizzle/email-target.mjs.map +1 -1
- package/dist/drizzle/mysql-emails.d.mts +4 -4
- package/dist/drizzle/mysql-emails.mjs +8 -7
- package/dist/drizzle/mysql-emails.mjs.map +1 -1
- package/dist/drizzle/mysql-identity.mjs +11 -4
- package/dist/drizzle/mysql-identity.mjs.map +1 -1
- package/dist/drizzle/mysql-password-prepared.d.mts +2 -2
- package/dist/drizzle/mysql-password-prepared.mjs +4 -3
- package/dist/drizzle/mysql-password-prepared.mjs.map +1 -1
- package/dist/drizzle/mysql-passwords.d.mts +3 -3
- package/dist/drizzle/mysql-passwords.mjs +7 -6
- package/dist/drizzle/mysql-passwords.mjs.map +1 -1
- package/dist/drizzle/mysql-proofs.d.mts +2 -2
- package/dist/drizzle/mysql-proofs.mjs +4 -3
- package/dist/drizzle/mysql-proofs.mjs.map +1 -1
- package/dist/drizzle/mysql-sessions.d.mts +6 -6
- package/dist/drizzle/mysql-sessions.mjs +16 -15
- package/dist/drizzle/mysql-sessions.mjs.map +1 -1
- package/dist/drizzle/native-database.mjs +1 -1
- package/dist/drizzle/native-database.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-drivers.mjs +10 -8
- package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-flow.mjs +62 -13
- package/dist/drizzle/oauth-connected-flow.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-input.mjs +6 -0
- package/dist/drizzle/oauth-connected-input.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-maintenance.mjs +3 -3
- package/dist/drizzle/oauth-connected-maintenance.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-target.d.mts +116 -2
- package/dist/drizzle/oauth-connected-target.mjs +5 -3
- package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
- package/dist/drizzle/oauth-drivers.mjs +8 -8
- package/dist/drizzle/oauth-drivers.mjs.map +1 -1
- package/dist/drizzle/oauth-target.d.mts +3 -3
- package/dist/drizzle/oauth-target.mjs +8 -6
- package/dist/drizzle/oauth-target.mjs.map +1 -1
- package/dist/drizzle/passkey-drivers.mjs +4 -4
- package/dist/drizzle/passkey-drivers.mjs.map +1 -1
- package/dist/drizzle/passkey-write-drivers.mjs +4 -4
- package/dist/drizzle/passkey-write-drivers.mjs.map +1 -1
- package/dist/drizzle/passkey-write-target.d.mts +1 -1
- package/dist/drizzle/password-prepared-sql.mjs +5 -0
- package/dist/drizzle/password-prepared-sql.mjs.map +1 -1
- package/dist/drizzle/password-prepared-target.d.mts +2 -1
- package/dist/drizzle/password-prepared-target.mjs +5 -0
- package/dist/drizzle/password-prepared-target.mjs.map +1 -1
- package/dist/drizzle/password-registration.d.mts +1 -1
- package/dist/drizzle/password-registration.mjs +2 -0
- package/dist/drizzle/password-registration.mjs.map +1 -1
- package/dist/drizzle/password-target.d.mts +1 -0
- package/dist/drizzle/password-target.mjs +6 -0
- package/dist/drizzle/password-target.mjs.map +1 -1
- package/dist/drizzle/pg-emails.d.mts +4 -4
- package/dist/drizzle/pg-emails.mjs +8 -7
- package/dist/drizzle/pg-emails.mjs.map +1 -1
- package/dist/drizzle/pg-identity.mjs +12 -5
- package/dist/drizzle/pg-identity.mjs.map +1 -1
- package/dist/drizzle/pg-password-prepared.d.mts +2 -2
- package/dist/drizzle/pg-password-prepared.mjs +4 -3
- package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
- package/dist/drizzle/pg-passwords.d.mts +3 -3
- package/dist/drizzle/pg-passwords.mjs +7 -6
- package/dist/drizzle/pg-passwords.mjs.map +1 -1
- package/dist/drizzle/pg-proofs.d.mts +2 -2
- package/dist/drizzle/pg-proofs.mjs +4 -3
- package/dist/drizzle/pg-proofs.mjs.map +1 -1
- package/dist/drizzle/pg-sessions.d.mts +6 -6
- package/dist/drizzle/pg-sessions.mjs +16 -15
- package/dist/drizzle/pg-sessions.mjs.map +1 -1
- package/dist/drizzle/phone-target.mjs +11 -2
- package/dist/drizzle/phone-target.mjs.map +1 -1
- package/dist/drizzle/proof-target.d.mts +1 -0
- package/dist/drizzle/proof-target.mjs +2 -0
- package/dist/drizzle/proof-target.mjs.map +1 -1
- package/dist/drizzle/query-operations.mjs +2 -0
- package/dist/drizzle/query-operations.mjs.map +1 -1
- package/dist/drizzle/session-target.d.mts +1 -0
- package/dist/drizzle/session-target.mjs +9 -6
- package/dist/drizzle/session-target.mjs.map +1 -1
- package/dist/drizzle/sqlite-emails.mjs +8 -7
- package/dist/drizzle/sqlite-emails.mjs.map +1 -1
- package/dist/drizzle/sqlite-identity.mjs +7 -2
- package/dist/drizzle/sqlite-identity.mjs.map +1 -1
- package/dist/drizzle/sqlite-password-prepared.mjs +4 -3
- package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
- package/dist/drizzle/sqlite-passwords.mjs +7 -6
- package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
- package/dist/drizzle/sqlite-proofs.mjs +4 -3
- package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
- package/dist/drizzle/sqlite-sessions.mjs +16 -15
- package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
- package/dist/drizzle/storage-validation.mjs +234 -0
- package/dist/drizzle/storage-validation.mjs.map +1 -0
- package/dist/drizzle/totp-target.mjs +5 -2
- package/dist/drizzle/totp-target.mjs.map +1 -1
- package/dist/drizzle/transaction-mysql.mjs +1 -1
- package/package.json +1 -1
- package/src/drizzle/d1-emails.ts +158 -40
- package/src/drizzle/d1-identity.ts +13 -0
- package/src/drizzle/d1-oauth.ts +8 -8
- package/src/drizzle/d1-passkey.ts +10 -8
- package/src/drizzle/d1-password-prepared.ts +14 -2
- package/src/drizzle/d1-passwords.ts +22 -4
- package/src/drizzle/d1-phone.ts +4 -2
- package/src/drizzle/d1-proofs.ts +55 -3
- package/src/drizzle/d1-sessions.ts +67 -18
- package/src/drizzle/d1-totp.ts +4 -2
- package/src/drizzle/email-registration.ts +160 -12
- package/src/drizzle/email-target.ts +22 -4
- package/src/drizzle/mysql-emails.ts +17 -7
- package/src/drizzle/mysql-identity.ts +13 -0
- package/src/drizzle/mysql-password-prepared.ts +7 -3
- package/src/drizzle/mysql-passwords.ts +13 -6
- package/src/drizzle/mysql-proofs.ts +7 -3
- package/src/drizzle/mysql-sessions.ts +31 -15
- package/src/drizzle/native-database.ts +5 -4
- package/src/drizzle/oauth-connected-drivers.ts +10 -8
- package/src/drizzle/oauth-connected-flow.ts +98 -10
- package/src/drizzle/oauth-connected-input.ts +7 -0
- package/src/drizzle/oauth-connected-maintenance.ts +3 -2
- package/src/drizzle/oauth-connected-target.ts +4 -2
- package/src/drizzle/oauth-drivers.ts +8 -8
- package/src/drizzle/oauth-target.ts +13 -7
- package/src/drizzle/passkey-drivers.ts +4 -4
- package/src/drizzle/passkey-write-drivers.ts +8 -5
- package/src/drizzle/password-prepared-sql.ts +13 -1
- package/src/drizzle/password-prepared-target.ts +7 -1
- package/src/drizzle/password-registration.ts +10 -3
- package/src/drizzle/password-target.ts +22 -4
- package/src/drizzle/pg-emails.ts +17 -7
- package/src/drizzle/pg-identity.ts +13 -0
- package/src/drizzle/pg-password-prepared.ts +7 -3
- package/src/drizzle/pg-passwords.ts +13 -6
- package/src/drizzle/pg-proofs.ts +7 -3
- package/src/drizzle/pg-sessions.ts +31 -15
- package/src/drizzle/phone-target.ts +30 -5
- package/src/drizzle/proof-target.ts +10 -2
- package/src/drizzle/query-operations.ts +2 -0
- package/src/drizzle/session-target.ts +20 -3
- package/src/drizzle/sqlite-emails.ts +17 -7
- package/src/drizzle/sqlite-identity.ts +5 -0
- package/src/drizzle/sqlite-password-prepared.ts +7 -3
- package/src/drizzle/sqlite-passwords.ts +13 -6
- package/src/drizzle/sqlite-proofs.ts +7 -3
- package/src/drizzle/sqlite-sessions.ts +31 -15
- package/src/drizzle/storage-validation.ts +285 -0
- package/src/drizzle/totp-target.ts +16 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import {
|
|
2
3
|
type PrepareEmailCommit,
|
|
3
4
|
type EmailCommandId,
|
|
@@ -16,7 +17,7 @@ import type { LoginIdentifier } from "@yielded/auth/Identity";
|
|
|
16
17
|
import { reportPersistenceFailure } from "@yielded/auth/Persistence";
|
|
17
18
|
import { type ProofCompletionPlan, ProofUnavailable } from "@yielded/auth/Proofs";
|
|
18
19
|
import type { TokenDigest } from "@yielded/auth/Schema";
|
|
19
|
-
import
|
|
20
|
+
import { SecurityRevision } from "@yielded/auth/Sessions";
|
|
20
21
|
/* oxlint-disable no-explicit-any -- registration plans bridge consumer-owned Drizzle rows. */
|
|
21
22
|
import { and, eq, isNull } from "drizzle-orm";
|
|
22
23
|
import { Cause, DateTime, Effect, Schema } from "effect";
|
|
@@ -26,13 +27,14 @@ import {
|
|
|
26
27
|
requiredEmailRegistrationConstraints,
|
|
27
28
|
} from "./email-model";
|
|
28
29
|
import { CurrentEmailSql, type EmailSqlDatabase, type EmailSqlQuery } from "./email-sql";
|
|
29
|
-
import { column, isMappedConstraintConflict, PersistenceMappingError } from "./model";
|
|
30
|
+
import { column, isMappedConstraintConflict, PersistenceMappingError, updateValues } from "./model";
|
|
30
31
|
import {
|
|
31
32
|
CurrentProofSql,
|
|
32
33
|
completeProofPlanIn,
|
|
33
34
|
type ProofSqlConfiguration,
|
|
34
35
|
type ProofSqlDatabase,
|
|
35
36
|
} from "./proof-sql";
|
|
37
|
+
import { validateDrizzleStorage } from "./storage-validation";
|
|
36
38
|
|
|
37
39
|
type Mapping<Registration> = AnyEmailRegistrationMapping<Registration>;
|
|
38
40
|
const unavailable = () => EmailUnavailable.make({});
|
|
@@ -146,6 +148,69 @@ const registrationRows = Effect.fn("Drizzle.registrationRows")(function* <Regist
|
|
|
146
148
|
);
|
|
147
149
|
});
|
|
148
150
|
|
|
151
|
+
/** Discover before locking, then lock subject before identifier like the other
|
|
152
|
+
* identity owners. A changed discovery is rejected rather than locking a second
|
|
153
|
+
* subject in the opposite order. Proof authority separately enforces the host's
|
|
154
|
+
* active/unverified eligibility policy in the same physical owner. */
|
|
155
|
+
export const readEmailRegistrationTarget = Effect.fnUntraced(function* <Registration>(
|
|
156
|
+
mapping: Extract<Mapping<Registration>, { readonly mode: "atomic" }>,
|
|
157
|
+
identifier: LoginIdentifier,
|
|
158
|
+
locking: boolean,
|
|
159
|
+
) {
|
|
160
|
+
const database = yield* CurrentEmailSql;
|
|
161
|
+
const i = mapping.identifier;
|
|
162
|
+
const s = mapping.subject;
|
|
163
|
+
|
|
164
|
+
const lookup = () =>
|
|
165
|
+
database
|
|
166
|
+
.select()
|
|
167
|
+
.from(i.table)
|
|
168
|
+
.where(
|
|
169
|
+
and(
|
|
170
|
+
eq(column(i.table, i.namespace), identifier.namespace),
|
|
171
|
+
eq(column(i.table, i.value), identifier.value),
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
.limit(1);
|
|
175
|
+
|
|
176
|
+
const discovered = (yield* lookup())[0];
|
|
177
|
+
|
|
178
|
+
if (discovered === undefined) return { _tag: "Absent" } as const;
|
|
179
|
+
if (discovered[i.verifiedAt] !== null) return { _tag: "Rejected" } as const;
|
|
180
|
+
const nativeSubjectId = discovered[i.subjectId];
|
|
181
|
+
|
|
182
|
+
const subject = (yield* selectRows(
|
|
183
|
+
database
|
|
184
|
+
.select()
|
|
185
|
+
.from(s.table)
|
|
186
|
+
.where(eq(column(s.table, s.id), nativeSubjectId))
|
|
187
|
+
.limit(1),
|
|
188
|
+
locking,
|
|
189
|
+
))[0];
|
|
190
|
+
|
|
191
|
+
const current = (yield* selectRows(lookup(), locking))[0];
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
subject === undefined ||
|
|
195
|
+
current === undefined ||
|
|
196
|
+
current[i.verifiedAt] !== null ||
|
|
197
|
+
!mapping.subjectId.equals(current[i.subjectId], nativeSubjectId) ||
|
|
198
|
+
current[i.bindingRevision] !== discovered[i.bindingRevision]
|
|
199
|
+
)
|
|
200
|
+
return { _tag: "Rejected" } as const;
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
_tag: "Reserved" as const,
|
|
204
|
+
nativeSubjectId,
|
|
205
|
+
bindingRevision: yield* Schema.decodeUnknownEffect(SecurityRevision)(
|
|
206
|
+
current[i.bindingRevision],
|
|
207
|
+
),
|
|
208
|
+
securityRevision: yield* Schema.decodeUnknownEffect(SecurityRevision)(
|
|
209
|
+
subject[s.securityRevision],
|
|
210
|
+
),
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
|
|
149
214
|
const owned = <A, E, R>(
|
|
150
215
|
database: EmailSqlDatabase,
|
|
151
216
|
configuration: EmailRegistrationConfiguration,
|
|
@@ -223,6 +288,7 @@ const registerIn = Effect.fn("DrizzleEmailRegistration.registerIn")(function* <R
|
|
|
223
288
|
allocated: {
|
|
224
289
|
readonly credentialId?: string;
|
|
225
290
|
readonly securityRevision?: SecurityRevision;
|
|
291
|
+
readonly displacedSecurityRevision?: SecurityRevision;
|
|
226
292
|
readonly identifierRevision?: SecurityRevision;
|
|
227
293
|
readonly credentialRevision?: SecurityRevision;
|
|
228
294
|
readonly nativeSubjectId?: unknown;
|
|
@@ -244,6 +310,20 @@ const registerIn = Effect.fn("DrizzleEmailRegistration.registerIn")(function* <R
|
|
|
244
310
|
if (!completionMatches(mapping, input)) return prepare({ _tag: "Rejected" }, journal);
|
|
245
311
|
if (!inspected.eligible || inspected.fingerprint !== input.fingerprint)
|
|
246
312
|
return prepare({ _tag: "Rejected" }, journal);
|
|
313
|
+
|
|
314
|
+
const target =
|
|
315
|
+
mapping.mode === "atomic"
|
|
316
|
+
? yield* readEmailRegistrationTarget(mapping, input.identifier, configuration.locking)
|
|
317
|
+
: undefined;
|
|
318
|
+
|
|
319
|
+
if (target?._tag === "Rejected") return prepare({ _tag: "Rejected" }, journal);
|
|
320
|
+
if (
|
|
321
|
+
target?._tag === "Reserved" &&
|
|
322
|
+
(allocated.displacedSecurityRevision === undefined ||
|
|
323
|
+
allocated.displacedSecurityRevision === target.securityRevision ||
|
|
324
|
+
allocated.identifierRevision === target.bindingRevision)
|
|
325
|
+
)
|
|
326
|
+
return yield* unavailable();
|
|
247
327
|
const now = yield* nowMillis;
|
|
248
328
|
|
|
249
329
|
const intent = {
|
|
@@ -298,14 +378,49 @@ const registerIn = Effect.fn("DrizzleEmailRegistration.registerIn")(function* <R
|
|
|
298
378
|
nativeSubjectId = yield* mapping.provisioning.decodeGeneratedId(rows);
|
|
299
379
|
} else yield* subjectInsert;
|
|
300
380
|
if (nativeSubjectId === undefined) return false;
|
|
301
|
-
|
|
302
|
-
mapping.identifier
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
381
|
+
if (target?._tag === "Reserved") {
|
|
382
|
+
const i = mapping.identifier;
|
|
383
|
+
const s = mapping.subject;
|
|
384
|
+
|
|
385
|
+
// Change only mapped ownership columns. Never delete a consumer-owned
|
|
386
|
+
// identifier row or copy insertion defaults over unrelated columns.
|
|
387
|
+
yield* transaction
|
|
388
|
+
.update(i.table)
|
|
389
|
+
.set(
|
|
390
|
+
updateValues([
|
|
391
|
+
[i.subjectId, nativeSubjectId],
|
|
392
|
+
[i.verifiedAt, mapping.encodeInstant(now)],
|
|
393
|
+
[i.bindingRevision, allocated.identifierRevision],
|
|
394
|
+
]),
|
|
395
|
+
)
|
|
396
|
+
.where(
|
|
397
|
+
and(
|
|
398
|
+
eq(column(i.table, i.namespace), input.identifier.namespace),
|
|
399
|
+
eq(column(i.table, i.value), input.identifier.value),
|
|
400
|
+
eq(column(i.table, i.subjectId), target.nativeSubjectId),
|
|
401
|
+
eq(column(i.table, i.bindingRevision), target.bindingRevision),
|
|
402
|
+
isNull(column(i.table, i.verifiedAt)),
|
|
403
|
+
),
|
|
404
|
+
);
|
|
405
|
+
yield* transaction
|
|
406
|
+
.update(s.table)
|
|
407
|
+
.set(updateValues([[s.securityRevision, allocated.displacedSecurityRevision]]))
|
|
408
|
+
.where(
|
|
409
|
+
and(
|
|
410
|
+
eq(column(s.table, s.id), target.nativeSubjectId),
|
|
411
|
+
eq(column(s.table, s.securityRevision), target.securityRevision),
|
|
412
|
+
),
|
|
413
|
+
);
|
|
414
|
+
} else {
|
|
415
|
+
yield* transaction.insert(mapping.identifier.table).values(
|
|
416
|
+
mapping.identifier.encodeVerifiedInsert({
|
|
417
|
+
identifier: input.identifier,
|
|
418
|
+
subjectId: nativeSubjectId,
|
|
419
|
+
verifiedAtMillis: now,
|
|
420
|
+
bindingRevision: allocated.identifierRevision,
|
|
421
|
+
}),
|
|
422
|
+
);
|
|
423
|
+
}
|
|
309
424
|
yield* transaction.insert(mapping.credential.table).values(
|
|
310
425
|
mapping.credential.encodeVerifiedInsert({
|
|
311
426
|
moduleId: input.moduleId,
|
|
@@ -456,7 +571,25 @@ const registerIn = Effect.fn("DrizzleEmailRegistration.registerIn")(function* <R
|
|
|
456
571
|
false,
|
|
457
572
|
))[0];
|
|
458
573
|
|
|
574
|
+
const displaced =
|
|
575
|
+
target?._tag === "Reserved"
|
|
576
|
+
? (yield* transaction
|
|
577
|
+
.select()
|
|
578
|
+
.from(mapping.subject.table)
|
|
579
|
+
.where(
|
|
580
|
+
and(
|
|
581
|
+
eq(subjectId, target.nativeSubjectId),
|
|
582
|
+
eq(
|
|
583
|
+
column(mapping.subject.table, mapping.subject.securityRevision),
|
|
584
|
+
allocated.displacedSecurityRevision,
|
|
585
|
+
),
|
|
586
|
+
),
|
|
587
|
+
)
|
|
588
|
+
.limit(1))[0]
|
|
589
|
+
: undefined;
|
|
590
|
+
|
|
459
591
|
if (
|
|
592
|
+
(target?._tag === "Reserved" && displaced === undefined) ||
|
|
460
593
|
subject === undefined ||
|
|
461
594
|
!mapping.subject.isActiveStatus(subject[mapping.subject.status]) ||
|
|
462
595
|
subject[mapping.subject.securityRevision] !== allocated.securityRevision ||
|
|
@@ -544,10 +677,16 @@ export const makeSqlEmailRegistrationAuthority = Effect.fn("makeSqlEmailRegistra
|
|
|
544
677
|
configuration: EmailRegistrationConfiguration,
|
|
545
678
|
): Effect.fn.Return<
|
|
546
679
|
EmailRegistrationAuthority<Registration>,
|
|
547
|
-
|
|
548
|
-
LifecycleHooks | CurrentEmailSql
|
|
680
|
+
EmailUnavailable,
|
|
681
|
+
LifecycleHooks | CurrentEmailSql | NativeDatabase
|
|
549
682
|
> {
|
|
550
683
|
const database = yield* CurrentEmailSql;
|
|
684
|
+
|
|
685
|
+
if (!configuration.coordinated)
|
|
686
|
+
yield* validateDrizzleStorage({
|
|
687
|
+
...mapping,
|
|
688
|
+
proof: configuration.proof.mapping,
|
|
689
|
+
}).pipe(Effect.mapError(unavailable));
|
|
551
690
|
const hooks = yield* LifecycleHooks;
|
|
552
691
|
|
|
553
692
|
return {
|
|
@@ -618,6 +757,14 @@ export const makeSqlEmailRegistrationAuthority = Effect.fn("makeSqlEmailRegistra
|
|
|
618
757
|
)
|
|
619
758
|
: undefined;
|
|
620
759
|
|
|
760
|
+
const displacedSecurityRevision = atomic
|
|
761
|
+
? yield* allocate(
|
|
762
|
+
configuration.mode,
|
|
763
|
+
mapping.allocateRevision,
|
|
764
|
+
mapping.allocateRevisionSync,
|
|
765
|
+
)
|
|
766
|
+
: undefined;
|
|
767
|
+
|
|
621
768
|
const identifierRevision = atomic
|
|
622
769
|
? yield* allocate(
|
|
623
770
|
configuration.mode,
|
|
@@ -664,6 +811,7 @@ export const makeSqlEmailRegistrationAuthority = Effect.fn("makeSqlEmailRegistra
|
|
|
664
811
|
{
|
|
665
812
|
...(credentialId === undefined ? {} : { credentialId }),
|
|
666
813
|
...(securityRevision === undefined ? {} : { securityRevision }),
|
|
814
|
+
...(displacedSecurityRevision === undefined ? {} : { displacedSecurityRevision }),
|
|
667
815
|
...(identifierRevision === undefined ? {} : { identifierRevision }),
|
|
668
816
|
...(credentialRevision === undefined ? {} : { credentialRevision }),
|
|
669
817
|
...(nativeSubjectId === undefined ? {} : { nativeSubjectId }),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import { EmailAddressPersistence, EmailSignInTargets, EmailUnavailable } from "@yielded/auth/Email";
|
|
2
3
|
import {
|
|
3
4
|
coordinateCommit,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
} from "./email-sql";
|
|
25
26
|
import type { ProofTargetConfiguration } from "./proof-target";
|
|
26
27
|
import { sqlClientStandaloneGuard, type TransactionService } from "./standalone-guard";
|
|
28
|
+
import { validateDrizzleStorage } from "./storage-validation";
|
|
27
29
|
|
|
28
30
|
export interface EmailTargetConfiguration {
|
|
29
31
|
readonly mode: "interactive" | "synchronous";
|
|
@@ -144,13 +146,21 @@ export const coordinateTargetEmailAddress = <Transaction, A, E, R>(
|
|
|
144
146
|
transaction: Transaction,
|
|
145
147
|
services: { readonly emailAddressPersistence: EmailAddressPersistence["Service"] },
|
|
146
148
|
) => Effect.Effect<A, E, R>,
|
|
147
|
-
): Effect.Effect<A, EmailCoordinatorError<E>, R | LifecycleHooks> =>
|
|
148
|
-
Effect.gen(function* (): Effect.fn.Return<
|
|
149
|
+
): Effect.Effect<A, EmailCoordinatorError<E>, R | LifecycleHooks | NativeDatabase> =>
|
|
150
|
+
Effect.gen(function* (): Effect.fn.Return<
|
|
151
|
+
A,
|
|
152
|
+
EmailCoordinatorError<E>,
|
|
153
|
+
R | LifecycleHooks | NativeDatabase
|
|
154
|
+
> {
|
|
149
155
|
const hooks = yield* LifecycleHooks;
|
|
150
156
|
|
|
151
157
|
if (yield* hasCommitScope) return yield* EmailUnavailable.make({});
|
|
152
158
|
yield* configuration.coordinatorGuard ?? configuration.standaloneGuard;
|
|
153
159
|
|
|
160
|
+
yield* validateDrizzleStorage({ ...mapping, proof: proofMapping }).pipe(
|
|
161
|
+
Effect.mapError(() => EmailUnavailable.make({})),
|
|
162
|
+
);
|
|
163
|
+
|
|
154
164
|
const result = yield* coordinateCommit(
|
|
155
165
|
() =>
|
|
156
166
|
database.transaction((transaction) =>
|
|
@@ -180,13 +190,21 @@ export const coordinateTargetEmailRegistration = <Registration, Transaction, A,
|
|
|
180
190
|
transaction: Transaction,
|
|
181
191
|
services: { readonly registrationAuthority: EmailRegistrationAuthority<Registration> },
|
|
182
192
|
) => Effect.Effect<A, E, R>,
|
|
183
|
-
): Effect.Effect<A, EmailCoordinatorError<E>, R | LifecycleHooks> =>
|
|
184
|
-
Effect.gen(function* (): Effect.fn.Return<
|
|
193
|
+
): Effect.Effect<A, EmailCoordinatorError<E>, R | LifecycleHooks | NativeDatabase> =>
|
|
194
|
+
Effect.gen(function* (): Effect.fn.Return<
|
|
195
|
+
A,
|
|
196
|
+
EmailCoordinatorError<E>,
|
|
197
|
+
R | LifecycleHooks | NativeDatabase
|
|
198
|
+
> {
|
|
185
199
|
const hooks = yield* LifecycleHooks;
|
|
186
200
|
|
|
187
201
|
if (yield* hasCommitScope) return yield* EmailUnavailable.make({});
|
|
188
202
|
yield* configuration.coordinatorGuard ?? configuration.standaloneGuard;
|
|
189
203
|
|
|
204
|
+
yield* validateDrizzleStorage({ ...mapping, proof: proofMapping }).pipe(
|
|
205
|
+
Effect.mapError(() => EmailUnavailable.make({})),
|
|
206
|
+
);
|
|
207
|
+
|
|
190
208
|
const result = yield* coordinateCommit(
|
|
191
209
|
() =>
|
|
192
210
|
database.transaction((transaction) =>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import { EmailAddressPersistence, type EmailUnavailable } from "@yielded/auth/Email";
|
|
2
3
|
import type { LifecycleHooks, HookConfigurationError } from "@yielded/auth/Hooks";
|
|
3
4
|
/* oxlint-disable no-explicit-any -- proof mapping tables are independently typed by the proof owner; this driver forwards them unchanged to the shared target. */
|
|
@@ -77,7 +78,10 @@ export const makeMysqlEmailSignInServices = <
|
|
|
77
78
|
Effect.flatMap(
|
|
78
79
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
79
80
|
(configuration) => makeTargetEmailSignInServices(mapping, configuration),
|
|
80
|
-
).pipe(
|
|
81
|
+
).pipe(
|
|
82
|
+
Effect.provideServiceEffect(CurrentEmailSql, nativeDatabase(DatabaseService)),
|
|
83
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
84
|
+
);
|
|
81
85
|
|
|
82
86
|
export const makeMysqlEmailAddressServices = <
|
|
83
87
|
S extends AnyMySqlTable,
|
|
@@ -93,7 +97,10 @@ export const makeMysqlEmailAddressServices = <
|
|
|
93
97
|
Effect.flatMap(
|
|
94
98
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
95
99
|
(configuration) => makeTargetEmailAddressServices(mapping, proofMapping, configuration),
|
|
96
|
-
).pipe(
|
|
100
|
+
).pipe(
|
|
101
|
+
Effect.provideServiceEffect(CurrentEmailSql, nativeDatabase(DatabaseService)),
|
|
102
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
103
|
+
);
|
|
97
104
|
|
|
98
105
|
export function coordinateMysqlEmailAddress<
|
|
99
106
|
D extends Database,
|
|
@@ -179,7 +186,7 @@ export function coordinateMysqlEmailAddress<
|
|
|
179
186
|
E | EmailUnavailable | HookConfigurationError | SqlError | DatabaseError,
|
|
180
187
|
Exclude<R, EmailAddressPersistence> | LifecycleHooks | DatabaseRequirements
|
|
181
188
|
> {
|
|
182
|
-
return Effect.flatMap(acquire, (database) =>
|
|
189
|
+
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
183
190
|
coordinateTargetEmailAddress<TransactionOf<D>, Out, E, Exclude<R, EmailAddressPersistence>>(
|
|
184
191
|
database,
|
|
185
192
|
options.mapping,
|
|
@@ -196,7 +203,7 @@ export function coordinateMysqlEmailAddress<
|
|
|
196
203
|
? work
|
|
197
204
|
: Effect.provideService(work, options.transaction, options.transaction.of(transaction));
|
|
198
205
|
},
|
|
199
|
-
),
|
|
206
|
+
).pipe(Effect.provideService(NativeDatabase, database)),
|
|
200
207
|
);
|
|
201
208
|
}
|
|
202
209
|
|
|
@@ -216,7 +223,10 @@ export const makeMysqlEmailRegistrationServices = <
|
|
|
216
223
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
217
224
|
(configuration) =>
|
|
218
225
|
makeTargetEmailRegistrationServices<Registration>(mapping, proofMapping, configuration),
|
|
219
|
-
).pipe(
|
|
226
|
+
).pipe(
|
|
227
|
+
Effect.provideServiceEffect(CurrentEmailSql, nativeDatabase(DatabaseService)),
|
|
228
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
229
|
+
);
|
|
220
230
|
|
|
221
231
|
export function coordinateMysqlEmailRegistration<
|
|
222
232
|
TargetId,
|
|
@@ -311,7 +321,7 @@ export function coordinateMysqlEmailRegistration<
|
|
|
311
321
|
E | EmailUnavailable | HookConfigurationError | SqlError | DatabaseError,
|
|
312
322
|
Exclude<R, TargetId> | LifecycleHooks | DatabaseRequirements
|
|
313
323
|
> {
|
|
314
|
-
return Effect.flatMap(acquire, (database) =>
|
|
324
|
+
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
315
325
|
coordinateTargetEmailRegistration<Registration, TransactionOf<D>, Out, E, Exclude<R, TargetId>>(
|
|
316
326
|
database,
|
|
317
327
|
options.mapping,
|
|
@@ -328,6 +338,6 @@ export function coordinateMysqlEmailRegistration<
|
|
|
328
338
|
? work
|
|
329
339
|
: Effect.provideService(work, options.transaction, options.transaction.of(transaction));
|
|
330
340
|
},
|
|
331
|
-
),
|
|
341
|
+
).pipe(Effect.provideService(NativeDatabase, database)),
|
|
332
342
|
);
|
|
333
343
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import {
|
|
2
3
|
ExternalIdentityMutation,
|
|
3
4
|
type ExternalIdentity,
|
|
@@ -25,6 +26,8 @@ import {
|
|
|
25
26
|
type SubjectProvisioningTables,
|
|
26
27
|
} from "./model";
|
|
27
28
|
import { Database as DatabaseService } from "./mysql-database";
|
|
29
|
+
import { nativeDatabase } from "./native-database";
|
|
30
|
+
import { validateDrizzleStorage } from "./storage-validation";
|
|
28
31
|
|
|
29
32
|
type RuntimeDatabase = EffectMysql2Database<any>;
|
|
30
33
|
const unavailable = () => IdentityUnavailable.make();
|
|
@@ -47,6 +50,11 @@ export const makeMysqlSubjectProvisioningServices = Effect.fnUntraced(function*
|
|
|
47
50
|
>(mapping: SubjectProvisioningTables<Subject, Identifier, Request, NativeId>) {
|
|
48
51
|
const database = yield* DatabaseService;
|
|
49
52
|
|
|
53
|
+
yield* validateDrizzleStorage(mapping).pipe(
|
|
54
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
55
|
+
Effect.mapError(() => IdentityUnavailable.make()),
|
|
56
|
+
);
|
|
57
|
+
|
|
50
58
|
const db = database as RuntimeDatabase;
|
|
51
59
|
const requestTable = mapping.provisioningRequest.table;
|
|
52
60
|
|
|
@@ -198,6 +206,11 @@ export const makeMysqlExternalIdentityServices = Effect.fnUntraced(function* <
|
|
|
198
206
|
>(mapping: ExternalIdentityTables<Subject, External, NativeId>) {
|
|
199
207
|
const database = yield* DatabaseService;
|
|
200
208
|
|
|
209
|
+
yield* validateDrizzleStorage(mapping).pipe(
|
|
210
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
211
|
+
Effect.mapError(() => IdentityUnavailable.make()),
|
|
212
|
+
);
|
|
213
|
+
|
|
201
214
|
const db = database as RuntimeDatabase;
|
|
202
215
|
const externalTable = mapping.externalIdentity.table;
|
|
203
216
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import type { LifecycleHooks, HookConfigurationError } from "@yielded/auth/Hooks";
|
|
2
3
|
import type { PasswordUnavailable, PasswordPreparedPersistence } from "@yielded/auth/Password";
|
|
3
4
|
import type { AnyRelations } from "drizzle-orm";
|
|
@@ -50,7 +51,10 @@ export const makeMySqlPasswordPreparedPersistenceServices = <
|
|
|
50
51
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
51
52
|
(configuration) =>
|
|
52
53
|
makeTargetPasswordPreparedPersistenceServices(mapping, configuration, proofMapping),
|
|
53
|
-
).pipe(
|
|
54
|
+
).pipe(
|
|
55
|
+
Effect.provideServiceEffect(CurrentPasswordSql, nativeDatabase(DatabaseService)),
|
|
56
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
57
|
+
);
|
|
54
58
|
|
|
55
59
|
export function coordinateMySqlPasswordPreparedPersistence<
|
|
56
60
|
TargetId,
|
|
@@ -184,7 +188,7 @@ export function coordinateMySqlPasswordPreparedPersistence<
|
|
|
184
188
|
E | PasswordUnavailable | HookConfigurationError | SqlError | DatabaseError,
|
|
185
189
|
Exclude<R, TargetId> | LifecycleHooks | DatabaseRequirements
|
|
186
190
|
> {
|
|
187
|
-
return Effect.flatMap(acquire, (database) =>
|
|
191
|
+
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
188
192
|
coordinateTargetPasswordPreparedPersistence<TransactionOf<D>, Out, E, Exclude<R, TargetId>>(
|
|
189
193
|
database,
|
|
190
194
|
options.mapping,
|
|
@@ -201,6 +205,6 @@ export function coordinateMySqlPasswordPreparedPersistence<
|
|
|
201
205
|
? work
|
|
202
206
|
: Effect.provideService(work, options.transaction, options.transaction.of(transaction));
|
|
203
207
|
},
|
|
204
|
-
),
|
|
208
|
+
).pipe(Effect.provideService(NativeDatabase, database)),
|
|
205
209
|
);
|
|
206
210
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import type { LifecycleHooks, HookConfigurationError } from "@yielded/auth/Hooks";
|
|
2
3
|
import { type PasswordUnavailable, PasswordPersistence } from "@yielded/auth/Password";
|
|
3
4
|
/* oxlint-disable no-explicit-any -- proof mapping tables are independently typed by the proof owner; this driver forwards them unchanged to the shared target. */
|
|
@@ -91,7 +92,10 @@ export const makeMysqlPasswordPersistenceServices = <
|
|
|
91
92
|
Effect.flatMap(
|
|
92
93
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
93
94
|
(configuration) => makeTargetPasswordPersistenceServices(mapping, configuration, proofMapping),
|
|
94
|
-
).pipe(
|
|
95
|
+
).pipe(
|
|
96
|
+
Effect.provideServiceEffect(CurrentPasswordSql, nativeDatabase(DatabaseService)),
|
|
97
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
98
|
+
);
|
|
95
99
|
|
|
96
100
|
export function coordinateMysqlPasswordPersistence<
|
|
97
101
|
D extends Database,
|
|
@@ -195,7 +199,7 @@ export function coordinateMysqlPasswordPersistence<
|
|
|
195
199
|
E | PasswordUnavailable | HookConfigurationError | SqlError | DatabaseError,
|
|
196
200
|
Exclude<R, PasswordPersistence> | LifecycleHooks | DatabaseRequirements
|
|
197
201
|
> {
|
|
198
|
-
return Effect.flatMap(acquire, (database) =>
|
|
202
|
+
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
199
203
|
coordinateTargetPasswordPersistence<TransactionOf<D>, Out, E, Exclude<R, PasswordPersistence>>(
|
|
200
204
|
database,
|
|
201
205
|
options.mapping,
|
|
@@ -212,7 +216,7 @@ export function coordinateMysqlPasswordPersistence<
|
|
|
212
216
|
? work
|
|
213
217
|
: Effect.provideService(work, options.transaction, options.transaction.of(transaction));
|
|
214
218
|
},
|
|
215
|
-
),
|
|
219
|
+
).pipe(Effect.provideService(NativeDatabase, database)),
|
|
216
220
|
);
|
|
217
221
|
}
|
|
218
222
|
|
|
@@ -230,7 +234,10 @@ export const makeMysqlPasswordRegistrationServices = <
|
|
|
230
234
|
Effect.flatMap(
|
|
231
235
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
232
236
|
(configuration) => makeTargetPasswordRegistrationServices<Registration>(mapping, configuration),
|
|
233
|
-
).pipe(
|
|
237
|
+
).pipe(
|
|
238
|
+
Effect.provideServiceEffect(CurrentPasswordSql, nativeDatabase(DatabaseService)),
|
|
239
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
240
|
+
);
|
|
234
241
|
|
|
235
242
|
export function coordinateMysqlPasswordRegistration<
|
|
236
243
|
TargetId,
|
|
@@ -322,7 +329,7 @@ export function coordinateMysqlPasswordRegistration<
|
|
|
322
329
|
E | PasswordUnavailable | HookConfigurationError | SqlError | DatabaseError,
|
|
323
330
|
Exclude<R, TargetId> | LifecycleHooks | DatabaseRequirements
|
|
324
331
|
> {
|
|
325
|
-
return Effect.flatMap(acquire, (database) =>
|
|
332
|
+
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
326
333
|
coordinateTargetPasswordRegistration<
|
|
327
334
|
Registration,
|
|
328
335
|
TransactionOf<D>,
|
|
@@ -344,7 +351,7 @@ export function coordinateMysqlPasswordRegistration<
|
|
|
344
351
|
? work
|
|
345
352
|
: Effect.provideService(work, options.transaction, options.transaction.of(transaction));
|
|
346
353
|
},
|
|
347
|
-
),
|
|
354
|
+
).pipe(Effect.provideService(NativeDatabase, database)),
|
|
348
355
|
);
|
|
349
356
|
}
|
|
350
357
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
2
|
import type { LifecycleHooks, HookConfigurationError } from "@yielded/auth/Hooks";
|
|
2
3
|
import { type ProofUnavailable, ProofPersistence } from "@yielded/auth/Proofs";
|
|
3
4
|
import type { AnyRelations } from "drizzle-orm";
|
|
@@ -70,7 +71,10 @@ export const makeMysqlProofPersistenceServices = <
|
|
|
70
71
|
Effect.flatMap(
|
|
71
72
|
Effect.map(acquireTransactionService(DatabaseService), configuration),
|
|
72
73
|
(configuration) => makeTargetProofPersistenceServices(mapping, configuration),
|
|
73
|
-
).pipe(
|
|
74
|
+
).pipe(
|
|
75
|
+
Effect.provideServiceEffect(CurrentProofSql, nativeDatabase(DatabaseService)),
|
|
76
|
+
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(DatabaseService)),
|
|
77
|
+
);
|
|
74
78
|
|
|
75
79
|
export function coordinateMysqlProofPersistence<
|
|
76
80
|
D extends Database,
|
|
@@ -171,7 +175,7 @@ export function coordinateMysqlProofPersistence<
|
|
|
171
175
|
E | ProofUnavailable | HookConfigurationError | SqlError | DatabaseError,
|
|
172
176
|
Exclude<R, ProofPersistence> | LifecycleHooks | DatabaseRequirements
|
|
173
177
|
> {
|
|
174
|
-
return Effect.flatMap(acquire, (database) =>
|
|
178
|
+
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
175
179
|
coordinateTargetProofPersistence<Transaction, A, E, Exclude<R, ProofPersistence>>(
|
|
176
180
|
database,
|
|
177
181
|
options.mapping,
|
|
@@ -187,6 +191,6 @@ export function coordinateMysqlProofPersistence<
|
|
|
187
191
|
? work
|
|
188
192
|
: Effect.provideService(work, options.transaction, options.transaction.of(transaction));
|
|
189
193
|
},
|
|
190
|
-
),
|
|
194
|
+
).pipe(Effect.provideService(NativeDatabase, database)),
|
|
191
195
|
);
|
|
192
196
|
}
|