@yielded/auth-persistence 0.1.0-beta.11 → 0.1.0-beta.12
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/README.md +6 -1
- package/dist/Adapter.d.mts +2 -2
- package/dist/Adapter.mjs +4 -4
- package/dist/internal/PasswordPreparedPostconditions.d.mts +1 -1
- package/dist/internal/configuration.d.mts +1 -1
- package/dist/internal/email-kernel.d.mts +5 -5
- package/dist/internal/email-kernel.mjs +8 -6
- package/dist/internal/email-kernel.mjs.map +1 -1
- package/dist/internal/models/email-model.d.mts +1 -1
- package/dist/internal/oauth-server.mjs +1 -1
- package/dist/internal/oauth-server.mjs.map +1 -1
- package/dist/internal/passkey/target.mjs +21 -12
- package/dist/internal/passkey/target.mjs.map +1 -1
- package/dist/internal/passkey/write-target.mjs +6 -6
- package/dist/internal/passkey/write-target.mjs.map +1 -1
- package/dist/internal/passkey-kernel.d.mts +9 -12
- package/dist/internal/passkeys.mjs +8 -7
- package/dist/internal/passkeys.mjs.map +1 -1
- package/dist/internal/password-kernel.d.mts +3 -3
- package/dist/internal/password-kernel.mjs +3 -2
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +3 -2
- package/dist/internal/persistence.mjs +22 -26
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/proof-kernel.d.mts +2 -2
- package/dist/internal/proof-kernel.mjs +2 -1
- package/dist/internal/proof-kernel.mjs.map +1 -1
- package/dist/internal/registration.mjs +6 -1
- package/dist/internal/registration.mjs.map +1 -1
- package/dist/internal/session-kernel.d.mts +19 -9
- package/dist/internal/session-kernel.mjs +155 -147
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-persistence.mjs +2 -3
- package/dist/internal/sql-persistence.mjs.map +1 -1
- package/dist/internal/sql-query.mjs +4 -2
- package/dist/internal/sql-query.mjs.map +1 -1
- package/dist/internal/standalone.d.mts +2 -4
- package/dist/internal/standalone.mjs +2 -2
- package/dist/internal/standalone.mjs.map +1 -1
- package/dist/internal/storage-mapping.mjs +2 -2
- package/dist/internal/storage-validation.mjs +4 -2
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/transaction-execution-kernel.d.mts +6 -7
- package/dist/internal/transaction-execution-kernel.mjs +9 -7
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
- package/dist/internal/transaction-kernel.d.mts +7 -3
- package/dist/internal/transaction-kernel.mjs +5 -2
- package/dist/internal/transaction-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +6 -1
- package/src/internal/email-kernel.ts +30 -35
- package/src/internal/oauth-server.ts +1 -1
- package/src/internal/passkey/target.ts +36 -43
- package/src/internal/passkey/write-target.ts +4 -14
- package/src/internal/passkeys.ts +4 -9
- package/src/internal/password-kernel.ts +2 -2
- package/src/internal/persistence.ts +77 -68
- package/src/internal/proof-kernel.ts +2 -2
- package/src/internal/registration.ts +22 -7
- package/src/internal/session-kernel.ts +1122 -1139
- package/src/internal/sql-persistence.ts +1 -1
- package/src/internal/sql-query.ts +5 -3
- package/src/internal/standalone.ts +5 -7
- package/src/internal/storage-validation.ts +20 -16
- package/src/internal/transaction-execution-kernel.ts +17 -15
- package/src/internal/transaction-kernel.ts +7 -1
|
@@ -27,14 +27,23 @@ import {
|
|
|
27
27
|
type PasskeyRequirement,
|
|
28
28
|
type MappingInput,
|
|
29
29
|
} from "./configuration";
|
|
30
|
-
import { makeEmailKernel, type EmailSqlDatabase } from "./email-kernel";
|
|
30
|
+
import { makeEmailKernel, CurrentEmailSql, type EmailSqlDatabase } from "./email-kernel";
|
|
31
31
|
import { PersistenceMappingError } from "./mapping-error";
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
makePasswordKernel,
|
|
34
|
+
CurrentPasswordSql,
|
|
35
|
+
type PasswordSqlDatabase,
|
|
36
|
+
} from "./password-kernel";
|
|
33
37
|
import { makePhoneKernel, CurrentPhoneTransaction } from "./phone-kernel";
|
|
34
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
makeProofKernel,
|
|
40
|
+
CurrentProofSql,
|
|
41
|
+
type ProofSqlDatabase,
|
|
42
|
+
type ProofSqlQuery,
|
|
43
|
+
} from "./proof-kernel";
|
|
35
44
|
import type { QueryOperations } from "./query-operations";
|
|
36
|
-
import { makeRegistrationAuthority
|
|
37
|
-
import { makeSessionKernel, type SessionSqlDatabase } from "./session-kernel";
|
|
45
|
+
import { makeRegistrationAuthority } from "./registration";
|
|
46
|
+
import { makeSessionKernel, CurrentSessionSql, type SessionSqlDatabase } from "./session-kernel";
|
|
38
47
|
import { requireStandalone } from "./standalone";
|
|
39
48
|
import { makeMappings } from "./storage-mapping";
|
|
40
49
|
import {
|
|
@@ -45,7 +54,11 @@ import {
|
|
|
45
54
|
} from "./storage-tables";
|
|
46
55
|
import { validateStorage } from "./storage-validation";
|
|
47
56
|
import { makeTransactionExecutionKernel } from "./transaction-execution-kernel";
|
|
48
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
makeTransactionKernel,
|
|
59
|
+
NativeDatabase,
|
|
60
|
+
type TransactionNativeDatabase,
|
|
61
|
+
} from "./transaction-kernel";
|
|
49
62
|
|
|
50
63
|
export interface Backend<T extends object, R> {
|
|
51
64
|
readonly makeTable: (definition: StorageTable) => T;
|
|
@@ -53,7 +66,6 @@ export interface Backend<T extends object, R> {
|
|
|
53
66
|
readonly acquire: Effect.Effect<object, PersistenceConfigurationError, R | SqlClient.SqlClient>;
|
|
54
67
|
readonly operations: QueryOperations;
|
|
55
68
|
readonly passkeys: (input: {
|
|
56
|
-
readonly database: object;
|
|
57
69
|
readonly storage: MappingInput;
|
|
58
70
|
readonly namespace: string;
|
|
59
71
|
readonly dialect: "pg" | "sqlite";
|
|
@@ -62,7 +74,7 @@ export interface Backend<T extends object, R> {
|
|
|
62
74
|
}) => Effect.Effect<
|
|
63
75
|
Context.Context<never>,
|
|
64
76
|
PersistenceConfigurationError,
|
|
65
|
-
PasskeyConfig | LifecycleHooks | SqlClient.SqlClient
|
|
77
|
+
PasskeyConfig | LifecycleHooks | SqlClient.SqlClient | NativeDatabase | CurrentProofSql
|
|
66
78
|
>;
|
|
67
79
|
}
|
|
68
80
|
|
|
@@ -165,14 +177,6 @@ export const createPersistence = <T extends object, R>(
|
|
|
165
177
|
`@yielded/auth-persistence/${auth.namespace}/Provisioning`,
|
|
166
178
|
);
|
|
167
179
|
|
|
168
|
-
// Capability metadata controls the conditional requirement, and core has
|
|
169
|
-
// already decoded each registration with its strategy's Schema.
|
|
170
|
-
const provisioners = management
|
|
171
|
-
? ProvisioningKey.pipe(
|
|
172
|
-
Effect.map((value) => value as Readonly<Record<string, CreateSubject>>),
|
|
173
|
-
)
|
|
174
|
-
: Effect.succeed<Readonly<Record<string, CreateSubject>>>({});
|
|
175
|
-
|
|
176
180
|
const layout = <N, Instant>(
|
|
177
181
|
options: {
|
|
178
182
|
readonly subjects: SubjectOptions<T, N>;
|
|
@@ -317,14 +321,15 @@ export const createPersistence = <T extends object, R>(
|
|
|
317
321
|
if (columns[name] === undefined)
|
|
318
322
|
return yield* configError(`Missing ${role}.${name} column`);
|
|
319
323
|
}
|
|
320
|
-
yield* validateStorage(
|
|
324
|
+
yield* validateStorage(dialect, description, storageTables[role].unique);
|
|
321
325
|
}
|
|
322
|
-
yield* validateStorage(
|
|
326
|
+
yield* validateStorage(dialect, backend.describe(storage.subjects.table as T), [
|
|
323
327
|
[storage.subjects.id],
|
|
324
328
|
]);
|
|
325
329
|
const mappings = makeMappings(storage);
|
|
326
330
|
|
|
327
|
-
const standalone = <E>(error: () => E) =>
|
|
331
|
+
const standalone = <E>(error: () => E) =>
|
|
332
|
+
requireStandalone(error, client.transactionService);
|
|
328
333
|
|
|
329
334
|
// Backend validation owns the foreign query-builder shape, never the decoded rows.
|
|
330
335
|
const native = database as ProofSqlDatabase &
|
|
@@ -341,17 +346,16 @@ export const createPersistence = <T extends object, R>(
|
|
|
341
346
|
|
|
342
347
|
const sessionMapping = mappings.sessions(auth.claims);
|
|
343
348
|
|
|
344
|
-
const sessionServices = yield* sessionKernel
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
options,
|
|
348
|
-
);
|
|
349
|
+
const sessionServices = yield* sessionKernel
|
|
350
|
+
.makeSqlStatefulSessions(sessionMapping, options)
|
|
351
|
+
.pipe(Effect.provideService(CurrentSessionSql, native));
|
|
349
352
|
|
|
350
|
-
const authority = yield* sessionKernel
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
const authority = yield* sessionKernel
|
|
354
|
+
.makeSqlAuthenticationAuthority<C["Type"]>(
|
|
355
|
+
{ ...mappings.authority(), isConstraintConflict: () => false },
|
|
356
|
+
options,
|
|
357
|
+
)
|
|
358
|
+
.pipe(Effect.provideService(CurrentSessionSql, native));
|
|
355
359
|
|
|
356
360
|
let context: Context.Context<never> = Context.make(AuthenticationAuthority, authority).pipe(
|
|
357
361
|
Context.add(
|
|
@@ -377,47 +381,40 @@ export const createPersistence = <T extends object, R>(
|
|
|
377
381
|
context = Context.add(
|
|
378
382
|
context,
|
|
379
383
|
ProofPersistence,
|
|
380
|
-
yield* proofKernel
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
proofConfiguration.configuration,
|
|
384
|
-
),
|
|
384
|
+
yield* proofKernel
|
|
385
|
+
.makeSqlProofPersistence(proofConfiguration.mapping, proofConfiguration.configuration)
|
|
386
|
+
.pipe(Effect.provideService(CurrentProofSql, native)),
|
|
385
387
|
);
|
|
386
388
|
}
|
|
387
389
|
|
|
388
390
|
if (password) {
|
|
389
|
-
const persistence = yield* passwordKernel
|
|
390
|
-
|
|
391
|
-
mappings.passwords(),
|
|
392
|
-
{
|
|
391
|
+
const persistence = yield* passwordKernel
|
|
392
|
+
.makeSqlPasswordPersistence(mappings.passwords(), {
|
|
393
393
|
mode: "interactive",
|
|
394
394
|
locking: dialect === "pg",
|
|
395
395
|
standaloneGuard: standalone(() => PasswordUnavailable.make({})),
|
|
396
396
|
insertIfAbsent: (query) => query.onConflictDoNothing(),
|
|
397
397
|
proof: proofConfiguration,
|
|
398
|
-
}
|
|
399
|
-
|
|
398
|
+
})
|
|
399
|
+
.pipe(Effect.provideService(CurrentPasswordSql, native));
|
|
400
400
|
|
|
401
401
|
context = Context.add(context, PasswordPersistence, persistence);
|
|
402
402
|
}
|
|
403
|
-
const creators = yield* provisioners;
|
|
404
|
-
|
|
405
403
|
for (const [name, strategy] of Object.entries(auth.strategies)) {
|
|
406
404
|
if (strategy.persistence?.kind !== "password" || !strategy.persistence.management)
|
|
407
405
|
continue;
|
|
408
406
|
const key = strategy.RegistrationAuthority;
|
|
409
|
-
const create = creators[name];
|
|
410
407
|
|
|
411
|
-
if (key === undefined
|
|
408
|
+
if (key === undefined)
|
|
412
409
|
return yield* configError(`Missing subject provisioning for ${name}`);
|
|
413
410
|
|
|
414
411
|
const registration = yield* makeRegistrationAuthority(
|
|
415
|
-
native,
|
|
416
412
|
mappings,
|
|
417
413
|
backend.operations,
|
|
418
414
|
standalone(() => PasswordUnavailable.make({})),
|
|
419
|
-
|
|
420
|
-
|
|
415
|
+
ProvisioningKey,
|
|
416
|
+
name,
|
|
417
|
+
).pipe(Effect.provideService(CurrentPasswordSql, native));
|
|
421
418
|
|
|
422
419
|
context = Context.add(context, key, registration);
|
|
423
420
|
}
|
|
@@ -425,12 +422,14 @@ export const createPersistence = <T extends object, R>(
|
|
|
425
422
|
context = Context.add(
|
|
426
423
|
context,
|
|
427
424
|
EmailAddressPersistence,
|
|
428
|
-
yield* emailKernel
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
425
|
+
yield* emailKernel
|
|
426
|
+
.makeSqlEmailAddressPersistence(mappings.emails(), {
|
|
427
|
+
mode: "interactive",
|
|
428
|
+
locking: dialect === "pg",
|
|
429
|
+
standaloneGuard: standalone(() => EmailUnavailable.make({})),
|
|
430
|
+
proof: proofConfiguration,
|
|
431
|
+
})
|
|
432
|
+
.pipe(Effect.provideService(CurrentEmailSql, native)),
|
|
434
433
|
);
|
|
435
434
|
}
|
|
436
435
|
if (phone) {
|
|
@@ -486,18 +485,19 @@ export const createPersistence = <T extends object, R>(
|
|
|
486
485
|
encodeInstant: storage.encodeInstant,
|
|
487
486
|
});
|
|
488
487
|
|
|
489
|
-
const execution = executionKernel
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
488
|
+
const execution = yield* executionKernel
|
|
489
|
+
.makeTransactionExecution(
|
|
490
|
+
CurrentPhoneTransaction,
|
|
491
|
+
{
|
|
492
|
+
mode: "interactive",
|
|
493
|
+
dialect,
|
|
494
|
+
locking: dialect === "pg",
|
|
495
|
+
standaloneGuard: () => standalone(() => PhoneOtpUnavailable.make({})),
|
|
496
|
+
},
|
|
497
|
+
() => PhoneOtpUnavailable.make({}),
|
|
498
|
+
randomId,
|
|
499
|
+
)
|
|
500
|
+
.pipe(Effect.provideService(NativeDatabase, native));
|
|
501
501
|
|
|
502
502
|
const run = <Value, E, Requirements>(
|
|
503
503
|
work: Effect.Effect<Value, E, Requirements>,
|
|
@@ -538,7 +538,6 @@ export const createPersistence = <T extends object, R>(
|
|
|
538
538
|
if (passkeys.length > 0) {
|
|
539
539
|
// Auth capability metadata determines whether PasskeyConfig is required.
|
|
540
540
|
const services = backend.passkeys({
|
|
541
|
-
database,
|
|
542
541
|
storage,
|
|
543
542
|
namespace: auth.namespace,
|
|
544
543
|
dialect,
|
|
@@ -549,10 +548,20 @@ export const createPersistence = <T extends object, R>(
|
|
|
549
548
|
}) as Effect.Effect<
|
|
550
549
|
Context.Context<never>,
|
|
551
550
|
PersistenceConfigurationError,
|
|
552
|
-
PasskeyRequirement<A>
|
|
551
|
+
| PasskeyRequirement<A>
|
|
552
|
+
| LifecycleHooks
|
|
553
|
+
| SqlClient.SqlClient
|
|
554
|
+
| NativeDatabase
|
|
555
|
+
| CurrentProofSql
|
|
553
556
|
>;
|
|
554
557
|
|
|
555
|
-
context = Context.merge(
|
|
558
|
+
context = Context.merge(
|
|
559
|
+
context,
|
|
560
|
+
yield* services.pipe(
|
|
561
|
+
Effect.provideService(NativeDatabase, native),
|
|
562
|
+
Effect.provideService(CurrentProofSql, native),
|
|
563
|
+
),
|
|
564
|
+
);
|
|
556
565
|
}
|
|
557
566
|
|
|
558
567
|
// The checked capability metadata above determines exactly these service keys.
|
|
@@ -659,10 +659,10 @@ export const makeProofKernel = <
|
|
|
659
659
|
};
|
|
660
660
|
|
|
661
661
|
const makeSqlProofPersistence = Effect.fn("makeSqlProofPersistence")(function* (
|
|
662
|
-
database: Database,
|
|
663
662
|
mapping: Mapping,
|
|
664
663
|
configuration: ProofSqlConfiguration,
|
|
665
|
-
): Effect.fn.Return<ProofPersistence["Service"], never, LifecycleHooks> {
|
|
664
|
+
): Effect.fn.Return<ProofPersistence["Service"], never, LifecycleHooks | CurrentProofSql> {
|
|
665
|
+
const database = yield* CurrentProofSql;
|
|
666
666
|
const hooks = yield* LifecycleHooks;
|
|
667
667
|
|
|
668
668
|
return ProofPersistence.of({
|
|
@@ -5,16 +5,17 @@ import { PasswordUnavailable } from "@yielded/auth/Password";
|
|
|
5
5
|
import { reportPersistenceFailure } from "@yielded/auth/Persistence";
|
|
6
6
|
import type { SubjectId } from "@yielded/auth/Schema";
|
|
7
7
|
import { SecurityRevision } from "@yielded/auth/Sessions";
|
|
8
|
-
import { Effect, Schema } from "effect";
|
|
8
|
+
import { type Context, Effect, Schema } from "effect";
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { PersistenceConfigurationError } from "./configuration";
|
|
11
|
+
import { CurrentPasswordSql } from "./password-kernel";
|
|
11
12
|
import type { QueryOperations } from "./query-operations";
|
|
12
13
|
import type { PasswordRegistrationAuthority } from "./registration-contract";
|
|
13
14
|
import type { makeMappings } from "./storage-mapping";
|
|
14
15
|
|
|
15
16
|
class IdentifierTaken extends Schema.TaggedError<IdentifierTaken>()("IdentifierTaken", {}) {}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
type CreateSubject = (input: {
|
|
18
19
|
readonly identifier: LoginIdentifier;
|
|
19
20
|
readonly registration: unknown;
|
|
20
21
|
}) => Effect.Effect<SubjectId, PasswordUnavailable>;
|
|
@@ -23,14 +24,28 @@ export type CreateSubject = (input: {
|
|
|
23
24
|
* creates the unverified identifier and password in one SQL transaction. Replays
|
|
24
25
|
* always suppress; a public request ID never recovers a private password intent.
|
|
25
26
|
*/
|
|
26
|
-
export const makeRegistrationAuthority = Effect.fn("makeRegistrationAuthority")(function* (
|
|
27
|
-
database: PasswordSqlDatabase,
|
|
27
|
+
export const makeRegistrationAuthority = Effect.fn("makeRegistrationAuthority")(function* <R>(
|
|
28
28
|
mappings: ReturnType<typeof makeMappings>,
|
|
29
29
|
operations: QueryOperations,
|
|
30
30
|
standalone: Effect.Effect<void, PasswordUnavailable>,
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
provisioning: Context.Key<R, object>,
|
|
32
|
+
strategy: string,
|
|
33
|
+
): Effect.fn.Return<
|
|
34
|
+
PasswordRegistrationAuthority<unknown>,
|
|
35
|
+
PersistenceConfigurationError,
|
|
36
|
+
LifecycleHooks | CurrentPasswordSql | R
|
|
37
|
+
> {
|
|
38
|
+
const database = yield* CurrentPasswordSql;
|
|
33
39
|
const hooks = yield* LifecycleHooks;
|
|
40
|
+
// Core has already decoded registration with the selected strategy's Schema;
|
|
41
|
+
// the dynamic strategy table erases only that heterogeneous callback signature.
|
|
42
|
+
const creators = (yield* provisioning) as Readonly<Record<string, CreateSubject>>;
|
|
43
|
+
const createSubject = creators[strategy];
|
|
44
|
+
|
|
45
|
+
if (createSubject === undefined)
|
|
46
|
+
return yield* PersistenceConfigurationError.make({
|
|
47
|
+
reason: `Missing subject provisioning for ${strategy}`,
|
|
48
|
+
});
|
|
34
49
|
const { and, eq, column } = operations;
|
|
35
50
|
const mapping = mappings.passwords();
|
|
36
51
|
const receipts = mappings.table("passwordRegistrations");
|