@yielded/auth-persistence 0.1.0-beta.17 → 0.1.0-beta.19
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 +5 -2
- package/dist/Adapter.d.mts +2 -2
- package/dist/Adapter.mjs +2 -2
- package/dist/internal/configuration.d.mts +5 -4
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/crypto.mjs +12 -0
- package/dist/internal/crypto.mjs.map +1 -0
- package/dist/internal/email-kernel.d.mts +7 -7
- package/dist/internal/passkey/admission.mjs +19 -15
- package/dist/internal/passkey/admission.mjs.map +1 -1
- package/dist/internal/passkey/credentials.mjs +4 -4
- package/dist/internal/passkey/credentials.mjs.map +1 -1
- package/dist/internal/passkey/enrollment.mjs +6 -6
- package/dist/internal/passkey/enrollment.mjs.map +1 -1
- package/dist/internal/passkey/flow.mjs +7 -7
- package/dist/internal/passkey/flow.mjs.map +1 -1
- package/dist/internal/passkey/management.mjs +2 -2
- package/dist/internal/passkey/management.mjs.map +1 -1
- package/dist/internal/passkey/registration-ceremony.mjs +2 -2
- package/dist/internal/passkey/registration-ceremony.mjs.map +1 -1
- package/dist/internal/passkey/registration-custody.mjs +1 -1
- package/dist/internal/passkey/registration-custody.mjs.map +1 -1
- package/dist/internal/passkey/registration-write.mjs +12 -12
- package/dist/internal/passkey/registration-write.mjs.map +1 -1
- package/dist/internal/passkey/state.d.mts +1 -1
- package/dist/internal/passkey/state.mjs +8 -9
- package/dist/internal/passkey/state.mjs.map +1 -1
- package/dist/internal/passkey/target.d.mts +1 -1
- package/dist/internal/passkey/target.mjs.map +1 -1
- package/dist/internal/passkey/write-state.mjs +13 -13
- package/dist/internal/passkey/write-state.mjs.map +1 -1
- package/dist/internal/passkey/write-target.mjs.map +1 -1
- package/dist/internal/passkey-kernel.d.mts +705 -249
- package/dist/internal/passkeys.mjs +32 -14
- package/dist/internal/passkeys.mjs.map +1 -1
- package/dist/internal/password-kernel.d.mts +6 -6
- package/dist/internal/password-kernel.mjs +1 -1
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +2 -2
- package/dist/internal/persistence.mjs +18 -9
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/phone-kernel.d.mts +7 -7
- package/dist/internal/phone-kernel.mjs +14 -14
- package/dist/internal/phone-kernel.mjs.map +1 -1
- package/dist/internal/registration.mjs +8 -7
- package/dist/internal/registration.mjs.map +1 -1
- package/dist/internal/session-kernel.mjs +2 -1
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-query.mjs +1 -1
- package/dist/internal/sql-query.mjs.map +1 -1
- package/dist/internal/storage-mapping.d.mts +5 -4
- package/dist/internal/storage-mapping.mjs +16 -13
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-validation.d.mts +13 -3
- package/dist/internal/storage-validation.mjs +128 -68
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/transaction-execution-kernel.d.mts +4 -4
- package/dist/internal/transaction-execution-kernel.mjs +7 -5
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +7 -1
- package/src/internal/configuration.ts +5 -3
- package/src/internal/crypto.ts +15 -0
- package/src/internal/passkey/admission.ts +22 -15
- package/src/internal/passkey/credentials.ts +4 -4
- package/src/internal/passkey/enrollment.ts +7 -7
- package/src/internal/passkey/flow.ts +8 -8
- package/src/internal/passkey/management.ts +7 -2
- package/src/internal/passkey/registration-ceremony.ts +2 -2
- package/src/internal/passkey/registration-custody.ts +1 -1
- package/src/internal/passkey/registration-write.ts +12 -12
- package/src/internal/passkey/state.ts +10 -13
- package/src/internal/passkey/target.ts +16 -8
- package/src/internal/passkey/write-state.ts +10 -14
- package/src/internal/passkey/write-target.ts +11 -3
- package/src/internal/passkeys.ts +121 -70
- package/src/internal/password-kernel.ts +2 -2
- package/src/internal/persistence.ts +30 -13
- package/src/internal/phone-kernel.ts +26 -17
- package/src/internal/registration.ts +10 -8
- package/src/internal/session-kernel.ts +3 -2
- package/src/internal/sql-query.ts +3 -2
- package/src/internal/storage-mapping.ts +21 -13
- package/src/internal/storage-validation.ts +219 -139
- package/src/internal/transaction-execution-kernel.ts +30 -11
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Direct Effect SQL persistence and shared storage contracts for `@yielded/auth`.
|
|
4
4
|
|
|
5
|
-
Import the named `AuthPersistence` facade and provide an explicit Effect SQL client
|
|
5
|
+
Import the named `AuthPersistence` facade and provide an explicit Effect SQL client
|
|
6
|
+
and Effect `Crypto` to its Layer. Managed storage requires a stable `prefix`; use
|
|
7
|
+
the existing table prefix when connecting an existing database.
|
|
6
8
|
Drizzle integrations live in `@yielded/auth-persistence-drizzle`; this package has
|
|
7
9
|
no Drizzle dependency or exports. Core workflows and replaceable service contracts
|
|
8
10
|
remain in `@yielded/auth`.
|
|
@@ -19,7 +21,8 @@ and changes, phone sign-in, and passkey sign-in and management with stateful ses
|
|
|
19
21
|
on PostgreSQL and SQLite. The `/Adapter` module exposes the shared mapping contracts
|
|
20
22
|
and transaction kernels used by companion adapters.
|
|
21
23
|
Its constructors acquire their database through the matching `Current*Sql` or
|
|
22
|
-
`NativeDatabase` service
|
|
24
|
+
`NativeDatabase` service, alongside Effect `Crypto` where required. Supply these
|
|
25
|
+
services during construction;
|
|
23
26
|
transaction coordinators supply their exact transaction when constructing bound
|
|
24
27
|
services. Keep root database provision outside later operations, where the
|
|
25
28
|
current SQL service identifies an active transaction.
|
package/dist/Adapter.d.mts
CHANGED
|
@@ -28,5 +28,5 @@ import { createPersistence } from "./internal/persistence.mjs";
|
|
|
28
28
|
import { makeComposedPasskeys } from "./internal/passkeys.mjs";
|
|
29
29
|
import { makeMappings } from "./internal/storage-mapping.mjs";
|
|
30
30
|
import { PasswordRegistrationAuthority } from "./internal/registration-contract.mjs";
|
|
31
|
-
import { PhysicalStorageTable, validateStorage } from "./internal/storage-validation.mjs";
|
|
32
|
-
export { type AnyEmailAddressMapping, type AnyEmailRegistrationMapping, type AnyEmailSignInMapping, type AnyPasswordPersistenceMapping, type AnyPasswordRegistrationMapping, type AnyProofPersistenceMapping, type AuthenticationAuthorityMapping, type BoundPersistence, type ClaimsCodec, type CurrentAddress, CurrentEmailSql, CurrentPasskeyTransaction, CurrentPasswordPreparedTransaction, CurrentPasswordSql, CurrentPhoneTransaction, CurrentProofSql, CurrentSessionSql, type D1AuthenticationAuthorityMapping, type D1EmailAddressMapping, type D1EmailRegistrationMapping, type D1PasskeyMapping, type D1PasswordPersistenceMapping, type D1PendingAuthenticationMapping, type D1ProofPersistenceMapping, type D1SessionClockMapping, type D1SessionStepUpMapping, type D1SignedSessionValidityMapping, type D1StatefulSessionMapping, type Definition, type EmailAddressMapping, type EmailAuthorityCredentialTable, type EmailCommandTable, type EmailCredentialReadTable, type EmailCredentialTable, type EmailD1Clock, type EmailIdentifierReadTable, type EmailIdentifierTable, type EmailRegistrationAuthorityCredentialTable, type EmailRegistrationCredentialTable, type EmailRegistrationIdentifierTable, type EmailRegistrationIntent, type EmailRegistrationMapping, type EmailRegistrationProvisioning, type EmailRegistrationState, type EmailRegistrationTable, type EmailSignInMapping, type EmailSqlConfiguration, type EmailSqlDatabase, type EmailSqlQuery, type EmailSubjectReadTable, type EmailSubjectTable, type GuardedUpdate, NativeDatabase, type Observation, type PasskeyAdmissionTable, type PasskeyCeremonyMapping, type PasskeyChargeInsert, type PasskeyChargeKind, type PasskeyChargeTable, type PasskeyClock, type PasskeyColumn, type PasskeyCommandTable, type PasskeyCoordinatorError, type PasskeyCredentialInsert, type PasskeyCredentialMapping, type PasskeyCredentialOwnershipTable, type PasskeyCredentialReadTable, type PasskeyCredentialServices, type PasskeyEnrollmentContextMapping, type PasskeyEnrollmentContextServices, type PasskeyExecution, type PasskeyFactorReadTable, type PasskeyFlowInsert, type PasskeyFlowState, type PasskeyFlowTable, type PasskeyHandleOwnershipTable, type PasskeyHandleReadTable, type PasskeyHandleReservationTable, type PasskeyInvalidationInput, type PasskeyInvalidationMutation, type PasskeyKernel, type PasskeyManagementMapping, type PasskeyManagementServices, type PasskeyMappingSource, type PasskeyModuleTable, type PasskeyOwner, type PasskeyPersistenceMapping, type PasskeyPersistenceServices, type PasskeyPolicyGuard, type PasskeyRegistrationCeremonyCapabilities, type PasskeyRegistrationCeremonyMapping, type PasskeyRegistrationCeremonyServices, type PasskeyRegistrationIntentReadTable, type PasskeyRegistrationMapping, type PasskeyRegistrationServices, type PasskeyRegistrationWriter, type PasskeySubjectIdCodec, type PasskeySubjectReadTable, type PasskeyTargetConfiguration, type PasskeyWriteTables, type PasswordAttemptAction, type PasswordAttemptRecord, type PasswordAttemptState, type PasswordAttemptTable, type PasswordAuthorityCredentialTable, type PasswordChargeTable, type PasswordCommandTable, type PasswordConstraintClassifier, type PasswordCredentialTable, type PasswordD1Clock, type PasswordIdentifierTable, type PasswordPersistenceMapping, PasswordPreparedJournalGuards, type PasswordPreparedPostcondition, PasswordPreparedPostconditions, type PasswordRateScopeKind, type PasswordRateScopeTable, type PasswordRegistrationAuthority, type PasswordRegistrationConstraintClassifier, type PasswordRegistrationIntent, type PasswordRegistrationMapping, type PasswordRegistrationProvisioning, type PasswordRegistrationState, type PasswordRegistrationTable, type PasswordScopeKeys, type PasswordSqlConfiguration, type PasswordSqlDatabase, type PasswordSqlQuery, type PasswordSubjectTable, type PendingAuthenticationMapping, type PendingAuthenticationTables, type PersistenceApi, PersistenceConfigurationError, PersistenceMappingError, type PhysicalStorageTable, type ProofAbuseEventTable, type ProofAction, type ProofAuthorityInput, type ProofAuthorityTables, type ProofCommandDecision, type ProofCommandKind, type ProofCommandTable, type ProofConstraintClassifier, type ProofContinuationRecord, type ProofContinuationTable, type ProofD1Clock, type ProofDeliveryState, type ProofFailureEventTable, type ProofGenerationState, type ProofGenerationTable, type ProofPersistenceMapping, type ProofRateScopeTable, type ProofRequestTable, type ProofScopeKeys, type ProofScopeKind, type ProofSeriesTable, type ProofSqlConfiguration, type ProofSqlDatabase, type ProofSqlQuery, type QueryFailure, type QueryOperations, type RequiredEmailAddressConstraints, type RequiredEmailRegistrationConstraints, type RequiredEmailSignInConstraints, type RequiredPasswordConstraints, type RequiredPasswordRegistrationConstraints, type RequiredPendingAuthenticationConstraints, type RequiredProofConstraints, type RequiredSessionConstraints, type RequiredSessionStepUpConstraints, type RequiredSignedValidityConstraints, type RequiredStatefulPendingConstraints, type Row, type SessionAuthorityTables, type SessionConstraintClassifier, type SessionFlowTables, type SessionIdCodec, type SessionSqlDatabase, type SessionSqlOptions, type SessionStepUpIntentTables, type SessionStepUpMapping, type SessionStepUpSourceTables, type SessionSubjectTables, type SignedSessionValidityMapping, type SignedSessionValidityTables, type SqlColumn, type SqlExpression, type SqlFragment, type StatefulSessionMapping, type StatefulSessionTables, type StorageTable, type SubjectIdCodec, type TableModel, type TransactionBound, type TransactionCoordinatorError, type TransactionExecution, type TransactionNativeDatabase, type TransactionOwner, type TransactionTargetConfiguration, createPersistence, decodeStepUpIntent, encodeStepUpIntent, isMappedConstraintConflict, makeComposedPasskeys, makeEmailKernel, makePasskeyKernel, makePasswordKernel, makePhoneKernel, makeProofKernel, makeSessionKernel, makeMappings as makeStorageMappings, makeTransactionExecutionKernel, makeTransactionKernel, passkeyCredentialsLayer, passkeyEnrollmentContextLayer, passkeyInvalidationMutation, passkeyPersistenceLayer, passkeyPolicyGuard, requireStandalone, requiredEmailAddressConstraints, requiredEmailRegistrationConstraints, requiredEmailSignInConstraints, requiredPasskeyCredentialConstraints, requiredPasskeyManagementConstraints, requiredPasskeyPersistenceConstraints, requiredPasskeyRegistrationCeremonyConstraints, requiredPasskeyRegistrationWriteConstraints, requiredPasswordConstraints, requiredPasswordRegistrationConstraints, requiredPendingAuthenticationConstraints, requiredProofConstraints, requiredSessionConstraints, requiredSessionStepUpConstraints, requiredSignedValidityConstraints, requiredStatefulPendingConstraints, stepUpIntentLive, validateStepUpPlan, validateStorage };
|
|
31
|
+
import { PhysicalStorageTable, StorageValidation, validateStorage, validateStorageBatch, withStorageValidation } from "./internal/storage-validation.mjs";
|
|
32
|
+
export { type AnyEmailAddressMapping, type AnyEmailRegistrationMapping, type AnyEmailSignInMapping, type AnyPasswordPersistenceMapping, type AnyPasswordRegistrationMapping, type AnyProofPersistenceMapping, type AuthenticationAuthorityMapping, type BoundPersistence, type ClaimsCodec, type CurrentAddress, CurrentEmailSql, CurrentPasskeyTransaction, CurrentPasswordPreparedTransaction, CurrentPasswordSql, CurrentPhoneTransaction, CurrentProofSql, CurrentSessionSql, type D1AuthenticationAuthorityMapping, type D1EmailAddressMapping, type D1EmailRegistrationMapping, type D1PasskeyMapping, type D1PasswordPersistenceMapping, type D1PendingAuthenticationMapping, type D1ProofPersistenceMapping, type D1SessionClockMapping, type D1SessionStepUpMapping, type D1SignedSessionValidityMapping, type D1StatefulSessionMapping, type Definition, type EmailAddressMapping, type EmailAuthorityCredentialTable, type EmailCommandTable, type EmailCredentialReadTable, type EmailCredentialTable, type EmailD1Clock, type EmailIdentifierReadTable, type EmailIdentifierTable, type EmailRegistrationAuthorityCredentialTable, type EmailRegistrationCredentialTable, type EmailRegistrationIdentifierTable, type EmailRegistrationIntent, type EmailRegistrationMapping, type EmailRegistrationProvisioning, type EmailRegistrationState, type EmailRegistrationTable, type EmailSignInMapping, type EmailSqlConfiguration, type EmailSqlDatabase, type EmailSqlQuery, type EmailSubjectReadTable, type EmailSubjectTable, type GuardedUpdate, NativeDatabase, type Observation, type PasskeyAdmissionTable, type PasskeyCeremonyMapping, type PasskeyChargeInsert, type PasskeyChargeKind, type PasskeyChargeTable, type PasskeyClock, type PasskeyColumn, type PasskeyCommandTable, type PasskeyCoordinatorError, type PasskeyCredentialInsert, type PasskeyCredentialMapping, type PasskeyCredentialOwnershipTable, type PasskeyCredentialReadTable, type PasskeyCredentialServices, type PasskeyEnrollmentContextMapping, type PasskeyEnrollmentContextServices, type PasskeyExecution, type PasskeyFactorReadTable, type PasskeyFlowInsert, type PasskeyFlowState, type PasskeyFlowTable, type PasskeyHandleOwnershipTable, type PasskeyHandleReadTable, type PasskeyHandleReservationTable, type PasskeyInvalidationInput, type PasskeyInvalidationMutation, type PasskeyKernel, type PasskeyManagementMapping, type PasskeyManagementServices, type PasskeyMappingSource, type PasskeyModuleTable, type PasskeyOwner, type PasskeyPersistenceMapping, type PasskeyPersistenceServices, type PasskeyPolicyGuard, type PasskeyRegistrationCeremonyCapabilities, type PasskeyRegistrationCeremonyMapping, type PasskeyRegistrationCeremonyServices, type PasskeyRegistrationIntentReadTable, type PasskeyRegistrationMapping, type PasskeyRegistrationServices, type PasskeyRegistrationWriter, type PasskeySubjectIdCodec, type PasskeySubjectReadTable, type PasskeyTargetConfiguration, type PasskeyWriteTables, type PasswordAttemptAction, type PasswordAttemptRecord, type PasswordAttemptState, type PasswordAttemptTable, type PasswordAuthorityCredentialTable, type PasswordChargeTable, type PasswordCommandTable, type PasswordConstraintClassifier, type PasswordCredentialTable, type PasswordD1Clock, type PasswordIdentifierTable, type PasswordPersistenceMapping, PasswordPreparedJournalGuards, type PasswordPreparedPostcondition, PasswordPreparedPostconditions, type PasswordRateScopeKind, type PasswordRateScopeTable, type PasswordRegistrationAuthority, type PasswordRegistrationConstraintClassifier, type PasswordRegistrationIntent, type PasswordRegistrationMapping, type PasswordRegistrationProvisioning, type PasswordRegistrationState, type PasswordRegistrationTable, type PasswordScopeKeys, type PasswordSqlConfiguration, type PasswordSqlDatabase, type PasswordSqlQuery, type PasswordSubjectTable, type PendingAuthenticationMapping, type PendingAuthenticationTables, type PersistenceApi, PersistenceConfigurationError, PersistenceMappingError, type PhysicalStorageTable, type ProofAbuseEventTable, type ProofAction, type ProofAuthorityInput, type ProofAuthorityTables, type ProofCommandDecision, type ProofCommandKind, type ProofCommandTable, type ProofConstraintClassifier, type ProofContinuationRecord, type ProofContinuationTable, type ProofD1Clock, type ProofDeliveryState, type ProofFailureEventTable, type ProofGenerationState, type ProofGenerationTable, type ProofPersistenceMapping, type ProofRateScopeTable, type ProofRequestTable, type ProofScopeKeys, type ProofScopeKind, type ProofSeriesTable, type ProofSqlConfiguration, type ProofSqlDatabase, type ProofSqlQuery, type QueryFailure, type QueryOperations, type RequiredEmailAddressConstraints, type RequiredEmailRegistrationConstraints, type RequiredEmailSignInConstraints, type RequiredPasswordConstraints, type RequiredPasswordRegistrationConstraints, type RequiredPendingAuthenticationConstraints, type RequiredProofConstraints, type RequiredSessionConstraints, type RequiredSessionStepUpConstraints, type RequiredSignedValidityConstraints, type RequiredStatefulPendingConstraints, type Row, type SessionAuthorityTables, type SessionConstraintClassifier, type SessionFlowTables, type SessionIdCodec, type SessionSqlDatabase, type SessionSqlOptions, type SessionStepUpIntentTables, type SessionStepUpMapping, type SessionStepUpSourceTables, type SessionSubjectTables, type SignedSessionValidityMapping, type SignedSessionValidityTables, type SqlColumn, type SqlExpression, type SqlFragment, type StatefulSessionMapping, type StatefulSessionTables, type StorageTable, type StorageValidation, type SubjectIdCodec, type TableModel, type TransactionBound, type TransactionCoordinatorError, type TransactionExecution, type TransactionNativeDatabase, type TransactionOwner, type TransactionTargetConfiguration, createPersistence, decodeStepUpIntent, encodeStepUpIntent, isMappedConstraintConflict, makeComposedPasskeys, makeEmailKernel, makePasskeyKernel, makePasswordKernel, makePhoneKernel, makeProofKernel, makeSessionKernel, makeMappings as makeStorageMappings, makeTransactionExecutionKernel, makeTransactionKernel, passkeyCredentialsLayer, passkeyEnrollmentContextLayer, passkeyInvalidationMutation, passkeyPersistenceLayer, passkeyPolicyGuard, requireStandalone, requiredEmailAddressConstraints, requiredEmailRegistrationConstraints, requiredEmailSignInConstraints, requiredPasskeyCredentialConstraints, requiredPasskeyManagementConstraints, requiredPasskeyPersistenceConstraints, requiredPasskeyRegistrationCeremonyConstraints, requiredPasskeyRegistrationWriteConstraints, requiredPasswordConstraints, requiredPasswordRegistrationConstraints, requiredPendingAuthenticationConstraints, requiredProofConstraints, requiredSessionConstraints, requiredSessionStepUpConstraints, requiredSignedValidityConstraints, requiredStatefulPendingConstraints, stepUpIntentLive, validateStepUpPlan, validateStorage, validateStorageBatch, withStorageValidation };
|
package/dist/Adapter.mjs
CHANGED
|
@@ -20,8 +20,8 @@ import { CurrentPhoneTransaction, makePhoneKernel } from "./internal/phone-kerne
|
|
|
20
20
|
import { decodeStepUpIntent, encodeStepUpIntent, stepUpIntentLive, validateStepUpPlan } from "./internal/step-up-state.mjs";
|
|
21
21
|
import { CurrentSessionSql, makeSessionKernel } from "./internal/session-kernel.mjs";
|
|
22
22
|
import { makeMappings } from "./internal/storage-mapping.mjs";
|
|
23
|
-
import { validateStorage } from "./internal/storage-validation.mjs";
|
|
23
|
+
import { validateStorage, validateStorageBatch, withStorageValidation } from "./internal/storage-validation.mjs";
|
|
24
24
|
import { createPersistence } from "./internal/persistence.mjs";
|
|
25
25
|
import { requiredPendingAuthenticationConstraints, requiredSessionConstraints, requiredSignedValidityConstraints, requiredStatefulPendingConstraints } from "./internal/models/session-model.mjs";
|
|
26
26
|
import { requiredSessionStepUpConstraints } from "./internal/models/step-up-model.mjs";
|
|
27
|
-
export { CurrentEmailSql, CurrentPasskeyTransaction, CurrentPasswordPreparedTransaction, CurrentPasswordSql, CurrentPhoneTransaction, CurrentProofSql, CurrentSessionSql, NativeDatabase, PasswordPreparedJournalGuards, PasswordPreparedPostconditions, PersistenceConfigurationError, PersistenceMappingError, createPersistence, decodeStepUpIntent, encodeStepUpIntent, isMappedConstraintConflict, makeComposedPasskeys, makeEmailKernel, makePasskeyKernel, makePasswordKernel, makePhoneKernel, makeProofKernel, makeSessionKernel, makeMappings as makeStorageMappings, makeTransactionExecutionKernel, makeTransactionKernel, passkeyCredentialsLayer, passkeyEnrollmentContextLayer, passkeyInvalidationMutation, passkeyPersistenceLayer, passkeyPolicyGuard, requireStandalone, requiredEmailAddressConstraints, requiredEmailRegistrationConstraints, requiredEmailSignInConstraints, requiredPasskeyCredentialConstraints, requiredPasskeyManagementConstraints, requiredPasskeyPersistenceConstraints, requiredPasskeyRegistrationCeremonyConstraints, requiredPasskeyRegistrationWriteConstraints, requiredPasswordConstraints, requiredPasswordRegistrationConstraints, requiredPendingAuthenticationConstraints, requiredProofConstraints, requiredSessionConstraints, requiredSessionStepUpConstraints, requiredSignedValidityConstraints, requiredStatefulPendingConstraints, stepUpIntentLive, validateStepUpPlan, validateStorage };
|
|
27
|
+
export { CurrentEmailSql, CurrentPasskeyTransaction, CurrentPasswordPreparedTransaction, CurrentPasswordSql, CurrentPhoneTransaction, CurrentProofSql, CurrentSessionSql, NativeDatabase, PasswordPreparedJournalGuards, PasswordPreparedPostconditions, PersistenceConfigurationError, PersistenceMappingError, createPersistence, decodeStepUpIntent, encodeStepUpIntent, isMappedConstraintConflict, makeComposedPasskeys, makeEmailKernel, makePasskeyKernel, makePasswordKernel, makePhoneKernel, makeProofKernel, makeSessionKernel, makeMappings as makeStorageMappings, makeTransactionExecutionKernel, makeTransactionKernel, passkeyCredentialsLayer, passkeyEnrollmentContextLayer, passkeyInvalidationMutation, passkeyPersistenceLayer, passkeyPolicyGuard, requireStandalone, requiredEmailAddressConstraints, requiredEmailRegistrationConstraints, requiredEmailSignInConstraints, requiredPasskeyCredentialConstraints, requiredPasskeyManagementConstraints, requiredPasskeyPersistenceConstraints, requiredPasskeyRegistrationCeremonyConstraints, requiredPasskeyRegistrationWriteConstraints, requiredPasswordConstraints, requiredPasswordRegistrationConstraints, requiredPendingAuthenticationConstraints, requiredProofConstraints, requiredSessionConstraints, requiredSessionStepUpConstraints, requiredSignedValidityConstraints, requiredStatefulPendingConstraints, stepUpIntentLive, validateStepUpPlan, validateStorage, validateStorageBatch, withStorageValidation };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PersistenceMappingError } from "./mapping-error.mjs";
|
|
2
2
|
import { StorageRole } from "./storage-tables.mjs";
|
|
3
|
-
import { Context, Effect, Layer, Schema } from "effect";
|
|
3
|
+
import { Context, Crypto, Effect, Layer, Schema } from "effect";
|
|
4
4
|
import { SqlClient } from "effect/sql";
|
|
5
5
|
import { PasskeyActionChallenge, PasskeyConfig, PasskeyConfigurationError, PasskeyCredentials, PasskeyEnrollmentContext, PasskeyManagementPersistence, PasskeyManagementPolicy, PasskeyMethodPolicy, PasskeyPersistence } from "@yielded/auth/Passkey";
|
|
6
6
|
import { SubjectId } from "@yielded/auth/Schema";
|
|
@@ -37,7 +37,7 @@ export interface Strategy {
|
|
|
37
37
|
export interface Definition<C extends ClaimsCodec, Id extends string> {
|
|
38
38
|
readonly namespace: string;
|
|
39
39
|
readonly claims: C;
|
|
40
|
-
readonly sessionMode?: string;
|
|
40
|
+
readonly sessionMode?: string | undefined;
|
|
41
41
|
readonly sessions: ReturnType<typeof make<C, Id>>;
|
|
42
42
|
readonly strategies: Readonly<Record<string, Strategy>>;
|
|
43
43
|
}
|
|
@@ -152,11 +152,12 @@ export interface BoundPersistence<T extends object, R, C extends ClaimsCodec, Id
|
|
|
152
152
|
* Do not perform external side effects or open an independent transaction here.
|
|
153
153
|
*/
|
|
154
154
|
readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;
|
|
155
|
-
readonly layer: Layer.Layer<Ports<C, Id, A>, PersistenceConfigurationError, ConfigId<A["namespace"]> | ProvisioningRequirement<A> | PasskeyRequirement$1<A> | SqlClient.SqlClient | R>;
|
|
155
|
+
readonly layer: Layer.Layer<Ports<C, Id, A>, PersistenceConfigurationError, Crypto.Crypto | ConfigId<A["namespace"]> | ProvisioningRequirement<A> | PasskeyRequirement$1<A> | SqlClient.SqlClient | R>;
|
|
156
156
|
readonly managed: <N, Instant = number>(options: {
|
|
157
157
|
readonly subjects: SubjectOptions<T, N>;
|
|
158
158
|
readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;
|
|
159
|
-
|
|
159
|
+
/** Stable SQL identifier prefix; reuse the deployed prefix for existing tables. */
|
|
160
|
+
readonly prefix: string;
|
|
160
161
|
readonly timestamps?: TimestampOptions<Instant>;
|
|
161
162
|
}) => StorageLayout<T, Roles<C, Id, A>>;
|
|
162
163
|
readonly map: <N, Instant = number>(options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.mjs","names":[],"sources":["../../src/internal/configuration.ts"],"sourcesContent":["import type { EmailAction, EmailAddressPersistence } from \"@yielded/auth/Email\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasskeyActionChallenge,\n PasskeyConfig,\n PasskeyCredentials,\n PasskeyEnrollmentContext,\n PasskeyManagementPersistence,\n PasskeyManagementPolicy,\n PasskeyMethodPolicy,\n PasskeyPersistence,\n PasskeyConfigurationError,\n} from \"@yielded/auth/Passkey\";\nimport type {\n PasswordAction,\n PasswordPersistence,\n PasswordUnavailable,\n} from \"@yielded/auth/Password\";\nimport type { PhoneAdmission, PhoneSignInTargets } from \"@yielded/auth/PhoneOtp\";\nimport type { ProofPersistence } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type {\n AuthenticationAuthority,\n AuthenticationRequirement,\n make as makeSessions,\n} from \"@yielded/auth/Sessions\";\nimport { type Context, type Effect, type Layer, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { StorageRole } from \"./storage-tables\";\n\nexport class PersistenceConfigurationError extends Schema.TaggedError<PersistenceConfigurationError>()(\n \"PersistenceConfigurationError\",\n { reason: Schema.String },\n) {}\n\nexport type ClaimsCodec = Schema.Codec<unknown, unknown, never, never>;\n\nexport interface PasskeyFeature {\n readonly kind: \"passkey\";\n readonly moduleId: string;\n readonly policy: Effect.Effect<PasskeyMethodPolicy, PasskeyConfigurationError, PasskeyConfig>;\n readonly management?: boolean;\n readonly managementPolicy?: PasskeyManagementPolicy;\n}\n\nexport interface Strategy {\n readonly strategy: object;\n readonly persistence?:\n | PasskeyFeature\n | {\n readonly kind: \"password\" | \"phone\" | \"email\";\n readonly moduleId: string;\n readonly lifecycle?: boolean;\n readonly management?: boolean;\n readonly addresses?: boolean;\n };\n readonly RegistrationAuthority?: Context.Key<unknown, unknown>;\n}\n\nexport interface Definition<C extends ClaimsCodec, Id extends string> {\n readonly namespace: string;\n readonly claims: C;\n readonly sessionMode?: string;\n readonly sessions: ReturnType<typeof makeSessions<C, Id>>;\n readonly strategies: Readonly<Record<string, Strategy>>;\n}\n\ntype KeyId<T> = T extends Context.Key<infer Id, infer _Service> ? Id : never;\ntype Enabled<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n Kind extends \"password\" | \"phone\" | \"email\" | \"passkey\",\n> = Extract<A[\"strategies\"][keyof A[\"strategies\"]], { persistence: { kind: Kind } }>;\n\ntype ManagedPassword<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"password\">, { persistence: { management: true } }>;\n\ntype ManagedPasskey<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"passkey\">, { persistence: { management: true } }>;\n\nexport type PasskeyRequirement<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n> = Enabled<A, \"passkey\"> extends never ? never : PasskeyConfig;\n\ntype RegistrationKey<S> = S extends { readonly RegistrationAuthority: infer K } ? K : never;\ntype Registration<S> =\n RegistrationKey<S> extends Context.Key<infer _Id, infer Service>\n ? Service extends { readonly register: (input: infer Input, ...args: never[]) => unknown }\n ? Input extends { readonly registration: infer Value }\n ? Value\n : never\n : never\n : never;\n\nexport interface ProvisioningId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceProvisioning\";\n}\n\nexport type Provisioning<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> = {\n readonly [\n K in keyof A[\"strategies\"] as A[\"strategies\"][K] extends {\n persistence: { kind: \"password\"; management: true };\n }\n ? K\n : never\n ]: (input: {\n readonly identifier: LoginIdentifier;\n readonly registration: Registration<A[\"strategies\"][K]>;\n }) => Effect.Effect<SubjectId, PasswordUnavailable>;\n};\n\nexport type ProvisioningRequirement<\n A extends { readonly namespace: string; readonly strategies: Readonly<Record<string, Strategy>> },\n> = ManagedPassword<A> extends never ? never : ProvisioningId<A[\"namespace\"]>;\n\ntype ProofRoles =\n | \"proofRequests\"\n | \"proofSeries\"\n | \"proofGenerations\"\n | \"proofContinuations\"\n | \"proofScopes\"\n | \"proofAbuse\"\n | \"proofFailures\"\n | \"proofCommands\";\n\ntype UsesProofs<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n | Enabled<A, \"phone\">\n | Enabled<A, \"email\">\n | ManagedPassword<A>;\n\nexport type Ports<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | AuthenticationAuthority\n | KeyId<A[\"sessions\"][\"StatefulSessionPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionRepository\"]>\n | (Enabled<A, \"password\"> extends never ? never : PasswordPersistence)\n | KeyId<RegistrationKey<ManagedPassword<A>>>\n | (Enabled<A, \"passkey\"> extends never ? never : PasskeyPersistence | PasskeyCredentials)\n | (ManagedPasskey<A> extends never\n ? never\n : PasskeyManagementPersistence | PasskeyEnrollmentContext)\n | (Enabled<A, \"email\"> extends never ? never : EmailAddressPersistence)\n | (UsesProofs<A> extends never ? never : ProofPersistence)\n | (Enabled<A, \"phone\"> extends never ? never : PhoneAdmission | PhoneSignInTargets);\n\nexport type Roles<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | \"identifiers\"\n | \"credentials\"\n | \"sessions\"\n | \"sessionFlows\"\n | (Enabled<A, \"password\"> extends never\n ? never\n :\n | \"passwords\"\n | \"passwordAttempts\"\n | \"passwordScopes\"\n | \"passwordCharges\"\n | \"passwordCommands\")\n | (ManagedPassword<A> extends never ? never : \"passwordRegistrations\")\n | (Enabled<A, \"phone\"> extends never ? never : \"phoneState\")\n | (Enabled<A, \"email\"> extends never ? never : \"emailCredentials\" | \"emailCommands\")\n | (Enabled<A, \"passkey\"> extends never\n ? never\n :\n | \"passkeyCredentials\"\n | \"passkeyOwnership\"\n | \"passkeyHandles\"\n | \"passkeyModules\"\n | \"passkeyFlows\"\n | \"passkeyAdmissions\"\n | \"passkeyCharges\")\n | (ManagedPasskey<A> extends never ? never : \"passkeyCommands\")\n | (UsesProofs<A> extends never ? never : ProofRoles);\n\nexport interface SubjectOptions<T extends object, NativeId> {\n readonly table: T;\n readonly id: string;\n readonly status: string;\n readonly activeValue: unknown;\n readonly securityRevision: string;\n readonly idCodec: Schema.Codec<SubjectId, NativeId>;\n readonly requirements: (\n row: Readonly<Record<string, unknown>>,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Defaults to sign-in requirements. Recovery can have a distinct application policy. */\n readonly actionRequirements?: (\n row: Readonly<Record<string, unknown>>,\n action: PasswordAction | EmailAction | PasskeyActionChallenge[\"action\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n}\n\n/** Codecs at the application-owned subject and timestamp boundary. */\nexport interface SubjectMapping {\n readonly table: object;\n readonly id: string;\n readonly status: string;\n readonly securityRevision: string;\n readonly activeValue: unknown;\n readonly requirements: SubjectOptions<object, unknown>[\"requirements\"];\n readonly actionRequirements: NonNullable<SubjectOptions<object, unknown>[\"actionRequirements\"]>;\n readonly toSubject: (value: unknown) => Effect.Effect<SubjectId, PersistenceMappingError>;\n readonly toNative: (value: SubjectId) => Effect.Effect<unknown, PersistenceMappingError>;\n readonly toSubjectSync: (value: unknown) => SubjectId;\n readonly toNativeSync: (value: SubjectId) => unknown;\n}\n\nexport interface MappingInput {\n readonly tables: Partial<Record<StorageRole, object>>;\n readonly subjects: SubjectMapping;\n readonly encodeInstant: (millis: number) => unknown;\n readonly decodeInstant: (value: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly decodeInstantSync: (value: unknown) => number;\n}\n\nexport interface StorageLayout<\n T extends object,\n Role extends StorageRole = StorageRole,\n> extends MappingInput {\n readonly namespace: string;\n readonly schema: Readonly<Record<Role, T>>;\n}\n\nexport interface ConfigId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceConfig\";\n}\n\nexport interface TimestampOptions<Instant> {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n}\n\nexport interface BoundPersistence<\n T extends object,\n R,\n C extends ClaimsCodec,\n Id extends string,\n A extends Definition<C, Id>,\n> {\n readonly Config: Context.Service<ConfigId<A[\"namespace\"]>, StorageLayout<T, Roles<C, Id, A>>> & {\n readonly layer: (\n value: StorageLayout<T, Roles<C, Id, A>>,\n ) => Layer.Layer<ConfigId<A[\"namespace\"]>>;\n };\n /** Create only the application subject. Runs inside the owning auth SQL transaction;\n * use that SqlClient (including Drizzle over it), and return the new subject ID.\n * Do not perform external side effects or open an independent transaction here.\n */\n readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;\n readonly layer: Layer.Layer<\n Ports<C, Id, A>,\n PersistenceConfigurationError,\n | ConfigId<A[\"namespace\"]>\n | ProvisioningRequirement<A>\n | PasskeyRequirement<A>\n | SqlClient.SqlClient\n | R\n >;\n readonly managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n readonly prefix?: string;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n readonly map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Readonly<Record<Roles<C, Id, A>, T>>;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n}\n\n/** Shared configuration shape. The backend chooses table types and service requirements. */\nexport interface PersistenceApi<T extends object, R = never> {\n readonly make: <\n C extends ClaimsCodec,\n const Id extends string,\n const A extends Definition<C, Id>,\n >(\n auth: A & Definition<C, Id>,\n ) => BoundPersistence<T, R, C, Id, A>;\n}\n"],"mappings":";;AAgCA,IAAa,gCAAb,cAAmD,OAAO,YAA2C,CAAC,CACpG,iCACA,EAAE,QAAQ,OAAO,OAAO,CAC1B,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"configuration.mjs","names":[],"sources":["../../src/internal/configuration.ts"],"sourcesContent":["import type { EmailAction, EmailAddressPersistence } from \"@yielded/auth/Email\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasskeyActionChallenge,\n PasskeyConfig,\n PasskeyCredentials,\n PasskeyEnrollmentContext,\n PasskeyManagementPersistence,\n PasskeyManagementPolicy,\n PasskeyMethodPolicy,\n PasskeyPersistence,\n PasskeyConfigurationError,\n} from \"@yielded/auth/Passkey\";\nimport type {\n PasswordAction,\n PasswordPersistence,\n PasswordUnavailable,\n} from \"@yielded/auth/Password\";\nimport type { PhoneAdmission, PhoneSignInTargets } from \"@yielded/auth/PhoneOtp\";\nimport type { ProofPersistence } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type {\n AuthenticationAuthority,\n AuthenticationRequirement,\n make as makeSessions,\n} from \"@yielded/auth/Sessions\";\nimport { type Context, type Crypto, type Effect, type Layer, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { StorageRole } from \"./storage-tables\";\n\nexport class PersistenceConfigurationError extends Schema.TaggedError<PersistenceConfigurationError>()(\n \"PersistenceConfigurationError\",\n { reason: Schema.String },\n) {}\n\nexport type ClaimsCodec = Schema.Codec<unknown, unknown, never, never>;\n\nexport interface PasskeyFeature {\n readonly kind: \"passkey\";\n readonly moduleId: string;\n readonly policy: Effect.Effect<PasskeyMethodPolicy, PasskeyConfigurationError, PasskeyConfig>;\n readonly management?: boolean;\n readonly managementPolicy?: PasskeyManagementPolicy;\n}\n\nexport interface Strategy {\n readonly strategy: object;\n readonly persistence?:\n | PasskeyFeature\n | {\n readonly kind: \"password\" | \"phone\" | \"email\";\n readonly moduleId: string;\n readonly lifecycle?: boolean;\n readonly management?: boolean;\n readonly addresses?: boolean;\n };\n readonly RegistrationAuthority?: Context.Key<unknown, unknown>;\n}\n\nexport interface Definition<C extends ClaimsCodec, Id extends string> {\n readonly namespace: string;\n readonly claims: C;\n readonly sessionMode?: string | undefined;\n readonly sessions: ReturnType<typeof makeSessions<C, Id>>;\n readonly strategies: Readonly<Record<string, Strategy>>;\n}\n\ntype KeyId<T> = T extends Context.Key<infer Id, infer _Service> ? Id : never;\ntype Enabled<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n Kind extends \"password\" | \"phone\" | \"email\" | \"passkey\",\n> = Extract<A[\"strategies\"][keyof A[\"strategies\"]], { persistence: { kind: Kind } }>;\n\ntype ManagedPassword<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"password\">, { persistence: { management: true } }>;\n\ntype ManagedPasskey<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"passkey\">, { persistence: { management: true } }>;\n\nexport type PasskeyRequirement<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n> = Enabled<A, \"passkey\"> extends never ? never : PasskeyConfig;\n\ntype RegistrationKey<S> = S extends { readonly RegistrationAuthority: infer K } ? K : never;\ntype Registration<S> =\n RegistrationKey<S> extends Context.Key<infer _Id, infer Service>\n ? Service extends { readonly register: (input: infer Input, ...args: never[]) => unknown }\n ? Input extends { readonly registration: infer Value }\n ? Value\n : never\n : never\n : never;\n\nexport interface ProvisioningId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceProvisioning\";\n}\n\nexport type Provisioning<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> = {\n readonly [\n K in keyof A[\"strategies\"] as A[\"strategies\"][K] extends {\n persistence: { kind: \"password\"; management: true };\n }\n ? K\n : never\n ]: (input: {\n readonly identifier: LoginIdentifier;\n readonly registration: Registration<A[\"strategies\"][K]>;\n }) => Effect.Effect<SubjectId, PasswordUnavailable>;\n};\n\nexport type ProvisioningRequirement<\n A extends { readonly namespace: string; readonly strategies: Readonly<Record<string, Strategy>> },\n> = ManagedPassword<A> extends never ? never : ProvisioningId<A[\"namespace\"]>;\n\ntype ProofRoles =\n | \"proofRequests\"\n | \"proofSeries\"\n | \"proofGenerations\"\n | \"proofContinuations\"\n | \"proofScopes\"\n | \"proofAbuse\"\n | \"proofFailures\"\n | \"proofCommands\";\n\ntype UsesProofs<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n | Enabled<A, \"phone\">\n | Enabled<A, \"email\">\n | ManagedPassword<A>;\n\nexport type Ports<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | AuthenticationAuthority\n | KeyId<A[\"sessions\"][\"StatefulSessionPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionRepository\"]>\n | (Enabled<A, \"password\"> extends never ? never : PasswordPersistence)\n | KeyId<RegistrationKey<ManagedPassword<A>>>\n | (Enabled<A, \"passkey\"> extends never ? never : PasskeyPersistence | PasskeyCredentials)\n | (ManagedPasskey<A> extends never\n ? never\n : PasskeyManagementPersistence | PasskeyEnrollmentContext)\n | (Enabled<A, \"email\"> extends never ? never : EmailAddressPersistence)\n | (UsesProofs<A> extends never ? never : ProofPersistence)\n | (Enabled<A, \"phone\"> extends never ? never : PhoneAdmission | PhoneSignInTargets);\n\nexport type Roles<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | \"identifiers\"\n | \"credentials\"\n | \"sessions\"\n | \"sessionFlows\"\n | (Enabled<A, \"password\"> extends never\n ? never\n :\n | \"passwords\"\n | \"passwordAttempts\"\n | \"passwordScopes\"\n | \"passwordCharges\"\n | \"passwordCommands\")\n | (ManagedPassword<A> extends never ? never : \"passwordRegistrations\")\n | (Enabled<A, \"phone\"> extends never ? never : \"phoneState\")\n | (Enabled<A, \"email\"> extends never ? never : \"emailCredentials\" | \"emailCommands\")\n | (Enabled<A, \"passkey\"> extends never\n ? never\n :\n | \"passkeyCredentials\"\n | \"passkeyOwnership\"\n | \"passkeyHandles\"\n | \"passkeyModules\"\n | \"passkeyFlows\"\n | \"passkeyAdmissions\"\n | \"passkeyCharges\")\n | (ManagedPasskey<A> extends never ? never : \"passkeyCommands\")\n | (UsesProofs<A> extends never ? never : ProofRoles);\n\nexport interface SubjectOptions<T extends object, NativeId> {\n readonly table: T;\n readonly id: string;\n readonly status: string;\n readonly activeValue: unknown;\n readonly securityRevision: string;\n readonly idCodec: Schema.Codec<SubjectId, NativeId>;\n readonly requirements: (\n row: Readonly<Record<string, unknown>>,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Defaults to sign-in requirements. Recovery can have a distinct application policy. */\n readonly actionRequirements?: (\n row: Readonly<Record<string, unknown>>,\n action: PasswordAction | EmailAction | PasskeyActionChallenge[\"action\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n}\n\n/** Codecs at the application-owned subject and timestamp boundary. */\nexport interface SubjectMapping {\n readonly table: object;\n readonly id: string;\n readonly status: string;\n readonly securityRevision: string;\n readonly activeValue: unknown;\n readonly requirements: SubjectOptions<object, unknown>[\"requirements\"];\n readonly actionRequirements: NonNullable<SubjectOptions<object, unknown>[\"actionRequirements\"]>;\n readonly toSubject: (value: unknown) => Effect.Effect<SubjectId, PersistenceMappingError>;\n readonly toNative: (value: SubjectId) => Effect.Effect<unknown, PersistenceMappingError>;\n readonly toSubjectSync: (value: unknown) => SubjectId;\n readonly toNativeSync: (value: SubjectId) => unknown;\n}\n\nexport interface MappingInput {\n readonly tables: Partial<Record<StorageRole, object>>;\n readonly subjects: SubjectMapping;\n readonly encodeInstant: (millis: number) => unknown;\n readonly decodeInstant: (value: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly decodeInstantSync: (value: unknown) => number;\n}\n\nexport interface StorageLayout<\n T extends object,\n Role extends StorageRole = StorageRole,\n> extends MappingInput {\n readonly namespace: string;\n readonly schema: Readonly<Record<Role, T>>;\n}\n\nexport interface ConfigId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceConfig\";\n}\n\nexport interface TimestampOptions<Instant> {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n}\n\nexport interface BoundPersistence<\n T extends object,\n R,\n C extends ClaimsCodec,\n Id extends string,\n A extends Definition<C, Id>,\n> {\n readonly Config: Context.Service<ConfigId<A[\"namespace\"]>, StorageLayout<T, Roles<C, Id, A>>> & {\n readonly layer: (\n value: StorageLayout<T, Roles<C, Id, A>>,\n ) => Layer.Layer<ConfigId<A[\"namespace\"]>>;\n };\n /** Create only the application subject. Runs inside the owning auth SQL transaction;\n * use that SqlClient (including Drizzle over it), and return the new subject ID.\n * Do not perform external side effects or open an independent transaction here.\n */\n readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;\n readonly layer: Layer.Layer<\n Ports<C, Id, A>,\n PersistenceConfigurationError,\n | Crypto.Crypto\n | ConfigId<A[\"namespace\"]>\n | ProvisioningRequirement<A>\n | PasskeyRequirement<A>\n | SqlClient.SqlClient\n | R\n >;\n readonly managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n /** Stable SQL identifier prefix; reuse the deployed prefix for existing tables. */\n readonly prefix: string;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n readonly map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Readonly<Record<Roles<C, Id, A>, T>>;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n}\n\n/** Shared configuration shape. The backend chooses table types and service requirements. */\nexport interface PersistenceApi<T extends object, R = never> {\n readonly make: <\n C extends ClaimsCodec,\n const Id extends string,\n const A extends Definition<C, Id>,\n >(\n auth: A & Definition<C, Id>,\n ) => BoundPersistence<T, R, C, Id, A>;\n}\n"],"mappings":";;AAgCA,IAAa,gCAAb,cAAmD,OAAO,YAA2C,CAAC,CACpG,iCACA,EAAE,QAAQ,OAAO,OAAO,CAC1B,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Crypto, Effect } from "effect";
|
|
2
|
+
import { TokenDigest } from "@yielded/auth/Schema";
|
|
3
|
+
import { Base64Url } from "effect/encoding";
|
|
4
|
+
//#region src/internal/crypto.ts
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
const randomId = Effect.flatMap(Crypto.Crypto, (crypto) => crypto.randomBytes(32).pipe(Effect.map(Base64Url.encode)));
|
|
7
|
+
/** Persisted UTF-8 SHA-256, encoded as unpadded base64url. */
|
|
8
|
+
const digest = (value) => Effect.flatMap(Crypto.Crypto, (crypto) => crypto.digest("SHA-256", encoder.encode(value))).pipe(Effect.map((bytes) => TokenDigest.make(Base64Url.encode(bytes))));
|
|
9
|
+
//#endregion
|
|
10
|
+
export { digest, randomId };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=crypto.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.mjs","names":[],"sources":["../../src/internal/crypto.ts"],"sourcesContent":["import { TokenDigest } from \"@yielded/auth/Schema\";\nimport { Crypto, Effect } from \"effect\";\nimport { Base64Url } from \"effect/encoding\";\n\nconst encoder = new TextEncoder();\n\nexport const randomId = Effect.flatMap(Crypto.Crypto, (crypto) =>\n crypto.randomBytes(32).pipe(Effect.map(Base64Url.encode)),\n);\n\n/** Persisted UTF-8 SHA-256, encoded as unpadded base64url. */\nexport const digest = (value: string) =>\n Effect.flatMap(Crypto.Crypto, (crypto) => crypto.digest(\"SHA-256\", encoder.encode(value))).pipe(\n Effect.map((bytes) => TokenDigest.make(Base64Url.encode(bytes))),\n );\n"],"mappings":";;;;AAIA,MAAM,UAAU,IAAI,YAAY;AAEhC,MAAa,WAAW,OAAO,QAAQ,OAAO,SAAS,WACrD,OAAO,YAAY,EAAE,CAAC,CAAC,KAAK,OAAO,IAAI,UAAU,MAAM,CAAC,CAC1D;;AAGA,MAAa,UAAU,UACrB,OAAO,QAAQ,OAAO,SAAS,WAAW,OAAO,OAAO,WAAW,QAAQ,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KACzF,OAAO,KAAK,UAAU,YAAY,KAAK,UAAU,OAAO,KAAK,CAAC,CAAC,CACjE"}
|
|
@@ -119,7 +119,7 @@ export declare const makeEmailKernel: <Fragment extends SqlFragment = SqlFragmen
|
|
|
119
119
|
readonly credential: any;
|
|
120
120
|
readonly identifier: any;
|
|
121
121
|
readonly snapshot: EmailCredentialSnapshot;
|
|
122
|
-
}
|
|
122
|
+
};
|
|
123
123
|
targetIdentifier?: any;
|
|
124
124
|
targetCredential?: any;
|
|
125
125
|
targetIdentifierRevision?: any;
|
|
@@ -135,7 +135,7 @@ export declare const makeEmailKernel: <Fragment extends SqlFragment = SqlFragmen
|
|
|
135
135
|
existingSessions: "immediate" | "original-absolute-expiry";
|
|
136
136
|
maximumExposureMillis: number;
|
|
137
137
|
oldAuthenticationEvidence: "rejected";
|
|
138
|
-
}
|
|
138
|
+
}>;
|
|
139
139
|
target: Readonly<{
|
|
140
140
|
namespace: string;
|
|
141
141
|
value: string;
|
|
@@ -150,7 +150,7 @@ export declare const makeEmailKernel: <Fragment extends SqlFragment = SqlFragmen
|
|
|
150
150
|
readonly revision: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
|
|
151
151
|
}[];
|
|
152
152
|
};
|
|
153
|
-
targetIdentifierRevision?:
|
|
153
|
+
targetIdentifierRevision?: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
|
|
154
154
|
source?: Readonly<{
|
|
155
155
|
moduleId: string;
|
|
156
156
|
identifierRevision: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
|
|
@@ -166,15 +166,15 @@ export declare const makeEmailKernel: <Fragment extends SqlFragment = SqlFragmen
|
|
|
166
166
|
readonly revision: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
|
|
167
167
|
}[];
|
|
168
168
|
};
|
|
169
|
-
}
|
|
169
|
+
}>;
|
|
170
170
|
}>;
|
|
171
171
|
authorization: Readonly<{
|
|
172
172
|
challenge: Readonly<{
|
|
173
173
|
moduleId: string;
|
|
174
174
|
action: "change-address" | "verify-address";
|
|
175
175
|
commandId: string & import("effect/Brand").Brand<"effect-auth/EmailCommandId">;
|
|
176
|
-
sourceCredentialId?: string
|
|
177
|
-
targetIdentifierRevision?:
|
|
176
|
+
sourceCredentialId?: string;
|
|
177
|
+
targetIdentifierRevision?: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
|
|
178
178
|
bindingDigest: string & import("effect/Brand").Brand<"effect-auth/TokenDigest">;
|
|
179
179
|
target: Readonly<{
|
|
180
180
|
namespace: string;
|
|
@@ -247,7 +247,7 @@ export declare const makeEmailKernel: <Fragment extends SqlFragment = SqlFragmen
|
|
|
247
247
|
readonly credential: any;
|
|
248
248
|
readonly identifier: any;
|
|
249
249
|
readonly snapshot: EmailCredentialSnapshot;
|
|
250
|
-
}
|
|
250
|
+
};
|
|
251
251
|
targetIdentifier?: any;
|
|
252
252
|
targetCredential?: any;
|
|
253
253
|
targetIdentifierRevision?: any;
|
|
@@ -7,22 +7,22 @@ const makePasskeyAdmissionKernel = (operations, state, transactions) => {
|
|
|
7
7
|
const invariant = state.invariant;
|
|
8
8
|
const { both } = transactions;
|
|
9
9
|
const chargeRetentionMillis = 864e5;
|
|
10
|
-
const chargeScopes = (ceremony, subjectId)
|
|
10
|
+
const chargeScopes = Effect.fnUntraced(function* (ceremony, subjectId) {
|
|
11
11
|
const scopes = [{
|
|
12
12
|
kind: "global",
|
|
13
|
-
scope: key("global", [ceremony.moduleId])
|
|
13
|
+
scope: yield* key("global", [ceremony.moduleId])
|
|
14
14
|
}];
|
|
15
15
|
if (subjectId !== void 0) scopes.push({
|
|
16
16
|
kind: "subject",
|
|
17
|
-
scope: subjectScope(subjectId)
|
|
17
|
+
scope: yield* subjectScope(subjectId)
|
|
18
18
|
});
|
|
19
|
-
const target = targetScope(ceremony);
|
|
19
|
+
const target = yield* targetScope(ceremony);
|
|
20
20
|
if (target !== null) scopes.push({
|
|
21
21
|
kind: "target",
|
|
22
22
|
scope: target
|
|
23
23
|
});
|
|
24
24
|
return scopes;
|
|
25
|
-
};
|
|
25
|
+
});
|
|
26
26
|
const lockAdmission = Effect.fn("passkey.lockAdmission")(function* (mapping) {
|
|
27
27
|
const owner = yield* CurrentPasskeyTransaction;
|
|
28
28
|
const table = mapping.admission;
|
|
@@ -63,13 +63,17 @@ const makePasskeyAdmissionKernel = (operations, state, transactions) => {
|
|
|
63
63
|
[table.kind]: kind
|
|
64
64
|
}), kind === "global" ? void 0 : equal(table.table, { [table.scope]: scope }), sql`${col(table.table, table.admittedAt)} is not null`, sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} > ${clock.engineNowMillis} - ${window}`)})`;
|
|
65
65
|
};
|
|
66
|
-
const admissionCondition = (mapping, policies, ceremony, subjectId, added, newFlow, resolvedSubject)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
...
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
const admissionCondition = Effect.fnUntraced(function* (mapping, policies, ceremony, subjectId, added, newFlow, resolvedSubject) {
|
|
67
|
+
const subject = subjectId === void 0 ? void 0 : yield* subjectScope(subjectId);
|
|
68
|
+
const scopes = yield* chargeScopes(ceremony, subjectId);
|
|
69
|
+
return both(...policies.flatMap((policy) => [
|
|
70
|
+
sql`${pendingCount(mapping)} + ${newFlow ? 1 : 0} <= ${policy.maximumPending}`,
|
|
71
|
+
subjectId === void 0 ? void 0 : sql`${pendingCount(mapping, subject)} + ${newFlow || resolvedSubject ? 1 : 0} <= ${policy.maximumPendingPerSubject}`,
|
|
72
|
+
...scopes.map(({ kind, scope }) => sql`${chargedCount(mapping, kind, scope, policy.admission[kind].windowMillis)} + ${added.includes(kind) ? 1 : 0} <= ${policy.admission[kind].limit}`)
|
|
73
|
+
]));
|
|
74
|
+
});
|
|
75
|
+
const guardAdmission = (mapping, policies, ceremony, subjectId) => Effect.gen(function* () {
|
|
76
|
+
(yield* CurrentPasskeyTransaction).postconditions.push(yield* admissionCondition(mapping, policies, ceremony, subjectId, [], false, false));
|
|
73
77
|
});
|
|
74
78
|
const readCharges = Effect.fn("passkey.readCharges")(function* (mapping, ceremony, policy, subjectId) {
|
|
75
79
|
const owner = yield* CurrentPasskeyTransaction;
|
|
@@ -83,7 +87,7 @@ const makePasskeyAdmissionKernel = (operations, state, transactions) => {
|
|
|
83
87
|
observe: false,
|
|
84
88
|
columns: mappedColumns(table)
|
|
85
89
|
});
|
|
86
|
-
const scopes = chargeScopes(ceremony, subjectId);
|
|
90
|
+
const scopes = yield* chargeScopes(ceremony, subjectId);
|
|
87
91
|
invariant(found.rows.length === scopes.length);
|
|
88
92
|
for (const { kind, scope } of scopes) {
|
|
89
93
|
const rows = found.rows.filter((row) => row[table.kind] === kind);
|
|
@@ -107,7 +111,7 @@ const makePasskeyAdmissionKernel = (operations, state, transactions) => {
|
|
|
107
111
|
});
|
|
108
112
|
const guardChargeSet = (mapping, ceremony, subjectId) => Effect.flatMap(CurrentPasskeyTransaction, (owner) => Effect.gen(function* () {
|
|
109
113
|
const table = mapping.charge;
|
|
110
|
-
const scopes = chargeScopes(ceremony, subjectId);
|
|
114
|
+
const scopes = yield* chargeScopes(ceremony, subjectId);
|
|
111
115
|
const where = equal(table.table, {
|
|
112
116
|
[table.moduleId]: ceremony.moduleId,
|
|
113
117
|
[table.flowId]: ceremony.flowId
|
|
@@ -137,7 +141,7 @@ const makePasskeyAdmissionKernel = (operations, state, transactions) => {
|
|
|
137
141
|
const clock = mapping.clock;
|
|
138
142
|
const earliest = yield* owner.now(clock);
|
|
139
143
|
const identities = [];
|
|
140
|
-
for (const { kind, scope } of chargeScopes(ceremony, subjectId).filter((item) => kinds.includes(item.kind))) {
|
|
144
|
+
for (const { kind, scope } of (yield* chargeScopes(ceremony, subjectId)).filter((item) => kinds.includes(item.kind))) {
|
|
141
145
|
const identity = {
|
|
142
146
|
[table.moduleId]: ceremony.moduleId,
|
|
143
147
|
[table.flowId]: ceremony.flowId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admission.mjs","names":[],"sources":["../../../src/internal/passkey/admission.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport type { PasskeyCeremony, PasskeyMethodPolicy } from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport type { PasskeyChargeKind } from \"../models/passkey-model\";\nimport type { SqlExpression as SQL, QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyAdmissionKernel = (\n operations: QueryOperations,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n | \"col\"\n | \"equal\"\n | \"existsExact\"\n | \"invariant\"\n | \"key\"\n | \"mappedColumns\"\n | \"subjectScope\"\n | \"targetScope\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n const { col, equal, existsExact, key, mappedColumns, subjectScope, targetScope } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n const chargeRetentionMillis = 86_400_000;\n\n const chargeScopes = (ceremony: PasskeyCeremony, subjectId?: string) => {\n const scopes: { kind: PasskeyChargeKind; scope: string }[] = [\n { kind: \"global\", scope: key(\"global\", [ceremony.moduleId]) },\n ];\n\n if (subjectId !== undefined) scopes.push({ kind: \"subject\", scope: subjectScope(subjectId) });\n const target = targetScope(ceremony);\n\n if (target !== null) scopes.push({ kind: \"target\", scope: target });\n\n return scopes;\n };\n\n const lockAdmission = Effect.fn(\"passkey.lockAdmission\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n const table = mapping.admission;\n\n const identity = {\n [table.authorityScope]: mapping.authorityScope,\n [table.moduleId]: mapping.moduleId,\n };\n\n // The previous marker/time is telemetry, not an authentication CAS. Actual\n // engine predicates serialize admission, including separately planned D1 batches.\n const found = yield* owner.read(table.table, equal(table.table, identity), {\n limit: 1,\n observe: false,\n columns: [table.authorityScope, table.moduleId],\n });\n\n invariant(found.rows.length === 1);\n invariant(\n found.rows[0]![table.authorityScope] === mapping.authorityScope &&\n found.rows[0]![table.moduleId] === mapping.moduleId,\n );\n\n const marked = {\n ...identity,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n yield* owner.updateGuarded(\n table.table,\n owner.exact(table.table, identity),\n {\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n [table.admittedAt]: null,\n },\n { rows: 1, postcondition: yield* existsExact(table.table, marked) },\n );\n });\n\n const pendingCount = (mapping: any, scope?: string) => {\n const table = mapping.flow;\n\n return sql`(select count(*) from ${table.table} where ${both(\n equal(table.table, { [table.moduleId]: mapping.moduleId }),\n sql`${col(table.table, table.state)} in (${table.states.Pending}, ${table.states.Claimed})`,\n scope === undefined ? undefined : equal(table.table, { [table.subjectScope]: scope }),\n )})`;\n };\n\n const chargedCount = (mapping: any, kind: PasskeyChargeKind, scope: string, window: number) => {\n const table = mapping.charge;\n const clock = mapping.clock;\n\n return sql`(select count(*) from ${table.table} where ${both(\n equal(table.table, { [table.moduleId]: mapping.moduleId, [table.kind]: kind }),\n kind === \"global\" ? undefined : equal(table.table, { [table.scope]: scope }),\n sql`${col(table.table, table.admittedAt)} is not null`,\n sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} > ${clock.engineNowMillis} - ${window}`,\n )})`;\n };\n\n const admissionCondition = (\n mapping: any,\n policies: ReadonlyArray<PasskeyMethodPolicy>,\n ceremony: PasskeyCeremony,\n subjectId: string | undefined,\n added: ReadonlyArray<PasskeyChargeKind>,\n newFlow: boolean,\n resolvedSubject: boolean,\n ) =>\n both(\n ...policies.flatMap((policy) => [\n sql`${pendingCount(mapping)} + ${newFlow ? 1 : 0} <= ${policy.maximumPending}`,\n subjectId === undefined\n ? undefined\n : sql`${pendingCount(mapping, subjectScope(subjectId))} + ${newFlow || resolvedSubject ? 1 : 0} <= ${policy.maximumPendingPerSubject}`,\n ...chargeScopes(ceremony, subjectId).map(\n ({ kind, scope }) =>\n sql`${chargedCount(mapping, kind, scope, policy.admission[kind].windowMillis)} + ${added.includes(kind) ? 1 : 0} <= ${policy.admission[kind].limit}`,\n ),\n ]),\n );\n\n const guardAdmission = (\n mapping: any,\n policies: ReadonlyArray<PasskeyMethodPolicy>,\n ceremony: PasskeyCeremony,\n subjectId?: string,\n ) =>\n Effect.map(CurrentPasskeyTransaction, (owner) => {\n owner.postconditions.push(\n admissionCondition(mapping, policies, ceremony, subjectId, [], false, false),\n );\n });\n\n const readCharges = Effect.fn(\"passkey.readCharges\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n policy: PasskeyMethodPolicy,\n subjectId?: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const table = mapping.charge;\n\n const where = equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n });\n\n const found = yield* owner.read(table.table, where, {\n limit: 3,\n observe: false,\n columns: mappedColumns(table),\n });\n\n const scopes = chargeScopes(ceremony, subjectId);\n\n invariant(found.rows.length === scopes.length);\n for (const { kind, scope } of scopes) {\n const rows = found.rows.filter((row) => row[table.kind] === kind);\n\n invariant(rows.length === 1);\n const row = rows[0]!;\n const admitted = mapping.clock.decodeInstant(row[table.admittedAt]);\n const retained = mapping.clock.decodeInstant(row[table.retainUntil]);\n\n invariant(\n Number.isSafeInteger(admitted) &&\n admitted >= 0 &&\n retained === admitted + chargeRetentionMillis,\n );\n invariant(\n row[table.moduleId] === ceremony.moduleId &&\n row[table.flowId] === ceremony.flowId &&\n row[table.purpose] === ceremony.purpose,\n );\n invariant(\n row[table.scope] === scope &&\n row[table.originalWindowMillis] === policy.admission[kind].windowMillis,\n );\n owner.observations.push({\n table: table.table,\n where: equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.kind]: kind,\n }),\n rows: [row],\n });\n }\n });\n\n const guardChargeSet = (mapping: any, ceremony: PasskeyCeremony, subjectId?: string) =>\n Effect.flatMap(CurrentPasskeyTransaction, (owner) =>\n Effect.gen(function* () {\n const table = mapping.charge;\n const scopes = chargeScopes(ceremony, subjectId);\n\n const where = equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n });\n\n owner.postconditions.push(\n sql`(select count(*) from ${table.table} where ${where}) = ${scopes.length}`,\n );\n for (const { kind, scope } of scopes)\n owner.postconditions.push(\n yield* existsExact(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.purpose]: ceremony.purpose,\n [table.kind]: kind,\n [table.scope]: scope,\n }),\n );\n }),\n );\n\n const insertCharges = Effect.fn(\"passkey.insertCharges\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n policy: PasskeyMethodPolicy,\n kinds: ReadonlyArray<PasskeyChargeKind>,\n subjectId?: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (kinds.length === 0) return;\n const table = mapping.charge;\n const anchor = mapping.admission;\n const clock = mapping.clock;\n const earliest = yield* owner.now(clock);\n const identities: Record<string, unknown>[] = [];\n\n for (const { kind, scope } of chargeScopes(ceremony, subjectId).filter((item) =>\n kinds.includes(item.kind),\n )) {\n const identity = {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.kind]: kind,\n };\n\n const values = {\n ...table.encodeInsert({\n moduleId: ceremony.moduleId,\n flowId: ceremony.flowId,\n purpose: ceremony.purpose,\n kind,\n scope,\n originalWindowMillis: policy.admission[kind].windowMillis,\n marker: owner.marker,\n }),\n ...identity,\n [table.purpose]: ceremony.purpose,\n [table.scope]: scope,\n [table.originalWindowMillis]: policy.admission[kind].windowMillis,\n [table.admittedAt]: null,\n [table.retainUntil]: null,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n const observation = yield* owner.insert(table.table, values, identity);\n\n observation.rows = observation.rows.map((row) =>\n Object.fromEntries(\n Object.entries(row).filter(\n ([name]) => name !== table.admittedAt && name !== table.retainUntil,\n ),\n ),\n );\n identities.push(identity);\n }\n invariant(identities.length === kinds.length);\n\n const anchorIdentity = {\n [anchor.authorityScope]: mapping.authorityScope,\n [anchor.moduleId]: mapping.moduleId,\n [anchor.version]: owner.marker,\n [anchor.ownerMarker]: owner.marker,\n };\n\n const stamp = sql`(select ${col(anchor.table, anchor.admittedAt)} from ${anchor.table} where ${owner.exact(anchor.table, anchorIdentity)})`;\n const stampMillis = clock.toMillis(stamp);\n\n const validStamp: SQL = both(\n yield* existsExact(anchor.table, anchorIdentity),\n sql`${stamp} is not null`,\n sql`${stampMillis} >= ${earliest}`,\n sql`${stampMillis} <= ${clock.engineNowMillis}`,\n sql`${stampMillis} <= ${8640000000000000 - chargeRetentionMillis}`,\n );\n\n yield* owner.finalUpdate(\n anchor.table,\n owner.exact(anchor.table, { ...anchorIdentity, [anchor.admittedAt]: null }),\n {\n [anchor.admittedAt]: clock.fromMillis(clock.engineNowMillis),\n },\n { rows: 1, postcondition: validStamp },\n );\n for (const identity of identities) {\n const expected = {\n ...identity,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n const postcondition = yield* existsExact(\n table.table,\n expected,\n both(\n sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} = ${stampMillis}`,\n sql`${clock.toMillis(sql`${col(table.table, table.retainUntil)}`)} = ${stampMillis} + ${chargeRetentionMillis}`,\n ),\n );\n\n yield* owner.finalUpdate(\n table.table,\n owner.exact(table.table, {\n ...expected,\n [table.admittedAt]: null,\n [table.retainUntil]: null,\n }),\n {\n [table.admittedAt]: stamp,\n [table.retainUntil]: clock.fromMillis(sql`${stampMillis} + ${chargeRetentionMillis}`),\n },\n { rows: 1, postcondition },\n );\n }\n owner.postconditions.push(\n sql`(select count(*) from ${table.table} where ${owner.exact(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.ownerMarker]: owner.marker,\n })}) = ${identities.length}`,\n );\n });\n\n return {\n chargeRetentionMillis,\n chargeScopes,\n lockAdmission,\n admissionCondition,\n guardAdmission,\n readCharges,\n guardChargeSet,\n insertCharges,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admission.mjs","names":[],"sources":["../../../src/internal/passkey/admission.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport type { PasskeyCeremony, PasskeyMethodPolicy } from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport type { PasskeyChargeKind } from \"../models/passkey-model\";\nimport type { SqlExpression as SQL, QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyAdmissionKernel = (\n operations: QueryOperations,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n | \"col\"\n | \"equal\"\n | \"existsExact\"\n | \"invariant\"\n | \"key\"\n | \"mappedColumns\"\n | \"subjectScope\"\n | \"targetScope\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n const { col, equal, existsExact, key, mappedColumns, subjectScope, targetScope } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n const chargeRetentionMillis = 86_400_000;\n\n const chargeScopes = Effect.fnUntraced(function* (ceremony: PasskeyCeremony, subjectId?: string) {\n const scopes: { kind: PasskeyChargeKind; scope: string }[] = [\n { kind: \"global\", scope: yield* key(\"global\", [ceremony.moduleId]) },\n ];\n\n if (subjectId !== undefined)\n scopes.push({ kind: \"subject\", scope: yield* subjectScope(subjectId) });\n const target = yield* targetScope(ceremony);\n\n if (target !== null) scopes.push({ kind: \"target\", scope: target });\n\n return scopes;\n });\n\n const lockAdmission = Effect.fn(\"passkey.lockAdmission\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n const table = mapping.admission;\n\n const identity = {\n [table.authorityScope]: mapping.authorityScope,\n [table.moduleId]: mapping.moduleId,\n };\n\n // The previous marker/time is telemetry, not an authentication CAS. Actual\n // engine predicates serialize admission, including separately planned D1 batches.\n const found = yield* owner.read(table.table, equal(table.table, identity), {\n limit: 1,\n observe: false,\n columns: [table.authorityScope, table.moduleId],\n });\n\n invariant(found.rows.length === 1);\n invariant(\n found.rows[0]![table.authorityScope] === mapping.authorityScope &&\n found.rows[0]![table.moduleId] === mapping.moduleId,\n );\n\n const marked = {\n ...identity,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n yield* owner.updateGuarded(\n table.table,\n owner.exact(table.table, identity),\n {\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n [table.admittedAt]: null,\n },\n { rows: 1, postcondition: yield* existsExact(table.table, marked) },\n );\n });\n\n const pendingCount = (mapping: any, scope?: string) => {\n const table = mapping.flow;\n\n return sql`(select count(*) from ${table.table} where ${both(\n equal(table.table, { [table.moduleId]: mapping.moduleId }),\n sql`${col(table.table, table.state)} in (${table.states.Pending}, ${table.states.Claimed})`,\n scope === undefined ? undefined : equal(table.table, { [table.subjectScope]: scope }),\n )})`;\n };\n\n const chargedCount = (mapping: any, kind: PasskeyChargeKind, scope: string, window: number) => {\n const table = mapping.charge;\n const clock = mapping.clock;\n\n return sql`(select count(*) from ${table.table} where ${both(\n equal(table.table, { [table.moduleId]: mapping.moduleId, [table.kind]: kind }),\n kind === \"global\" ? undefined : equal(table.table, { [table.scope]: scope }),\n sql`${col(table.table, table.admittedAt)} is not null`,\n sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} > ${clock.engineNowMillis} - ${window}`,\n )})`;\n };\n\n const admissionCondition = Effect.fnUntraced(function* (\n mapping: any,\n policies: ReadonlyArray<PasskeyMethodPolicy>,\n ceremony: PasskeyCeremony,\n subjectId: string | undefined,\n added: ReadonlyArray<PasskeyChargeKind>,\n newFlow: boolean,\n resolvedSubject: boolean,\n ) {\n const subject = subjectId === undefined ? undefined : yield* subjectScope(subjectId);\n const scopes = yield* chargeScopes(ceremony, subjectId);\n\n return both(\n ...policies.flatMap((policy) => [\n sql`${pendingCount(mapping)} + ${newFlow ? 1 : 0} <= ${policy.maximumPending}`,\n subjectId === undefined\n ? undefined\n : sql`${pendingCount(mapping, subject)} + ${newFlow || resolvedSubject ? 1 : 0} <= ${policy.maximumPendingPerSubject}`,\n ...scopes.map(\n ({ kind, scope }) =>\n sql`${chargedCount(mapping, kind, scope, policy.admission[kind].windowMillis)} + ${added.includes(kind) ? 1 : 0} <= ${policy.admission[kind].limit}`,\n ),\n ]),\n );\n });\n\n const guardAdmission = (\n mapping: any,\n policies: ReadonlyArray<PasskeyMethodPolicy>,\n ceremony: PasskeyCeremony,\n subjectId?: string,\n ) =>\n Effect.gen(function* () {\n const owner = yield* CurrentPasskeyTransaction;\n\n owner.postconditions.push(\n yield* admissionCondition(mapping, policies, ceremony, subjectId, [], false, false),\n );\n });\n\n const readCharges = Effect.fn(\"passkey.readCharges\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n policy: PasskeyMethodPolicy,\n subjectId?: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const table = mapping.charge;\n\n const where = equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n });\n\n const found = yield* owner.read(table.table, where, {\n limit: 3,\n observe: false,\n columns: mappedColumns(table),\n });\n\n const scopes = yield* chargeScopes(ceremony, subjectId);\n\n invariant(found.rows.length === scopes.length);\n for (const { kind, scope } of scopes) {\n const rows = found.rows.filter((row) => row[table.kind] === kind);\n\n invariant(rows.length === 1);\n const row = rows[0]!;\n const admitted = mapping.clock.decodeInstant(row[table.admittedAt]);\n const retained = mapping.clock.decodeInstant(row[table.retainUntil]);\n\n invariant(\n Number.isSafeInteger(admitted) &&\n admitted >= 0 &&\n retained === admitted + chargeRetentionMillis,\n );\n invariant(\n row[table.moduleId] === ceremony.moduleId &&\n row[table.flowId] === ceremony.flowId &&\n row[table.purpose] === ceremony.purpose,\n );\n invariant(\n row[table.scope] === scope &&\n row[table.originalWindowMillis] === policy.admission[kind].windowMillis,\n );\n owner.observations.push({\n table: table.table,\n where: equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.kind]: kind,\n }),\n rows: [row],\n });\n }\n });\n\n const guardChargeSet = (mapping: any, ceremony: PasskeyCeremony, subjectId?: string) =>\n Effect.flatMap(CurrentPasskeyTransaction, (owner) =>\n Effect.gen(function* () {\n const table = mapping.charge;\n const scopes = yield* chargeScopes(ceremony, subjectId);\n\n const where = equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n });\n\n owner.postconditions.push(\n sql`(select count(*) from ${table.table} where ${where}) = ${scopes.length}`,\n );\n for (const { kind, scope } of scopes)\n owner.postconditions.push(\n yield* existsExact(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.purpose]: ceremony.purpose,\n [table.kind]: kind,\n [table.scope]: scope,\n }),\n );\n }),\n );\n\n const insertCharges = Effect.fn(\"passkey.insertCharges\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n policy: PasskeyMethodPolicy,\n kinds: ReadonlyArray<PasskeyChargeKind>,\n subjectId?: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (kinds.length === 0) return;\n const table = mapping.charge;\n const anchor = mapping.admission;\n const clock = mapping.clock;\n const earliest = yield* owner.now(clock);\n const identities: Record<string, unknown>[] = [];\n\n for (const { kind, scope } of (yield* chargeScopes(ceremony, subjectId)).filter((item) =>\n kinds.includes(item.kind),\n )) {\n const identity = {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.kind]: kind,\n };\n\n const values = {\n ...table.encodeInsert({\n moduleId: ceremony.moduleId,\n flowId: ceremony.flowId,\n purpose: ceremony.purpose,\n kind,\n scope,\n originalWindowMillis: policy.admission[kind].windowMillis,\n marker: owner.marker,\n }),\n ...identity,\n [table.purpose]: ceremony.purpose,\n [table.scope]: scope,\n [table.originalWindowMillis]: policy.admission[kind].windowMillis,\n [table.admittedAt]: null,\n [table.retainUntil]: null,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n const observation = yield* owner.insert(table.table, values, identity);\n\n observation.rows = observation.rows.map((row) =>\n Object.fromEntries(\n Object.entries(row).filter(\n ([name]) => name !== table.admittedAt && name !== table.retainUntil,\n ),\n ),\n );\n identities.push(identity);\n }\n invariant(identities.length === kinds.length);\n\n const anchorIdentity = {\n [anchor.authorityScope]: mapping.authorityScope,\n [anchor.moduleId]: mapping.moduleId,\n [anchor.version]: owner.marker,\n [anchor.ownerMarker]: owner.marker,\n };\n\n const stamp = sql`(select ${col(anchor.table, anchor.admittedAt)} from ${anchor.table} where ${owner.exact(anchor.table, anchorIdentity)})`;\n const stampMillis = clock.toMillis(stamp);\n\n const validStamp: SQL = both(\n yield* existsExact(anchor.table, anchorIdentity),\n sql`${stamp} is not null`,\n sql`${stampMillis} >= ${earliest}`,\n sql`${stampMillis} <= ${clock.engineNowMillis}`,\n sql`${stampMillis} <= ${8640000000000000 - chargeRetentionMillis}`,\n );\n\n yield* owner.finalUpdate(\n anchor.table,\n owner.exact(anchor.table, { ...anchorIdentity, [anchor.admittedAt]: null }),\n {\n [anchor.admittedAt]: clock.fromMillis(clock.engineNowMillis),\n },\n { rows: 1, postcondition: validStamp },\n );\n for (const identity of identities) {\n const expected = {\n ...identity,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n const postcondition = yield* existsExact(\n table.table,\n expected,\n both(\n sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} = ${stampMillis}`,\n sql`${clock.toMillis(sql`${col(table.table, table.retainUntil)}`)} = ${stampMillis} + ${chargeRetentionMillis}`,\n ),\n );\n\n yield* owner.finalUpdate(\n table.table,\n owner.exact(table.table, {\n ...expected,\n [table.admittedAt]: null,\n [table.retainUntil]: null,\n }),\n {\n [table.admittedAt]: stamp,\n [table.retainUntil]: clock.fromMillis(sql`${stampMillis} + ${chargeRetentionMillis}`),\n },\n { rows: 1, postcondition },\n );\n }\n owner.postconditions.push(\n sql`(select count(*) from ${table.table} where ${owner.exact(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.ownerMarker]: owner.marker,\n })}) = ${identities.length}`,\n );\n });\n\n return {\n chargeRetentionMillis,\n chargeScopes,\n lockAdmission,\n admissionCondition,\n guardAdmission,\n readCharges,\n guardChargeSet,\n insertCharges,\n };\n};\n"],"mappings":"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"}
|
|
@@ -65,7 +65,7 @@ const makePasskeyCredentialsKernel = (operations, state, transactions) => {
|
|
|
65
65
|
const discoverSubject = Effect.fn("passkey.discoverSubject")(function* (mapping, rpId, protocolCredentialId) {
|
|
66
66
|
const owner = yield* CurrentPasskeyTransaction;
|
|
67
67
|
const descriptor = mapping.credentialOwnership;
|
|
68
|
-
const row = (yield* owner.read(descriptor.table, equal(descriptor.table, { [descriptor.credentialKey]: credentialKey(rpId, protocolCredentialId) }), {
|
|
68
|
+
const row = (yield* owner.read(descriptor.table, equal(descriptor.table, { [descriptor.credentialKey]: yield* credentialKey(rpId, protocolCredentialId) }), {
|
|
69
69
|
lock: false,
|
|
70
70
|
observe: false,
|
|
71
71
|
limit: 1,
|
|
@@ -107,7 +107,7 @@ const makePasskeyCredentialsKernel = (operations, state, transactions) => {
|
|
|
107
107
|
const readCredential = Effect.fn("passkey.readCredential")(function* (mapping, subject, rpId, protocolCredentialId, ids) {
|
|
108
108
|
const owner = yield* CurrentPasskeyTransaction;
|
|
109
109
|
const ownership = mapping.credentialOwnership;
|
|
110
|
-
const tupleKey = credentialKey(rpId, protocolCredentialId);
|
|
110
|
+
const tupleKey = yield* credentialKey(rpId, protocolCredentialId);
|
|
111
111
|
const tuple = (yield* owner.read(ownership.table, equal(ownership.table, { [ownership.credentialKey]: tupleKey }), {
|
|
112
112
|
limit: 1,
|
|
113
113
|
columns: mappedColumns(ownership)
|
|
@@ -130,7 +130,7 @@ const makePasskeyCredentialsKernel = (operations, state, transactions) => {
|
|
|
130
130
|
if (!mapping.subjectIds.equals(descriptor.decodeSubjectId(copiedRow(row)), subject.nativeId)) return void 0;
|
|
131
131
|
let decoded = descriptor.decode(copiedRow(row));
|
|
132
132
|
const handle = mapping.handleOwnership;
|
|
133
|
-
const expectedHandleKey = handleKey(rpId, decoded.userHandle);
|
|
133
|
+
const expectedHandleKey = yield* handleKey(rpId, decoded.userHandle);
|
|
134
134
|
const handleRow = (yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: expectedHandleKey }), {
|
|
135
135
|
limit: 1,
|
|
136
136
|
columns: mappedColumns(handle)
|
|
@@ -208,7 +208,7 @@ const makePasskeyCredentialsKernel = (operations, state, transactions) => {
|
|
|
208
208
|
invariant(handle.isOwnedState(handleRow[handle.state]) && handleRow[handle.rpId] === input.rpId);
|
|
209
209
|
invariant(mapping.read.subjectIds.equals(handle.decodeSubjectId(copiedRow(handleRow)), subject.nativeId));
|
|
210
210
|
userHandle = Schema.decodeUnknownSync(PasskeyUserHandle)(handleRow[handle.userHandle]);
|
|
211
|
-
invariant(handleRow[handle.handleKey] === handleKey(input.rpId, userHandle));
|
|
211
|
+
invariant(handleRow[handle.handleKey] === (yield* handleKey(input.rpId, userHandle)));
|
|
212
212
|
}
|
|
213
213
|
const credential = mapping.read.credential;
|
|
214
214
|
const credentials = (yield* owner.read(credential.table, both(equal(credential.table, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.mjs","names":[],"sources":["../../../src/internal/passkey/credentials.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyCredential,\n PasskeyDescriptor,\n PasskeyRevision,\n PasskeyUserHandle,\n validatePasskeyPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport { Effect, Schema } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport interface SubjectRead {\n readonly nativeId: unknown;\n readonly subjectId: SubjectId;\n readonly securityRevision: typeof PasskeyRevision.Type.securityRevision;\n}\n\nexport interface CredentialRead {\n readonly credential: PasskeyCredential;\n readonly row: Record<string, unknown>;\n}\n\nexport const makePasskeyCredentialsKernel = (\n operations: QueryOperations,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n | \"assertNow\"\n | \"col\"\n | \"copiedRow\"\n | \"credentialKey\"\n | \"equal\"\n | \"handleKey\"\n | \"invariant\"\n | \"mappedColumns\"\n | \"observeSemantic\"\n | \"matchesNativeRow\"\n | \"semanticCredentialColumns\"\n | \"unavailable\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { asc, inArray, sql } = operations;\n\n const {\n assertNow,\n col,\n copiedRow,\n credentialKey,\n equal,\n handleKey,\n mappedColumns,\n observeSemantic,\n matchesNativeRow,\n semanticCredentialColumns,\n unavailable,\n } = state;\n\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n const readSubject = Effect.fn(\"passkey.readSubject\")(function* (\n mapping: any,\n subjectId: SubjectId,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const descriptor = mapping.subject;\n const nativeId = mapping.subjectIds.toNative(subjectId);\n\n invariant(mapping.subjectIds.toSubject(nativeId) === subjectId);\n\n const found = yield* owner.read(\n descriptor.table,\n equal(descriptor.table, { [descriptor.id]: nativeId }),\n {\n limit: 1,\n columns: [descriptor.id, descriptor.status, descriptor.securityRevision],\n },\n );\n\n const row = found.rows[0];\n\n if (row === undefined || !descriptor.isActiveStatus(row[descriptor.status])) return undefined;\n invariant(mapping.subjectIds.equals(descriptor.decodeId(copiedRow(row)), nativeId));\n\n const active = sql`exists(select 1 from ${descriptor.table} where ${both(\n owner.exact(descriptor.table, row),\n descriptor.activeCondition,\n )})`;\n\n if (!(yield* owner.check(active))) return undefined;\n owner.postconditions.push(active);\n\n // oxlint-disable-next-line no-restricted-properties -- native SQL column values enter through this untyped driver boundary.\n const securityRevision = Schema.decodeUnknownSync(PasskeyRevision.fields.securityRevision)(\n row[descriptor.securityRevision],\n );\n\n return { nativeId, subjectId, securityRevision };\n });\n\n const readModule = Effect.fn(\"passkey.readModule\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n const descriptor = mapping.module;\n\n const columns = [\n ...new Set<string>([\n descriptor.moduleId,\n descriptor.status,\n descriptor.policyRevision,\n ...descriptor.policyColumns,\n ]),\n ];\n\n const found = yield* owner.read(\n descriptor.table,\n equal(descriptor.table, { [descriptor.moduleId]: mapping.moduleId }),\n {\n limit: 1,\n columns,\n },\n );\n\n const row = found.rows[0];\n\n if (row === undefined || !descriptor.isActiveStatus(row[descriptor.status])) return undefined;\n invariant(row[descriptor.moduleId] === mapping.moduleId);\n yield* assertNow(\n sql`exists(select 1 from ${descriptor.table} where ${both(\n owner.exact(descriptor.table, row),\n descriptor.activeCondition,\n )})`,\n );\n\n return yield* validatePasskeyPolicy(descriptor.decodeMethodPolicy(copiedRow(row))).pipe(\n Effect.mapError(unavailable),\n );\n });\n\n const readPolicyGuards = Effect.fn(\"passkey.readPolicyGuards\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n\n for (const guard of [...(mapping.module.guards ?? [])].sort((a, b) =>\n a.scope < b.scope ? -1 : a.scope > b.scope ? 1 : 0,\n )) {\n const locked = yield* owner.read(guard.table, guard.where(mapping.moduleId), {\n limit: 1,\n columns: guard.columns,\n });\n\n invariant(locked.rows.length === 1);\n yield* assertNow(guard.condition(mapping.moduleId));\n }\n });\n\n /** Advisory discovery never locks a tuple before its subject. All fields are\n * reread under the common subject/tuple lock order by readCredential. */\n const discoverSubject = Effect.fn(\"passkey.discoverSubject\")(function* (\n mapping: any,\n rpId: string,\n protocolCredentialId: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const descriptor = mapping.credentialOwnership;\n\n const found = yield* owner.read(\n descriptor.table,\n equal(descriptor.table, {\n [descriptor.credentialKey]: credentialKey(rpId, protocolCredentialId),\n }),\n { lock: false, observe: false, limit: 1, columns: mappedColumns(descriptor) },\n );\n\n const row = found.rows[0];\n\n if (row === undefined) return undefined;\n invariant(\n row[descriptor.rpId] === rpId &&\n row[descriptor.protocolCredentialId] === protocolCredentialId,\n );\n if (!descriptor.isOwnedState(row[descriptor.state])) return undefined;\n const native = descriptor.decodeSubjectId(copiedRow(row));\n const subjectId = mapping.subjectIds.toSubject(native);\n\n invariant(mapping.subjectIds.equals(mapping.subjectIds.toNative(subjectId), native));\n\n return subjectId as SubjectId;\n });\n\n const readRevision = Effect.fn(\"passkey.readRevision\")(function* (\n mapping: any,\n subject: SubjectRead,\n ids?: ReadonlyArray<string>,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (ids !== undefined) invariant(ids.length <= 64 && new Set(ids).size === ids.length);\n const descriptor = mapping.authority;\n\n const where = both(\n equal(descriptor.table, { [descriptor.subjectId]: subject.nativeId }),\n descriptor.activeCondition,\n ids === undefined\n ? undefined\n : ids.length === 0\n ? sql`1 = 0`\n : inArray(col(descriptor.table, descriptor.credentialId), [...ids]),\n );\n\n const found = yield* owner.read(descriptor.table, where, {\n limit: 64,\n columns: mappedColumns(descriptor),\n orderBy: asc(col(descriptor.table, descriptor.credentialId)),\n });\n\n const credentials = found.rows.map((row) => {\n invariant(descriptor.isActiveStatus(row[descriptor.status]));\n\n return {\n credentialId: row[descriptor.credentialId],\n revision: row[descriptor.revision],\n };\n });\n\n const revision = snapshotPasskeySync(PasskeyRevision, {\n subjectId: subject.subjectId,\n securityRevision: subject.securityRevision,\n credentials,\n });\n\n invariant(\n new Set(revision.credentials.map((item) => item.credentialId)).size ===\n revision.credentials.length,\n );\n if (\n ids !== undefined &&\n (revision.credentials.length !== ids.length ||\n !ids.every((id) => revision.credentials.some((item) => item.credentialId === id)))\n )\n return undefined;\n\n return revision;\n });\n\n const readCredential = Effect.fn(\"passkey.readCredential\")(function* (\n mapping: any,\n subject: SubjectRead,\n rpId: string,\n protocolCredentialId: string,\n ids?: ReadonlyArray<string>,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ownership = mapping.credentialOwnership;\n const tupleKey = credentialKey(rpId, protocolCredentialId);\n\n const tuple = (yield* owner.read(\n ownership.table,\n equal(ownership.table, { [ownership.credentialKey]: tupleKey }),\n {\n limit: 1,\n columns: mappedColumns(ownership),\n },\n )).rows[0];\n\n if (tuple === undefined) return undefined;\n invariant(\n tuple[ownership.rpId] === rpId &&\n tuple[ownership.protocolCredentialId] === protocolCredentialId,\n );\n if (!ownership.isOwnedState(tuple[ownership.state])) return undefined;\n if (!mapping.subjectIds.equals(ownership.decodeSubjectId(copiedRow(tuple)), subject.nativeId))\n return undefined;\n yield* assertNow(\n sql`exists(select 1 from ${ownership.table} where ${both(owner.exact(ownership.table, tuple), ownership.ownedCondition)})`,\n );\n const descriptor = mapping.credential;\n\n const where = equal(descriptor.table, {\n [descriptor.credentialId]: tuple[ownership.credentialId],\n });\n\n let row = (yield* owner.read(descriptor.table, where, {\n limit: 1,\n lock: false,\n observe: false,\n columns: mappedColumns(descriptor),\n })).rows[0];\n\n if (row === undefined) return undefined;\n if (!descriptor.isActiveStatus(row[descriptor.status])) return undefined;\n if (!mapping.subjectIds.equals(descriptor.decodeSubjectId(copiedRow(row)), subject.nativeId))\n return undefined;\n let decoded = descriptor.decode(copiedRow(row));\n const handle = mapping.handleOwnership;\n const expectedHandleKey = handleKey(rpId, decoded.userHandle);\n\n const handleRow = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: expectedHandleKey }),\n {\n limit: 1,\n columns: mappedColumns(handle),\n },\n )).rows[0];\n\n invariant(\n row[descriptor.credentialKey] === tupleKey && row[descriptor.handleKey] === expectedHandleKey,\n );\n if (handleRow === undefined || !handle.isOwnedState(handleRow[handle.state])) return undefined;\n invariant(\n handleRow[handle.rpId] === rpId && handleRow[handle.userHandle] === decoded.userHandle,\n );\n if (!mapping.subjectIds.equals(handle.decodeSubjectId(copiedRow(handleRow)), subject.nativeId))\n return undefined;\n yield* assertNow(\n sql`exists(select 1 from ${handle.table} where ${both(owner.exact(handle.table, handleRow), handle.ownedCondition)})`,\n );\n\n const locked = (yield* owner.read(descriptor.table, where, {\n limit: 1,\n observe: false,\n columns: mappedColumns(descriptor),\n })).rows[0];\n\n if (locked === undefined) return undefined;\n invariant(\n matchesNativeRow(\n descriptor.table,\n locked,\n Object.fromEntries(\n semanticCredentialColumns(descriptor).map((column) => [column, row![column]]),\n ),\n ),\n );\n row = locked;\n decoded = descriptor.decode(copiedRow(row));\n yield* observeSemantic(descriptor, where, row);\n yield* assertNow(\n sql`exists(select 1 from ${descriptor.table} where ${both(\n owner.exact(descriptor.table, {\n [descriptor.credentialId]: row[descriptor.credentialId],\n [descriptor.credentialRevision]: row[descriptor.credentialRevision],\n }),\n descriptor.activeCondition,\n )})`,\n );\n\n const requested =\n ids === undefined ? undefined : [...new Set([...ids, decoded.credentialId])].sort();\n\n const revision = yield* readRevision(mapping, subject, requested);\n\n if (revision === undefined) return undefined;\n\n const credential = snapshotPasskeySync(PasskeyCredential, {\n ...decoded,\n revision,\n active: true,\n });\n\n invariant(credential.rpId === rpId && credential.protocolCredentialId === protocolCredentialId);\n invariant(\n credential.credentialId === row[descriptor.credentialId] &&\n credential.credentialId === tuple[ownership.credentialId],\n );\n invariant(\n credential.profile.rpId === rpId &&\n credential.profile.algorithms.includes(credential.algorithm),\n );\n invariant(\n credential.counter === Number(row[descriptor.counter]) &&\n credential.maximumCounter === Number(row[descriptor.maximumCounter]),\n );\n invariant(\n credential.maximumCounter >= credential.counter &&\n (!credential.backupState || credential.backupEligible),\n );\n if (\n !revision.credentials.some(\n (item) =>\n item.credentialId === credential.credentialId &&\n item.revision === row[descriptor.credentialRevision],\n )\n )\n return undefined;\n\n return { credential, row };\n });\n\n const lookupCredential = Effect.fn(\"passkey.lookupCredential\")(function* (\n mapping: any,\n rpId: string,\n protocolCredentialId: string,\n ) {\n const subjectId = yield* discoverSubject(mapping, rpId, protocolCredentialId);\n\n if (subjectId === undefined) return undefined;\n const subject = yield* readSubject(mapping, subjectId);\n\n if (subject === undefined) return undefined;\n\n return (yield* readCredential(mapping, subject, rpId, protocolCredentialId))?.credential;\n });\n\n const captureEnrollmentContext = Effect.fn(\"passkey.captureEnrollmentContext\")(function* (\n mapping: any,\n input: { readonly moduleId: string; readonly rpId: string; readonly subjectId: SubjectId },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (input.moduleId !== mapping.moduleId) return undefined;\n const policy = yield* readModule(mapping);\n\n if (policy === undefined || !policy.profiles.some((profile) => profile.rpId === input.rpId))\n return undefined;\n const subject = yield* readSubject(mapping.read, input.subjectId);\n\n if (subject === undefined) return undefined;\n yield* readPolicyGuards(mapping);\n const handle = mapping.read.handleOwnership;\n\n const handles = yield* owner.read(\n handle.table,\n both(\n equal(handle.table, {\n [handle.rpId]: input.rpId,\n [handle.subjectId]: subject.nativeId,\n }),\n handle.ownedCondition,\n ),\n { limit: 1, columns: mappedColumns(handle) },\n );\n\n const handleRow = handles.rows[0];\n let userHandle: PasskeyUserHandle | undefined;\n\n if (handleRow !== undefined) {\n invariant(\n handle.isOwnedState(handleRow[handle.state]) && handleRow[handle.rpId] === input.rpId,\n );\n invariant(\n mapping.read.subjectIds.equals(\n handle.decodeSubjectId(copiedRow(handleRow)),\n subject.nativeId,\n ),\n );\n // oxlint-disable-next-line no-restricted-properties -- native SQL column values enter through this untyped driver boundary.\n userHandle = Schema.decodeUnknownSync(PasskeyUserHandle)(handleRow[handle.userHandle]);\n invariant(handleRow[handle.handleKey] === handleKey(input.rpId, userHandle));\n }\n const credential = mapping.read.credential;\n\n const found = yield* owner.read(\n credential.table,\n both(\n equal(credential.table, {\n [credential.rpId]: input.rpId,\n [credential.subjectId]: subject.nativeId,\n }),\n credential.activeCondition,\n ),\n {\n limit: 64,\n columns: [\n credential.credentialId,\n credential.rpId,\n credential.protocolCredentialId,\n credential.subjectId,\n credential.credentialRevision,\n credential.status,\n ],\n orderBy: asc(col(credential.table, credential.protocolCredentialId)),\n },\n );\n\n const credentials = found.rows.map((row) => {\n invariant(\n credential.isActiveStatus(row[credential.status]) && row[credential.rpId] === input.rpId,\n );\n\n return snapshotPasskeySync(PasskeyDescriptor, {\n type: \"public-key\",\n id: row[credential.protocolCredentialId],\n });\n });\n\n invariant(new Set(credentials.map((item) => item.id)).size === credentials.length);\n const revision = yield* readRevision(mapping.read, subject);\n\n invariant(revision !== undefined);\n\n return { revision, ...(userHandle === undefined ? {} : { userHandle }), credentials };\n });\n\n return {\n readSubject,\n readModule,\n readPolicyGuards,\n discoverSubject,\n readRevision,\n readCredential,\n lookupCredential,\n captureEnrollmentContext,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"credentials.mjs","names":[],"sources":["../../../src/internal/passkey/credentials.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyCredential,\n PasskeyDescriptor,\n PasskeyRevision,\n PasskeyUserHandle,\n validatePasskeyPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport { Effect, Schema } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport interface SubjectRead {\n readonly nativeId: unknown;\n readonly subjectId: SubjectId;\n readonly securityRevision: typeof PasskeyRevision.Type.securityRevision;\n}\n\nexport interface CredentialRead {\n readonly credential: PasskeyCredential;\n readonly row: Record<string, unknown>;\n}\n\nexport const makePasskeyCredentialsKernel = (\n operations: QueryOperations,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n | \"assertNow\"\n | \"col\"\n | \"copiedRow\"\n | \"credentialKey\"\n | \"equal\"\n | \"handleKey\"\n | \"invariant\"\n | \"mappedColumns\"\n | \"observeSemantic\"\n | \"matchesNativeRow\"\n | \"semanticCredentialColumns\"\n | \"unavailable\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { asc, inArray, sql } = operations;\n\n const {\n assertNow,\n col,\n copiedRow,\n credentialKey,\n equal,\n handleKey,\n mappedColumns,\n observeSemantic,\n matchesNativeRow,\n semanticCredentialColumns,\n unavailable,\n } = state;\n\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n const readSubject = Effect.fn(\"passkey.readSubject\")(function* (\n mapping: any,\n subjectId: SubjectId,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const descriptor = mapping.subject;\n const nativeId = mapping.subjectIds.toNative(subjectId);\n\n invariant(mapping.subjectIds.toSubject(nativeId) === subjectId);\n\n const found = yield* owner.read(\n descriptor.table,\n equal(descriptor.table, { [descriptor.id]: nativeId }),\n {\n limit: 1,\n columns: [descriptor.id, descriptor.status, descriptor.securityRevision],\n },\n );\n\n const row = found.rows[0];\n\n if (row === undefined || !descriptor.isActiveStatus(row[descriptor.status])) return undefined;\n invariant(mapping.subjectIds.equals(descriptor.decodeId(copiedRow(row)), nativeId));\n\n const active = sql`exists(select 1 from ${descriptor.table} where ${both(\n owner.exact(descriptor.table, row),\n descriptor.activeCondition,\n )})`;\n\n if (!(yield* owner.check(active))) return undefined;\n owner.postconditions.push(active);\n\n // oxlint-disable-next-line no-restricted-properties -- native SQL column values enter through this untyped driver boundary.\n const securityRevision = Schema.decodeUnknownSync(PasskeyRevision.fields.securityRevision)(\n row[descriptor.securityRevision],\n );\n\n return { nativeId, subjectId, securityRevision };\n });\n\n const readModule = Effect.fn(\"passkey.readModule\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n const descriptor = mapping.module;\n\n const columns = [\n ...new Set<string>([\n descriptor.moduleId,\n descriptor.status,\n descriptor.policyRevision,\n ...descriptor.policyColumns,\n ]),\n ];\n\n const found = yield* owner.read(\n descriptor.table,\n equal(descriptor.table, { [descriptor.moduleId]: mapping.moduleId }),\n {\n limit: 1,\n columns,\n },\n );\n\n const row = found.rows[0];\n\n if (row === undefined || !descriptor.isActiveStatus(row[descriptor.status])) return undefined;\n invariant(row[descriptor.moduleId] === mapping.moduleId);\n yield* assertNow(\n sql`exists(select 1 from ${descriptor.table} where ${both(\n owner.exact(descriptor.table, row),\n descriptor.activeCondition,\n )})`,\n );\n\n return yield* validatePasskeyPolicy(descriptor.decodeMethodPolicy(copiedRow(row))).pipe(\n Effect.mapError(unavailable),\n );\n });\n\n const readPolicyGuards = Effect.fn(\"passkey.readPolicyGuards\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n\n for (const guard of [...(mapping.module.guards ?? [])].sort((a, b) =>\n a.scope < b.scope ? -1 : a.scope > b.scope ? 1 : 0,\n )) {\n const locked = yield* owner.read(guard.table, guard.where(mapping.moduleId), {\n limit: 1,\n columns: guard.columns,\n });\n\n invariant(locked.rows.length === 1);\n yield* assertNow(guard.condition(mapping.moduleId));\n }\n });\n\n /** Advisory discovery never locks a tuple before its subject. All fields are\n * reread under the common subject/tuple lock order by readCredential. */\n const discoverSubject = Effect.fn(\"passkey.discoverSubject\")(function* (\n mapping: any,\n rpId: string,\n protocolCredentialId: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const descriptor = mapping.credentialOwnership;\n\n const found = yield* owner.read(\n descriptor.table,\n equal(descriptor.table, {\n [descriptor.credentialKey]: yield* credentialKey(rpId, protocolCredentialId),\n }),\n { lock: false, observe: false, limit: 1, columns: mappedColumns(descriptor) },\n );\n\n const row = found.rows[0];\n\n if (row === undefined) return undefined;\n invariant(\n row[descriptor.rpId] === rpId &&\n row[descriptor.protocolCredentialId] === protocolCredentialId,\n );\n if (!descriptor.isOwnedState(row[descriptor.state])) return undefined;\n const native = descriptor.decodeSubjectId(copiedRow(row));\n const subjectId = mapping.subjectIds.toSubject(native);\n\n invariant(mapping.subjectIds.equals(mapping.subjectIds.toNative(subjectId), native));\n\n return subjectId as SubjectId;\n });\n\n const readRevision = Effect.fn(\"passkey.readRevision\")(function* (\n mapping: any,\n subject: SubjectRead,\n ids?: ReadonlyArray<string>,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (ids !== undefined) invariant(ids.length <= 64 && new Set(ids).size === ids.length);\n const descriptor = mapping.authority;\n\n const where = both(\n equal(descriptor.table, { [descriptor.subjectId]: subject.nativeId }),\n descriptor.activeCondition,\n ids === undefined\n ? undefined\n : ids.length === 0\n ? sql`1 = 0`\n : inArray(col(descriptor.table, descriptor.credentialId), [...ids]),\n );\n\n const found = yield* owner.read(descriptor.table, where, {\n limit: 64,\n columns: mappedColumns(descriptor),\n orderBy: asc(col(descriptor.table, descriptor.credentialId)),\n });\n\n const credentials = found.rows.map((row) => {\n invariant(descriptor.isActiveStatus(row[descriptor.status]));\n\n return {\n credentialId: row[descriptor.credentialId],\n revision: row[descriptor.revision],\n };\n });\n\n const revision = snapshotPasskeySync(PasskeyRevision, {\n subjectId: subject.subjectId,\n securityRevision: subject.securityRevision,\n credentials,\n });\n\n invariant(\n new Set(revision.credentials.map((item) => item.credentialId)).size ===\n revision.credentials.length,\n );\n if (\n ids !== undefined &&\n (revision.credentials.length !== ids.length ||\n !ids.every((id) => revision.credentials.some((item) => item.credentialId === id)))\n )\n return undefined;\n\n return revision;\n });\n\n const readCredential = Effect.fn(\"passkey.readCredential\")(function* (\n mapping: any,\n subject: SubjectRead,\n rpId: string,\n protocolCredentialId: string,\n ids?: ReadonlyArray<string>,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ownership = mapping.credentialOwnership;\n const tupleKey = yield* credentialKey(rpId, protocolCredentialId);\n\n const tuple = (yield* owner.read(\n ownership.table,\n equal(ownership.table, { [ownership.credentialKey]: tupleKey }),\n {\n limit: 1,\n columns: mappedColumns(ownership),\n },\n )).rows[0];\n\n if (tuple === undefined) return undefined;\n invariant(\n tuple[ownership.rpId] === rpId &&\n tuple[ownership.protocolCredentialId] === protocolCredentialId,\n );\n if (!ownership.isOwnedState(tuple[ownership.state])) return undefined;\n if (!mapping.subjectIds.equals(ownership.decodeSubjectId(copiedRow(tuple)), subject.nativeId))\n return undefined;\n yield* assertNow(\n sql`exists(select 1 from ${ownership.table} where ${both(owner.exact(ownership.table, tuple), ownership.ownedCondition)})`,\n );\n const descriptor = mapping.credential;\n\n const where = equal(descriptor.table, {\n [descriptor.credentialId]: tuple[ownership.credentialId],\n });\n\n let row = (yield* owner.read(descriptor.table, where, {\n limit: 1,\n lock: false,\n observe: false,\n columns: mappedColumns(descriptor),\n })).rows[0];\n\n if (row === undefined) return undefined;\n if (!descriptor.isActiveStatus(row[descriptor.status])) return undefined;\n if (!mapping.subjectIds.equals(descriptor.decodeSubjectId(copiedRow(row)), subject.nativeId))\n return undefined;\n let decoded = descriptor.decode(copiedRow(row));\n const handle = mapping.handleOwnership;\n const expectedHandleKey = yield* handleKey(rpId, decoded.userHandle);\n\n const handleRow = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: expectedHandleKey }),\n {\n limit: 1,\n columns: mappedColumns(handle),\n },\n )).rows[0];\n\n invariant(\n row[descriptor.credentialKey] === tupleKey && row[descriptor.handleKey] === expectedHandleKey,\n );\n if (handleRow === undefined || !handle.isOwnedState(handleRow[handle.state])) return undefined;\n invariant(\n handleRow[handle.rpId] === rpId && handleRow[handle.userHandle] === decoded.userHandle,\n );\n if (!mapping.subjectIds.equals(handle.decodeSubjectId(copiedRow(handleRow)), subject.nativeId))\n return undefined;\n yield* assertNow(\n sql`exists(select 1 from ${handle.table} where ${both(owner.exact(handle.table, handleRow), handle.ownedCondition)})`,\n );\n\n const locked = (yield* owner.read(descriptor.table, where, {\n limit: 1,\n observe: false,\n columns: mappedColumns(descriptor),\n })).rows[0];\n\n if (locked === undefined) return undefined;\n invariant(\n matchesNativeRow(\n descriptor.table,\n locked,\n Object.fromEntries(\n semanticCredentialColumns(descriptor).map((column) => [column, row![column]]),\n ),\n ),\n );\n row = locked;\n decoded = descriptor.decode(copiedRow(row));\n yield* observeSemantic(descriptor, where, row);\n yield* assertNow(\n sql`exists(select 1 from ${descriptor.table} where ${both(\n owner.exact(descriptor.table, {\n [descriptor.credentialId]: row[descriptor.credentialId],\n [descriptor.credentialRevision]: row[descriptor.credentialRevision],\n }),\n descriptor.activeCondition,\n )})`,\n );\n\n const requested =\n ids === undefined ? undefined : [...new Set([...ids, decoded.credentialId])].sort();\n\n const revision = yield* readRevision(mapping, subject, requested);\n\n if (revision === undefined) return undefined;\n\n const credential = snapshotPasskeySync(PasskeyCredential, {\n ...decoded,\n revision,\n active: true,\n });\n\n invariant(credential.rpId === rpId && credential.protocolCredentialId === protocolCredentialId);\n invariant(\n credential.credentialId === row[descriptor.credentialId] &&\n credential.credentialId === tuple[ownership.credentialId],\n );\n invariant(\n credential.profile.rpId === rpId &&\n credential.profile.algorithms.includes(credential.algorithm),\n );\n invariant(\n credential.counter === Number(row[descriptor.counter]) &&\n credential.maximumCounter === Number(row[descriptor.maximumCounter]),\n );\n invariant(\n credential.maximumCounter >= credential.counter &&\n (!credential.backupState || credential.backupEligible),\n );\n if (\n !revision.credentials.some(\n (item) =>\n item.credentialId === credential.credentialId &&\n item.revision === row[descriptor.credentialRevision],\n )\n )\n return undefined;\n\n return { credential, row };\n });\n\n const lookupCredential = Effect.fn(\"passkey.lookupCredential\")(function* (\n mapping: any,\n rpId: string,\n protocolCredentialId: string,\n ) {\n const subjectId = yield* discoverSubject(mapping, rpId, protocolCredentialId);\n\n if (subjectId === undefined) return undefined;\n const subject = yield* readSubject(mapping, subjectId);\n\n if (subject === undefined) return undefined;\n\n return (yield* readCredential(mapping, subject, rpId, protocolCredentialId))?.credential;\n });\n\n const captureEnrollmentContext = Effect.fn(\"passkey.captureEnrollmentContext\")(function* (\n mapping: any,\n input: { readonly moduleId: string; readonly rpId: string; readonly subjectId: SubjectId },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (input.moduleId !== mapping.moduleId) return undefined;\n const policy = yield* readModule(mapping);\n\n if (policy === undefined || !policy.profiles.some((profile) => profile.rpId === input.rpId))\n return undefined;\n const subject = yield* readSubject(mapping.read, input.subjectId);\n\n if (subject === undefined) return undefined;\n yield* readPolicyGuards(mapping);\n const handle = mapping.read.handleOwnership;\n\n const handles = yield* owner.read(\n handle.table,\n both(\n equal(handle.table, {\n [handle.rpId]: input.rpId,\n [handle.subjectId]: subject.nativeId,\n }),\n handle.ownedCondition,\n ),\n { limit: 1, columns: mappedColumns(handle) },\n );\n\n const handleRow = handles.rows[0];\n let userHandle: PasskeyUserHandle | undefined;\n\n if (handleRow !== undefined) {\n invariant(\n handle.isOwnedState(handleRow[handle.state]) && handleRow[handle.rpId] === input.rpId,\n );\n invariant(\n mapping.read.subjectIds.equals(\n handle.decodeSubjectId(copiedRow(handleRow)),\n subject.nativeId,\n ),\n );\n // oxlint-disable-next-line no-restricted-properties -- native SQL column values enter through this untyped driver boundary.\n userHandle = Schema.decodeUnknownSync(PasskeyUserHandle)(handleRow[handle.userHandle]);\n invariant(handleRow[handle.handleKey] === (yield* handleKey(input.rpId, userHandle)));\n }\n const credential = mapping.read.credential;\n\n const found = yield* owner.read(\n credential.table,\n both(\n equal(credential.table, {\n [credential.rpId]: input.rpId,\n [credential.subjectId]: subject.nativeId,\n }),\n credential.activeCondition,\n ),\n {\n limit: 64,\n columns: [\n credential.credentialId,\n credential.rpId,\n credential.protocolCredentialId,\n credential.subjectId,\n credential.credentialRevision,\n credential.status,\n ],\n orderBy: asc(col(credential.table, credential.protocolCredentialId)),\n },\n );\n\n const credentials = found.rows.map((row) => {\n invariant(\n credential.isActiveStatus(row[credential.status]) && row[credential.rpId] === input.rpId,\n );\n\n return snapshotPasskeySync(PasskeyDescriptor, {\n type: \"public-key\",\n id: row[credential.protocolCredentialId],\n });\n });\n\n invariant(new Set(credentials.map((item) => item.id)).size === credentials.length);\n const revision = yield* readRevision(mapping.read, subject);\n\n invariant(revision !== undefined);\n\n return { revision, ...(userHandle === undefined ? {} : { userHandle }), credentials };\n });\n\n return {\n readSubject,\n readModule,\n readPolicyGuards,\n discoverSubject,\n readRevision,\n readCredential,\n lookupCredential,\n captureEnrollmentContext,\n };\n};\n"],"mappings":"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"}
|