@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
|
@@ -27,7 +27,7 @@ const makePasskeyRegistrationCeremonyKernel = (operations, admission, credential
|
|
|
27
27
|
observe: false
|
|
28
28
|
})).rows[0];
|
|
29
29
|
if (advisory === void 0) return false;
|
|
30
|
-
const hashedHandle = handleKey(ceremony.profile.rpId, context.userHandle);
|
|
30
|
+
const hashedHandle = yield* handleKey(ceremony.profile.rpId, context.userHandle);
|
|
31
31
|
const held = (yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: hashedHandle }), {
|
|
32
32
|
limit: 1,
|
|
33
33
|
columns: mappedColumns(handle)
|
|
@@ -80,7 +80,7 @@ const makePasskeyRegistrationCeremonyKernel = (operations, admission, credential
|
|
|
80
80
|
const read = yield* readFlow(mapping, input.access.flowId);
|
|
81
81
|
if (read === void 0 || read.state !== "Pending" || !matchesAccess(read.ceremony, input.access)) return { _tag: "Rejected" };
|
|
82
82
|
invariant(ceremonyStorage.encode(read.ceremony) === ceremonyStorage.encode(input.ceremony));
|
|
83
|
-
if (!held || !compatiblePolicy(read.ceremony, read.policy, current) || policyStorage.encode(current) !== policyStorage.encode(input.policy) || !(yield* owner.check(liveCondition(mapping, read.ceremony))) || !(yield* owner.check(admissionCondition(mapping, [read.policy, current], read.ceremony, void 0, [], false, false)))) {
|
|
83
|
+
if (!held || !compatiblePolicy(read.ceremony, read.policy, current) || policyStorage.encode(current) !== policyStorage.encode(input.policy) || !(yield* owner.check(liveCondition(mapping, read.ceremony))) || !(yield* owner.check(yield* admissionCondition(mapping, [read.policy, current], read.ceremony, void 0, [], false, false)))) {
|
|
84
84
|
if (held) yield* releaseRegistrationCustody(mapping, read.ceremony);
|
|
85
85
|
yield* terminalFlow(mapping, read, "Rejected");
|
|
86
86
|
return { _tag: "Rejected" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration-ceremony.mjs","names":[],"sources":["../../../src/internal/passkey/registration-ceremony.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyClaim,\n type PasskeyAccess,\n type PasskeyCeremony,\n type PasskeyMethodPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport type { makePasskeyAdmissionKernel } from \"./admission\";\nimport type { makePasskeyCredentialsKernel } from \"./credentials\";\nimport type { makePasskeyFlowKernel } from \"./flow\";\nimport type { makePasskeyRegistrationCustodyKernel } from \"./registration-custody\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyRegistrationCeremonyKernel = (\n operations: QueryOperations,\n admission: Pick<\n ReturnType<typeof makePasskeyAdmissionKernel>,\n \"admissionCondition\" | \"guardAdmission\" | \"guardChargeSet\" | \"lockAdmission\" | \"readCharges\"\n >,\n credentials: Pick<\n ReturnType<typeof makePasskeyCredentialsKernel>,\n \"readModule\" | \"readPolicyGuards\"\n >,\n flow: Pick<\n ReturnType<typeof makePasskeyFlowKernel>,\n \"compatiblePolicy\" | \"liveCondition\" | \"matchesAccess\" | \"readFlow\" | \"terminalFlow\"\n >,\n registrationCustody: Pick<\n ReturnType<typeof makePasskeyRegistrationCustodyKernel>,\n \"releaseRegistrationCustody\"\n >,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"equal\" | \"handleKey\" | \"invariant\" | \"mappedColumns\" | \"policyStorage\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n\n const { admissionCondition, guardAdmission, guardChargeSet, lockAdmission, readCharges } =\n admission;\n\n const { readModule, readPolicyGuards } = credentials;\n const { compatiblePolicy, liveCondition, matchesAccess, readFlow, terminalFlow } = flow;\n const { releaseRegistrationCustody } = registrationCustody;\n const { ceremonyStorage, equal, handleKey, mappedColumns, policyStorage } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n /** The issuing registration authority owns the application value and reservation.\n * This facet only compares its opaque custody and cannot issue or provision. */\n const custody = Effect.fn(\"passkey.registrationCustody\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n pending: boolean,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n invariant(ceremony.context._tag === \"Registration\");\n\n const context = ceremony.context,\n intent = mapping.intent,\n handle = mapping.handle;\n\n const identity = { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId };\n\n const advisory = (yield* owner.read(intent.table, equal(intent.table, identity), {\n limit: 1,\n lock: false,\n observe: false,\n })).rows[0];\n\n if (advisory === undefined) return false;\n const hashedHandle = handleKey(ceremony.profile.rpId, context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashedHandle }),\n { limit: 1, columns: mappedColumns(handle) },\n )).rows[0];\n\n if (\n held === undefined ||\n !handle.isReservedState(held[handle.state]) ||\n held[handle.handleKey] !== hashedHandle ||\n held[handle.rpId] !== ceremony.profile.rpId ||\n held[handle.userHandle] !== context.userHandle ||\n held[handle.reservationId] !== advisory[intent.reservationId]\n )\n return false;\n const heldCondition = sql`exists (select 1 from ${handle.table} where ${both(owner.exact(handle.table, held), handle.reservedCondition)})`;\n\n if (!(yield* owner.check(heldCondition))) return false;\n\n const row = (yield* owner.read(intent.table, equal(intent.table, identity), {\n limit: 1,\n columns: mappedColumns(intent),\n })).rows[0];\n\n if (\n row === undefined ||\n row[intent.commandId] !== ceremony.commandId ||\n row[intent.fingerprint] !== context.fingerprint ||\n row[intent.handleKey] !== hashedHandle ||\n row[intent.reservationId] !== held[handle.reservationId] ||\n row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) ||\n typeof row[intent.applicationSnapshot] !== \"string\" ||\n new TextEncoder().encode(row[intent.applicationSnapshot]).length > 1048576 ||\n (pending && !intent.isPendingState(row[intent.state]))\n )\n return false;\n const condition = pending ? intent.pendingCondition : intent.custodyCondition;\n\n if (\n !(yield* owner.check(\n sql`exists (select 1 from ${intent.table} where ${both(owner.exact(intent.table, row), condition)})`,\n ))\n )\n return false;\n\n return [\n heldCondition,\n sql`exists (select 1 from ${intent.table} where ${both(owner.exact(intent.table, row), condition)})`,\n ];\n });\n\n const contextRegistration = Effect.fn(\"passkey.contextRegistration\")(function* (\n mapping: any,\n access: PasskeyAccess,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (access.moduleId !== mapping.moduleId) return undefined;\n const current = yield* readModule(mapping);\n\n if (current === undefined) return undefined;\n yield* readPolicyGuards(mapping);\n const table = mapping.flow;\n\n const advisory = (yield* owner.read(\n table.table,\n equal(table.table, { [table.moduleId]: mapping.moduleId, [table.flowId]: access.flowId }),\n { limit: 1, lock: false, observe: false },\n )).rows[0];\n\n if (advisory === undefined) return undefined;\n const ceremony = ceremonyStorage.decode(advisory[table.snapshot]);\n\n if (ceremony.purpose !== \"registration\" || !matchesAccess(ceremony, access)) return undefined;\n const held = yield* custody(mapping, ceremony, true);\n\n if (!held) return undefined;\n const read = yield* readFlow(mapping, access.flowId);\n\n if (\n read === undefined ||\n read.state !== \"Pending\" ||\n ceremonyStorage.encode(read.ceremony) !== ceremonyStorage.encode(ceremony) ||\n !compatiblePolicy(read.ceremony, read.policy, current) ||\n !(yield* owner.check(liveCondition(mapping, ceremony)))\n )\n return undefined;\n owner.postconditions.push(...held, liveCondition(mapping, ceremony));\n\n return read.ceremony;\n });\n\n const claimRegistration = Effect.fn(\"passkey.claimRegistration\")(function* (\n mapping: any,\n input: {\n readonly access: PasskeyAccess;\n readonly policy: PasskeyMethodPolicy;\n readonly ceremony: PasskeyCeremony;\n readonly claimId: string;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const current = yield* readModule(mapping);\n\n if (current === undefined || input.access.moduleId !== mapping.moduleId)\n return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const held = yield* custody(mapping, input.ceremony, true);\n const read = yield* readFlow(mapping, input.access.flowId);\n\n if (\n read === undefined ||\n read.state !== \"Pending\" ||\n !matchesAccess(read.ceremony, input.access)\n )\n return { _tag: \"Rejected\" } as const;\n invariant(ceremonyStorage.encode(read.ceremony) === ceremonyStorage.encode(input.ceremony));\n if (\n !held ||\n !compatiblePolicy(read.ceremony, read.policy, current) ||\n policyStorage.encode(current) !== policyStorage.encode(input.policy) ||\n !(yield* owner.check(liveCondition(mapping, read.ceremony))) ||\n !(yield* owner.check(\n admissionCondition(\n mapping,\n [read.policy, current],\n read.ceremony,\n undefined,\n [],\n false,\n false,\n ),\n ))\n ) {\n if (held) yield* releaseRegistrationCustody(mapping, read.ceremony);\n yield* terminalFlow(mapping, read, \"Rejected\");\n\n return { _tag: \"Rejected\" } as const;\n }\n yield* readCharges(mapping, read.ceremony, read.policy, undefined);\n const claimedAtMillis = yield* owner.now(mapping.clock);\n\n const claim = snapshotPasskeySync(PasskeyClaim, {\n ceremony: read.ceremony,\n claimId: input.claimId,\n claimedAtMillis,\n claimExpiresAtMillis: Math.min(\n claimedAtMillis + read.ceremony.claimLifetimeMillis,\n read.ceremony.expiresAtMillis,\n ),\n });\n\n const table = mapping.flow;\n\n yield* owner.update(\n table.table,\n { [table.moduleId]: mapping.moduleId, [table.flowId]: read.ceremony.flowId },\n {\n [table.state]: table.states.Claimed,\n [table.version]: owner.marker,\n [table.claimId]: claim.claimId,\n [table.claimedAt]: mapping.clock.encodeInstant(claim.claimedAtMillis),\n [table.claimExpiresAt]: mapping.clock.encodeInstant(claim.claimExpiresAtMillis),\n },\n );\n yield* guardChargeSet(mapping, read.ceremony, undefined);\n yield* guardAdmission(mapping, [read.policy, current], read.ceremony, undefined);\n owner.postconditions.push(...held, liveCondition(mapping, read.ceremony, claim));\n\n return { _tag: \"Claimed\", claim } as const;\n });\n\n const settleRegistration = Effect.fn(\"passkey.settleRegistration\")(function* (\n mapping: any,\n claim: PasskeyClaim,\n outcome: \"Rejected\" | \"Ambiguous\",\n ) {\n yield* readModule(mapping);\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const read = yield* readFlow(mapping, claim.ceremony.flowId);\n\n if (\n read === undefined ||\n read.state !== \"Claimed\" ||\n read.claim === undefined ||\n read.claim.claimId !== claim.claimId ||\n read.claim.claimedAtMillis !== claim.claimedAtMillis ||\n read.claim.claimExpiresAtMillis !== claim.claimExpiresAtMillis ||\n ceremonyStorage.encode(read.ceremony) !== ceremonyStorage.encode(claim.ceremony)\n )\n return \"Rejected\" as const;\n\n if (outcome === \"Rejected\") yield* releaseRegistrationCustody(mapping, claim.ceremony);\n\n return yield* terminalFlow(mapping, read, outcome);\n });\n\n return { contextRegistration, claimRegistration, settleRegistration };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registration-ceremony.mjs","names":[],"sources":["../../../src/internal/passkey/registration-ceremony.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyClaim,\n type PasskeyAccess,\n type PasskeyCeremony,\n type PasskeyMethodPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport type { makePasskeyAdmissionKernel } from \"./admission\";\nimport type { makePasskeyCredentialsKernel } from \"./credentials\";\nimport type { makePasskeyFlowKernel } from \"./flow\";\nimport type { makePasskeyRegistrationCustodyKernel } from \"./registration-custody\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyRegistrationCeremonyKernel = (\n operations: QueryOperations,\n admission: Pick<\n ReturnType<typeof makePasskeyAdmissionKernel>,\n \"admissionCondition\" | \"guardAdmission\" | \"guardChargeSet\" | \"lockAdmission\" | \"readCharges\"\n >,\n credentials: Pick<\n ReturnType<typeof makePasskeyCredentialsKernel>,\n \"readModule\" | \"readPolicyGuards\"\n >,\n flow: Pick<\n ReturnType<typeof makePasskeyFlowKernel>,\n \"compatiblePolicy\" | \"liveCondition\" | \"matchesAccess\" | \"readFlow\" | \"terminalFlow\"\n >,\n registrationCustody: Pick<\n ReturnType<typeof makePasskeyRegistrationCustodyKernel>,\n \"releaseRegistrationCustody\"\n >,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"equal\" | \"handleKey\" | \"invariant\" | \"mappedColumns\" | \"policyStorage\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n\n const { admissionCondition, guardAdmission, guardChargeSet, lockAdmission, readCharges } =\n admission;\n\n const { readModule, readPolicyGuards } = credentials;\n const { compatiblePolicy, liveCondition, matchesAccess, readFlow, terminalFlow } = flow;\n const { releaseRegistrationCustody } = registrationCustody;\n const { ceremonyStorage, equal, handleKey, mappedColumns, policyStorage } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n /** The issuing registration authority owns the application value and reservation.\n * This facet only compares its opaque custody and cannot issue or provision. */\n const custody = Effect.fn(\"passkey.registrationCustody\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n pending: boolean,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n invariant(ceremony.context._tag === \"Registration\");\n\n const context = ceremony.context,\n intent = mapping.intent,\n handle = mapping.handle;\n\n const identity = { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId };\n\n const advisory = (yield* owner.read(intent.table, equal(intent.table, identity), {\n limit: 1,\n lock: false,\n observe: false,\n })).rows[0];\n\n if (advisory === undefined) return false;\n const hashedHandle = yield* handleKey(ceremony.profile.rpId, context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashedHandle }),\n { limit: 1, columns: mappedColumns(handle) },\n )).rows[0];\n\n if (\n held === undefined ||\n !handle.isReservedState(held[handle.state]) ||\n held[handle.handleKey] !== hashedHandle ||\n held[handle.rpId] !== ceremony.profile.rpId ||\n held[handle.userHandle] !== context.userHandle ||\n held[handle.reservationId] !== advisory[intent.reservationId]\n )\n return false;\n const heldCondition = sql`exists (select 1 from ${handle.table} where ${both(owner.exact(handle.table, held), handle.reservedCondition)})`;\n\n if (!(yield* owner.check(heldCondition))) return false;\n\n const row = (yield* owner.read(intent.table, equal(intent.table, identity), {\n limit: 1,\n columns: mappedColumns(intent),\n })).rows[0];\n\n if (\n row === undefined ||\n row[intent.commandId] !== ceremony.commandId ||\n row[intent.fingerprint] !== context.fingerprint ||\n row[intent.handleKey] !== hashedHandle ||\n row[intent.reservationId] !== held[handle.reservationId] ||\n row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) ||\n typeof row[intent.applicationSnapshot] !== \"string\" ||\n new TextEncoder().encode(row[intent.applicationSnapshot]).length > 1048576 ||\n (pending && !intent.isPendingState(row[intent.state]))\n )\n return false;\n const condition = pending ? intent.pendingCondition : intent.custodyCondition;\n\n if (\n !(yield* owner.check(\n sql`exists (select 1 from ${intent.table} where ${both(owner.exact(intent.table, row), condition)})`,\n ))\n )\n return false;\n\n return [\n heldCondition,\n sql`exists (select 1 from ${intent.table} where ${both(owner.exact(intent.table, row), condition)})`,\n ];\n });\n\n const contextRegistration = Effect.fn(\"passkey.contextRegistration\")(function* (\n mapping: any,\n access: PasskeyAccess,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (access.moduleId !== mapping.moduleId) return undefined;\n const current = yield* readModule(mapping);\n\n if (current === undefined) return undefined;\n yield* readPolicyGuards(mapping);\n const table = mapping.flow;\n\n const advisory = (yield* owner.read(\n table.table,\n equal(table.table, { [table.moduleId]: mapping.moduleId, [table.flowId]: access.flowId }),\n { limit: 1, lock: false, observe: false },\n )).rows[0];\n\n if (advisory === undefined) return undefined;\n const ceremony = ceremonyStorage.decode(advisory[table.snapshot]);\n\n if (ceremony.purpose !== \"registration\" || !matchesAccess(ceremony, access)) return undefined;\n const held = yield* custody(mapping, ceremony, true);\n\n if (!held) return undefined;\n const read = yield* readFlow(mapping, access.flowId);\n\n if (\n read === undefined ||\n read.state !== \"Pending\" ||\n ceremonyStorage.encode(read.ceremony) !== ceremonyStorage.encode(ceremony) ||\n !compatiblePolicy(read.ceremony, read.policy, current) ||\n !(yield* owner.check(liveCondition(mapping, ceremony)))\n )\n return undefined;\n owner.postconditions.push(...held, liveCondition(mapping, ceremony));\n\n return read.ceremony;\n });\n\n const claimRegistration = Effect.fn(\"passkey.claimRegistration\")(function* (\n mapping: any,\n input: {\n readonly access: PasskeyAccess;\n readonly policy: PasskeyMethodPolicy;\n readonly ceremony: PasskeyCeremony;\n readonly claimId: string;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const current = yield* readModule(mapping);\n\n if (current === undefined || input.access.moduleId !== mapping.moduleId)\n return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const held = yield* custody(mapping, input.ceremony, true);\n const read = yield* readFlow(mapping, input.access.flowId);\n\n if (\n read === undefined ||\n read.state !== \"Pending\" ||\n !matchesAccess(read.ceremony, input.access)\n )\n return { _tag: \"Rejected\" } as const;\n invariant(ceremonyStorage.encode(read.ceremony) === ceremonyStorage.encode(input.ceremony));\n if (\n !held ||\n !compatiblePolicy(read.ceremony, read.policy, current) ||\n policyStorage.encode(current) !== policyStorage.encode(input.policy) ||\n !(yield* owner.check(liveCondition(mapping, read.ceremony))) ||\n !(yield* owner.check(\n yield* admissionCondition(\n mapping,\n [read.policy, current],\n read.ceremony,\n undefined,\n [],\n false,\n false,\n ),\n ))\n ) {\n if (held) yield* releaseRegistrationCustody(mapping, read.ceremony);\n yield* terminalFlow(mapping, read, \"Rejected\");\n\n return { _tag: \"Rejected\" } as const;\n }\n yield* readCharges(mapping, read.ceremony, read.policy, undefined);\n const claimedAtMillis = yield* owner.now(mapping.clock);\n\n const claim = snapshotPasskeySync(PasskeyClaim, {\n ceremony: read.ceremony,\n claimId: input.claimId,\n claimedAtMillis,\n claimExpiresAtMillis: Math.min(\n claimedAtMillis + read.ceremony.claimLifetimeMillis,\n read.ceremony.expiresAtMillis,\n ),\n });\n\n const table = mapping.flow;\n\n yield* owner.update(\n table.table,\n { [table.moduleId]: mapping.moduleId, [table.flowId]: read.ceremony.flowId },\n {\n [table.state]: table.states.Claimed,\n [table.version]: owner.marker,\n [table.claimId]: claim.claimId,\n [table.claimedAt]: mapping.clock.encodeInstant(claim.claimedAtMillis),\n [table.claimExpiresAt]: mapping.clock.encodeInstant(claim.claimExpiresAtMillis),\n },\n );\n yield* guardChargeSet(mapping, read.ceremony, undefined);\n yield* guardAdmission(mapping, [read.policy, current], read.ceremony, undefined);\n owner.postconditions.push(...held, liveCondition(mapping, read.ceremony, claim));\n\n return { _tag: \"Claimed\", claim } as const;\n });\n\n const settleRegistration = Effect.fn(\"passkey.settleRegistration\")(function* (\n mapping: any,\n claim: PasskeyClaim,\n outcome: \"Rejected\" | \"Ambiguous\",\n ) {\n yield* readModule(mapping);\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const read = yield* readFlow(mapping, claim.ceremony.flowId);\n\n if (\n read === undefined ||\n read.state !== \"Claimed\" ||\n read.claim === undefined ||\n read.claim.claimId !== claim.claimId ||\n read.claim.claimedAtMillis !== claim.claimedAtMillis ||\n read.claim.claimExpiresAtMillis !== claim.claimExpiresAtMillis ||\n ceremonyStorage.encode(read.ceremony) !== ceremonyStorage.encode(claim.ceremony)\n )\n return \"Rejected\" as const;\n\n if (outcome === \"Rejected\") yield* releaseRegistrationCustody(mapping, claim.ceremony);\n\n return yield* terminalFlow(mapping, read, outcome);\n });\n\n return { contextRegistration, claimRegistration, settleRegistration };\n};\n"],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ const makePasskeyRegistrationCustodyKernel = (state) => {
|
|
|
10
10
|
const owner = yield* CurrentPasskeyTransaction;
|
|
11
11
|
const handle = mapping.handle;
|
|
12
12
|
const intent = mapping.intent;
|
|
13
|
-
const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
|
|
13
|
+
const hashed = yield* handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
|
|
14
14
|
const held = (yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: hashed }), { limit: 1 })).rows[0];
|
|
15
15
|
const row = (yield* owner.read(intent.table, equal(intent.table, {
|
|
16
16
|
[intent.moduleId]: mapping.moduleId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration-custody.mjs","names":[],"sources":["../../../src/internal/passkey/registration-custody.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport type { PasskeyCeremony } from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyRegistrationCustodyKernel = (\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"equal\" | \"handleKey\" | \"invariant\"\n >,\n) => {\n const { ceremonyStorage, equal, handleKey } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n\n /** SQL-local writers have no external provisioning work. A definite terminal\n * rejection can release only its exact still-reserved handle; ambiguous owners\n * retain custody, and an accepted transaction no longer matches this reservation. */\n const releaseRegistrationCustody = Effect.fn(\"passkey.releaseRegistrationCustody\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n ) {\n if (mapping.registration === undefined || ceremony.context._tag !== \"Registration\") return;\n const owner = yield* CurrentPasskeyTransaction;\n const handle = mapping.handle;\n const intent = mapping.intent;\n const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n )).rows[0];\n\n const row = (yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n )).rows[0];\n\n if (row === undefined || !intent.isPendingState(row[intent.state])) return;\n invariant(\n held !== undefined &&\n handle.isReservedState(held[handle.state]) &&\n held[handle.reservationId] === row[intent.reservationId] &&\n row[intent.handleKey] === hashed &&\n row[intent.commandId] === ceremony.commandId &&\n row[intent.ceremonySnapshot] === ceremonyStorage.encode(ceremony),\n );\n yield* owner.update(\n intent.table,\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n { [intent.state]: intent.rejectedState, [intent.version]: owner.marker },\n );\n yield* owner.remove(handle.table, { [handle.handleKey]: hashed });\n });\n\n /** Keep the command/flow tombstone after retention, without retaining the\n * application registration payload or ceremony. Ambiguous custody is never scrubbed. */\n const scrubRegistrationIntent = Effect.fn(\"passkey.scrubRegistrationIntent\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n ) {\n if (mapping.registration === undefined || ceremony.context._tag !== \"Registration\") return;\n const owner = yield* CurrentPasskeyTransaction;\n const intent = mapping.intent;\n const identity = { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId };\n\n const row = (yield* owner.read(intent.table, equal(intent.table, identity), { limit: 1 }))\n .rows[0];\n\n if (row === undefined) return;\n invariant(\n (row[intent.state] === intent.acceptedState || row[intent.state] === intent.rejectedState) &&\n row[intent.commandId] === ceremony.commandId &&\n row[intent.ceremonySnapshot] === ceremonyStorage.encode(ceremony),\n );\n yield* owner.update(intent.table, identity, {\n [intent.applicationSnapshot]: \"\",\n [intent.ceremonySnapshot]: \"\",\n [intent.version]: owner.marker,\n });\n });\n\n return { releaseRegistrationCustody, scrubRegistrationIntent };\n};\n"],"mappings":";;;AAQA,MAAa,wCACX,UAIG;CACH,MAAM,EAAE,iBAAiB,OAAO,cAAc;CAC9C,MAAM,YAA+C,MAAM;CA0E3D,OAAO;EAAE,4BArE0B,OAAO,GAAG,oCAAoC,CAAC,CAAC,WACjF,SACA,UACA;GACA,IAAI,QAAQ,iBAAiB,KAAA,KAAa,SAAS,QAAQ,SAAS,gBAAgB;GACpF,MAAM,QAAQ,OAAO;GACrB,MAAM,SAAS,QAAQ;GACvB,MAAM,SAAS,QAAQ;GACvB,MAAM,SAAS,UAAU,SAAS,QAAQ,MAAM,SAAS,QAAQ,UAAU;
|
|
1
|
+
{"version":3,"file":"registration-custody.mjs","names":[],"sources":["../../../src/internal/passkey/registration-custody.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport type { PasskeyCeremony } from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyRegistrationCustodyKernel = (\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"equal\" | \"handleKey\" | \"invariant\"\n >,\n) => {\n const { ceremonyStorage, equal, handleKey } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n\n /** SQL-local writers have no external provisioning work. A definite terminal\n * rejection can release only its exact still-reserved handle; ambiguous owners\n * retain custody, and an accepted transaction no longer matches this reservation. */\n const releaseRegistrationCustody = Effect.fn(\"passkey.releaseRegistrationCustody\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n ) {\n if (mapping.registration === undefined || ceremony.context._tag !== \"Registration\") return;\n const owner = yield* CurrentPasskeyTransaction;\n const handle = mapping.handle;\n const intent = mapping.intent;\n const hashed = yield* handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n )).rows[0];\n\n const row = (yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n )).rows[0];\n\n if (row === undefined || !intent.isPendingState(row[intent.state])) return;\n invariant(\n held !== undefined &&\n handle.isReservedState(held[handle.state]) &&\n held[handle.reservationId] === row[intent.reservationId] &&\n row[intent.handleKey] === hashed &&\n row[intent.commandId] === ceremony.commandId &&\n row[intent.ceremonySnapshot] === ceremonyStorage.encode(ceremony),\n );\n yield* owner.update(\n intent.table,\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n { [intent.state]: intent.rejectedState, [intent.version]: owner.marker },\n );\n yield* owner.remove(handle.table, { [handle.handleKey]: hashed });\n });\n\n /** Keep the command/flow tombstone after retention, without retaining the\n * application registration payload or ceremony. Ambiguous custody is never scrubbed. */\n const scrubRegistrationIntent = Effect.fn(\"passkey.scrubRegistrationIntent\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n ) {\n if (mapping.registration === undefined || ceremony.context._tag !== \"Registration\") return;\n const owner = yield* CurrentPasskeyTransaction;\n const intent = mapping.intent;\n const identity = { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId };\n\n const row = (yield* owner.read(intent.table, equal(intent.table, identity), { limit: 1 }))\n .rows[0];\n\n if (row === undefined) return;\n invariant(\n (row[intent.state] === intent.acceptedState || row[intent.state] === intent.rejectedState) &&\n row[intent.commandId] === ceremony.commandId &&\n row[intent.ceremonySnapshot] === ceremonyStorage.encode(ceremony),\n );\n yield* owner.update(intent.table, identity, {\n [intent.applicationSnapshot]: \"\",\n [intent.ceremonySnapshot]: \"\",\n [intent.version]: owner.marker,\n });\n });\n\n return { releaseRegistrationCustody, scrubRegistrationIntent };\n};\n"],"mappings":";;;AAQA,MAAa,wCACX,UAIG;CACH,MAAM,EAAE,iBAAiB,OAAO,cAAc;CAC9C,MAAM,YAA+C,MAAM;CA0E3D,OAAO;EAAE,4BArE0B,OAAO,GAAG,oCAAoC,CAAC,CAAC,WACjF,SACA,UACA;GACA,IAAI,QAAQ,iBAAiB,KAAA,KAAa,SAAS,QAAQ,SAAS,gBAAgB;GACpF,MAAM,QAAQ,OAAO;GACrB,MAAM,SAAS,QAAQ;GACvB,MAAM,SAAS,QAAQ;GACvB,MAAM,SAAS,OAAO,UAAU,SAAS,QAAQ,MAAM,SAAS,QAAQ,UAAU;GAElF,MAAM,QAAQ,OAAO,MAAM,KACzB,OAAO,OACP,MAAM,OAAO,OAAO,GAAG,OAAO,YAAY,OAAO,CAAC,GAClD,EAAE,OAAO,EAAE,CACb,EAAA,CAAG,KAAK;GAER,MAAM,OAAO,OAAO,MAAM,KACxB,OAAO,OACP,MAAM,OAAO,OAAO;KACjB,OAAO,WAAW,QAAQ;KAC1B,OAAO,SAAS,SAAS;GAC5B,CAAC,GACD,EAAE,OAAO,EAAE,CACb,EAAA,CAAG,KAAK;GAER,IAAI,QAAQ,KAAA,KAAa,CAAC,OAAO,eAAe,IAAI,OAAO,MAAM,GAAG;GACpE,UACE,SAAS,KAAA,KACP,OAAO,gBAAgB,KAAK,OAAO,MAAM,KACzC,KAAK,OAAO,mBAAmB,IAAI,OAAO,kBAC1C,IAAI,OAAO,eAAe,UAC1B,IAAI,OAAO,eAAe,SAAS,aACnC,IAAI,OAAO,sBAAsB,gBAAgB,OAAO,QAAQ,CACpE;GACA,OAAO,MAAM,OACX,OAAO,OACP;KAAG,OAAO,WAAW,QAAQ;KAAW,OAAO,SAAS,SAAS;GAAO,GACxE;KAAG,OAAO,QAAQ,OAAO;KAAgB,OAAO,UAAU,MAAM;GAAO,CACzE;GACA,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG,OAAO,YAAY,OAAO,CAAC;EAClE,CA6BkC;EAAG,yBAzBL,OAAO,GAAG,iCAAiC,CAAC,CAAC,WAC3E,SACA,UACA;GACA,IAAI,QAAQ,iBAAiB,KAAA,KAAa,SAAS,QAAQ,SAAS,gBAAgB;GACpF,MAAM,QAAQ,OAAO;GACrB,MAAM,SAAS,QAAQ;GACvB,MAAM,WAAW;KAAG,OAAO,WAAW,QAAQ;KAAW,OAAO,SAAS,SAAS;GAAO;GAEzF,MAAM,OAAO,OAAO,MAAM,KAAK,OAAO,OAAO,MAAM,OAAO,OAAO,QAAQ,GAAG,EAAE,OAAO,EAAE,CAAC,EAAA,CACrF,KAAK;GAER,IAAI,QAAQ,KAAA,GAAW;GACvB,WACG,IAAI,OAAO,WAAW,OAAO,iBAAiB,IAAI,OAAO,WAAW,OAAO,kBAC1E,IAAI,OAAO,eAAe,SAAS,aACnC,IAAI,OAAO,sBAAsB,gBAAgB,OAAO,QAAQ,CACpE;GACA,OAAO,MAAM,OAAO,OAAO,OAAO,UAAU;KACzC,OAAO,sBAAsB;KAC7B,OAAO,mBAAmB;KAC1B,OAAO,UAAU,MAAM;GAC1B,CAAC;EACH,CAE2D;CAAE;AAC/D"}
|
|
@@ -13,18 +13,18 @@ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials,
|
|
|
13
13
|
const invariant = state.invariant;
|
|
14
14
|
const { digest, insertCredential, jsonStorage, validRegistration } = writeState;
|
|
15
15
|
const { both } = transactions;
|
|
16
|
-
const registrationData = (mapping, registration)
|
|
16
|
+
const registrationData = Effect.fnUntraced(function* (mapping, registration) {
|
|
17
17
|
const codec = jsonStorage(mapping.registration.schema);
|
|
18
18
|
const encoded = codec.encode(registration);
|
|
19
19
|
invariant(new TextEncoder().encode(encoded).length <= 1048576);
|
|
20
20
|
return {
|
|
21
21
|
encoded,
|
|
22
22
|
registration: codec.decode(encoded),
|
|
23
|
-
fingerprint: digest(mapping.registration.schema, registration)
|
|
23
|
+
fingerprint: yield* digest(mapping.registration.schema, registration)
|
|
24
24
|
};
|
|
25
|
-
};
|
|
26
|
-
const describe = (mapping, registration)
|
|
27
|
-
const data = registrationData(mapping, registration);
|
|
25
|
+
});
|
|
26
|
+
const describe = Effect.fnUntraced(function* (mapping, registration) {
|
|
27
|
+
const data = yield* registrationData(mapping, registration);
|
|
28
28
|
const labels = Schema.decodeSync(Schema.Struct({
|
|
29
29
|
name: PasskeyLabel,
|
|
30
30
|
displayName: PasskeyLabel
|
|
@@ -33,11 +33,11 @@ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials,
|
|
|
33
33
|
...data,
|
|
34
34
|
...labels
|
|
35
35
|
};
|
|
36
|
-
};
|
|
36
|
+
});
|
|
37
37
|
return {
|
|
38
38
|
inspectRegistration: Effect.fn("passkey.inspectRegistration")(function* (mapping, registration) {
|
|
39
39
|
const owner = yield* CurrentPasskeyTransaction;
|
|
40
|
-
const value = describe(mapping, registration);
|
|
40
|
+
const value = yield* describe(mapping, registration);
|
|
41
41
|
const current = yield* readModule(mapping);
|
|
42
42
|
yield* readPolicyGuards(mapping);
|
|
43
43
|
const eligible = current !== void 0 && (yield* owner.check(mapping.registration.eligible(value.registration)));
|
|
@@ -52,7 +52,7 @@ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials,
|
|
|
52
52
|
const owner = yield* CurrentPasskeyTransaction;
|
|
53
53
|
const ceremony = input.ceremony;
|
|
54
54
|
if (ceremony.moduleId !== mapping.moduleId || ceremony.purpose !== "registration" || ceremony.context._tag !== "Registration" || !ceremony.profile.primarySignIn || ceremony.profile.userVerification !== "required" || ceremony.profile.residentKey !== "required" || ceremony.allowedCredentials.length !== 0) return { _tag: "Rejected" };
|
|
55
|
-
const value = describe(mapping, input.registration);
|
|
55
|
+
const value = yield* describe(mapping, input.registration);
|
|
56
56
|
if (value.fingerprint !== ceremony.context.fingerprint || value.name !== ceremony.context.name || value.displayName !== ceremony.context.displayName) return { _tag: "Rejected" };
|
|
57
57
|
if ((yield* readModule(mapping)) === void 0) return { _tag: "Rejected" };
|
|
58
58
|
yield* lockAdmission(mapping);
|
|
@@ -60,7 +60,7 @@ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials,
|
|
|
60
60
|
const eligible = mapping.registration.eligible(value.registration);
|
|
61
61
|
if (!(yield* owner.check(eligible))) return { _tag: "Rejected" };
|
|
62
62
|
const handle = mapping.handle;
|
|
63
|
-
const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
|
|
63
|
+
const hashed = yield* handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
|
|
64
64
|
const absentHandle = yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: hashed }), { limit: 1 });
|
|
65
65
|
const intent = mapping.intent;
|
|
66
66
|
const absentCommand = yield* owner.read(intent.table, equal(intent.table, {
|
|
@@ -121,12 +121,12 @@ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials,
|
|
|
121
121
|
yield* lockAdmission(mapping);
|
|
122
122
|
yield* readPolicyGuards(mapping);
|
|
123
123
|
const tuple = mapping.read.credentialOwnership;
|
|
124
|
-
const absentCredential = yield* owner.read(tuple.table, equal(tuple.table, { [tuple.credentialKey]: credentialKey(ceremony.profile.rpId, input.verified.protocolCredentialId) }), {
|
|
124
|
+
const absentCredential = yield* owner.read(tuple.table, equal(tuple.table, { [tuple.credentialKey]: yield* credentialKey(ceremony.profile.rpId, input.verified.protocolCredentialId) }), {
|
|
125
125
|
limit: 1,
|
|
126
126
|
observe: false
|
|
127
127
|
});
|
|
128
128
|
const handle = mapping.handle;
|
|
129
|
-
const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
|
|
129
|
+
const hashed = yield* handleKey(ceremony.profile.rpId, ceremony.context.userHandle);
|
|
130
130
|
const held = (yield* owner.read(handle.table, equal(handle.table, { [handle.handleKey]: hashed }), { limit: 1 })).rows[0];
|
|
131
131
|
const intent = mapping.intent;
|
|
132
132
|
const row = (yield* owner.read(intent.table, equal(intent.table, {
|
|
@@ -141,7 +141,7 @@ const makePasskeyRegistrationWriteKernel = (operations, admission, credentials,
|
|
|
141
141
|
return { _tag: "Rejected" };
|
|
142
142
|
});
|
|
143
143
|
if (absentCredential.rows.length !== 0 || held === void 0 || row === void 0 || !handle.isReservedState(held[handle.state]) || !intent.isPendingState(row[intent.state]) || row[intent.commandId] !== ceremony.commandId || row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) || row[intent.fingerprint] !== ceremony.context.fingerprint || row[intent.handleKey] !== hashed || held[handle.reservationId] !== row[intent.reservationId] || held[handle.rpId] !== ceremony.profile.rpId || held[handle.userHandle] !== ceremony.context.userHandle || !compatiblePolicy(ceremony, read.policy, current) || !validRegistration(ceremony, input.verified) || !input.verified.userVerified || !ceremony.profile.primarySignIn || !(yield* owner.check(liveCondition(mapping, ceremony, input.claim)))) return yield* reject;
|
|
144
|
-
const data = describe(mapping, jsonStorage(mapping.registration.schema).decode(row[intent.applicationSnapshot]));
|
|
144
|
+
const data = yield* describe(mapping, jsonStorage(mapping.registration.schema).decode(row[intent.applicationSnapshot]));
|
|
145
145
|
if (data.encoded !== row[intent.applicationSnapshot] || data.fingerprint !== ceremony.context.fingerprint || data.name !== ceremony.context.name || data.displayName !== ceremony.context.displayName) return yield* reject;
|
|
146
146
|
const eligible = mapping.registration.eligible(data.registration);
|
|
147
147
|
if (!(yield* owner.check(eligible))) return yield* reject;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration-write.mjs","names":[],"sources":["../../../src/internal/passkey/registration-write.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyLabel,\n PasskeyRevision,\n type PasskeyCeremony,\n type PasskeyClaim,\n type PasskeyRegistrationVerified,\n type PasskeyMethodPolicy,\n} from \"@yielded/auth/Passkey\";\nimport { SubjectId } from \"@yielded/auth/Schema\";\nimport { Effect, Schema } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport type { makePasskeyAdmissionKernel } from \"./admission\";\nimport type { makePasskeyCredentialsKernel } from \"./credentials\";\nimport type { makePasskeyFlowKernel } from \"./flow\";\nimport type { makePasskeyRegistrationCustodyKernel } from \"./registration-custody\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\nimport type { makePasskeyWriteStateKernel } from \"./write-state\";\n\nexport const makePasskeyRegistrationWriteKernel = (\n operations: QueryOperations,\n admission: Pick<\n ReturnType<typeof makePasskeyAdmissionKernel>,\n \"lockAdmission\" | \"readCharges\" | \"guardChargeSet\"\n >,\n credentials: Pick<\n ReturnType<typeof makePasskeyCredentialsKernel>,\n \"readModule\" | \"readPolicyGuards\"\n >,\n flow: Pick<\n ReturnType<typeof makePasskeyFlowKernel>,\n | \"compatiblePolicy\"\n | \"exactClaim\"\n | \"issueAssertion\"\n | \"liveCondition\"\n | \"readFlow\"\n | \"terminalFlow\"\n >,\n registrationCustody: Pick<\n ReturnType<typeof makePasskeyRegistrationCustodyKernel>,\n \"releaseRegistrationCustody\"\n >,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"credentialKey\" | \"equal\" | \"handleKey\" | \"invariant\"\n >,\n writeState: Pick<\n ReturnType<typeof makePasskeyWriteStateKernel>,\n \"digest\" | \"insertCredential\" | \"jsonStorage\" | \"validRegistration\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n const { lockAdmission, readCharges, guardChargeSet } = admission;\n const { readModule, readPolicyGuards } = credentials;\n\n const { compatiblePolicy, exactClaim, issueAssertion, liveCondition, readFlow, terminalFlow } =\n flow;\n\n const { releaseRegistrationCustody } = registrationCustody;\n const { ceremonyStorage, credentialKey, equal, handleKey } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { digest, insertCredential, jsonStorage, validRegistration } = writeState;\n const { both } = transactions;\n\n const registrationData = (mapping: any, registration: any) => {\n const codec = jsonStorage(mapping.registration.schema);\n const encoded = codec.encode(registration);\n\n invariant(new TextEncoder().encode(encoded).length <= 1048576);\n\n return {\n encoded,\n registration: codec.decode(encoded),\n fingerprint: digest(mapping.registration.schema, registration),\n };\n };\n\n const describe = (mapping: any, registration: any) => {\n const data = registrationData(mapping, registration);\n\n const labels = Schema.decodeSync(\n Schema.Struct({ name: PasskeyLabel, displayName: PasskeyLabel }),\n )(mapping.registration.describe(data.registration));\n\n return { ...data, ...labels };\n };\n\n const inspectRegistration = Effect.fn(\"passkey.inspectRegistration\")(function* (\n mapping: any,\n registration: any,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const value = describe(mapping, registration);\n const current = yield* readModule(mapping);\n\n yield* readPolicyGuards(mapping);\n\n const eligible =\n current !== undefined &&\n (yield* owner.check(mapping.registration.eligible(value.registration)));\n\n return {\n fingerprint: value.fingerprint,\n name: value.name,\n displayName: value.displayName,\n eligible,\n };\n });\n\n const issueRegistration = Effect.fn(\"passkey.issueRegistration\")(function* (\n mapping: any,\n input: {\n readonly ceremony: PasskeyCeremony;\n readonly policy: PasskeyMethodPolicy;\n readonly registration: any;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ceremony = input.ceremony;\n\n if (\n ceremony.moduleId !== mapping.moduleId ||\n ceremony.purpose !== \"registration\" ||\n ceremony.context._tag !== \"Registration\" ||\n !ceremony.profile.primarySignIn ||\n ceremony.profile.userVerification !== \"required\" ||\n ceremony.profile.residentKey !== \"required\" ||\n ceremony.allowedCredentials.length !== 0\n )\n return { _tag: \"Rejected\" } as const;\n const value = describe(mapping, input.registration);\n\n if (\n value.fingerprint !== ceremony.context.fingerprint ||\n value.name !== ceremony.context.name ||\n value.displayName !== ceremony.context.displayName\n )\n return { _tag: \"Rejected\" } as const;\n if ((yield* readModule(mapping)) === undefined) return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const eligible = mapping.registration.eligible(value.registration);\n\n if (!(yield* owner.check(eligible))) return { _tag: \"Rejected\" } as const;\n const handle = mapping.handle;\n const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const absentHandle = yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n );\n\n const intent = mapping.intent;\n\n const absentCommand = yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.commandId]: ceremony.commandId,\n }),\n { limit: 1 },\n );\n\n const absentFlow = yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n );\n\n if (\n absentHandle.rows.length !== 0 ||\n absentCommand.rows.length !== 0 ||\n absentFlow.rows.length !== 0\n )\n return { _tag: \"Rejected\" } as const;\n const issued = yield* issueAssertion(mapping, ceremony, input.policy);\n\n if (issued._tag !== \"Issued\") return issued;\n\n const reserved = yield* owner.insert(\n handle.table,\n {\n ...handle.encodeInsert({ ceremony, reservationId: owner.marker }),\n [handle.handleKey]: hashed,\n [handle.rpId]: ceremony.profile.rpId,\n [handle.userHandle]: ceremony.context.userHandle,\n [handle.state]: handle.reservedState,\n [handle.version]: owner.marker,\n [handle.reservationId]: owner.marker,\n [mapping.read.handleOwnership.subjectId]: null,\n },\n { [handle.handleKey]: hashed },\n );\n\n absentHandle.rows = reserved.rows;\n\n const inserted = yield* owner.insert(\n intent.table,\n {\n ...intent.encodeInsert({\n ceremony,\n registration: value.registration,\n reservationId: owner.marker,\n }),\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n [intent.commandId]: ceremony.commandId,\n [intent.state]: intent.pendingState,\n [intent.version]: owner.marker,\n [intent.fingerprint]: value.fingerprint,\n [intent.handleKey]: hashed,\n [intent.reservationId]: owner.marker,\n [intent.ceremonySnapshot]: ceremonyStorage.encode(ceremony),\n [intent.applicationSnapshot]: value.encoded,\n },\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n );\n\n absentCommand.rows = inserted.rows;\n absentFlow.rows = inserted.rows;\n owner.postconditions.push(eligible);\n\n return issued;\n });\n\n const completeRegistration = Effect.fn(\"passkey.completeRegistration\")(function* (\n mapping: any,\n input: {\n readonly claim: PasskeyClaim;\n readonly verified: PasskeyRegistrationVerified;\n readonly nowMillis: number;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ceremony = input.claim.ceremony;\n\n if (\n ceremony.moduleId !== mapping.moduleId ||\n ceremony.purpose !== \"registration\" ||\n ceremony.context._tag !== \"Registration\"\n )\n return { _tag: \"Rejected\" } as const;\n const current = yield* readModule(mapping);\n\n if (current === undefined) return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const tuple = mapping.read.credentialOwnership;\n\n const absentCredential = yield* owner.read(\n tuple.table,\n equal(tuple.table, {\n [tuple.credentialKey]: credentialKey(\n ceremony.profile.rpId,\n input.verified.protocolCredentialId,\n ),\n }),\n { limit: 1, observe: false },\n );\n\n const handle = mapping.handle;\n const hashed = handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n )).rows[0];\n\n const intent = mapping.intent;\n\n const row = (yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n )).rows[0];\n\n const read = yield* readFlow(mapping, ceremony.flowId);\n\n if (read === undefined || !exactClaim(read, input.claim)) return { _tag: \"Rejected\" } as const;\n\n const reject = Effect.gen(function* () {\n yield* releaseRegistrationCustody(mapping, ceremony);\n yield* terminalFlow(mapping, read, \"Rejected\");\n\n return { _tag: \"Rejected\" } as const;\n });\n\n if (\n absentCredential.rows.length !== 0 ||\n held === undefined ||\n row === undefined ||\n !handle.isReservedState(held[handle.state]) ||\n !intent.isPendingState(row[intent.state]) ||\n row[intent.commandId] !== ceremony.commandId ||\n row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) ||\n row[intent.fingerprint] !== ceremony.context.fingerprint ||\n row[intent.handleKey] !== hashed ||\n held[handle.reservationId] !== row[intent.reservationId] ||\n held[handle.rpId] !== ceremony.profile.rpId ||\n held[handle.userHandle] !== ceremony.context.userHandle ||\n !compatiblePolicy(ceremony, read.policy, current) ||\n !validRegistration(ceremony, input.verified) ||\n !input.verified.userVerified ||\n !ceremony.profile.primarySignIn ||\n !(yield* owner.check(liveCondition(mapping, ceremony, input.claim)))\n )\n return yield* reject;\n\n const data = describe(\n mapping,\n jsonStorage(mapping.registration.schema).decode(row[intent.applicationSnapshot]),\n );\n\n if (\n data.encoded !== row[intent.applicationSnapshot] ||\n data.fingerprint !== ceremony.context.fingerprint ||\n data.name !== ceremony.context.name ||\n data.displayName !== ceremony.context.displayName\n )\n return yield* reject;\n const eligible = mapping.registration.eligible(data.registration);\n\n if (!(yield* owner.check(eligible))) return yield* reject;\n\n const provision = mapping.registration.subject({\n registration: data.registration,\n ceremony,\n marker: owner.marker,\n });\n\n const nativeId = provision.subjectId;\n const subjectId = SubjectId.make(mapping.read.subjectIds.toSubject(nativeId));\n\n invariant(\n mapping.read.subjectIds.equals(mapping.read.subjectIds.toNative(subjectId), nativeId),\n );\n const table = mapping.read.subject;\n\n const absentSubject = yield* owner.read(\n table.table,\n equal(table.table, { [table.id]: nativeId }),\n { limit: 1 },\n );\n\n if (absentSubject.rows.length !== 0) return yield* reject;\n\n const values = {\n ...provision.values,\n [table.id]: nativeId,\n [table.status]: mapping.registration.activeStatus,\n [table.securityRevision]: owner.marker,\n };\n\n const inserted = yield* owner.insert(table.table, values, { [table.id]: nativeId });\n\n absentSubject.rows = inserted.rows;\n\n const subject = {\n subjectId,\n nativeId,\n row: values,\n revision: PasskeyRevision.make({\n subjectId,\n securityRevision: PasskeyRevision.fields.securityRevision.make(owner.marker),\n credentials: [],\n }),\n };\n\n yield* owner.update(\n handle.table,\n { [handle.handleKey]: hashed },\n {\n [mapping.read.handleOwnership.subjectId]: nativeId,\n [handle.state]: mapping.write.handleOwnership.ownedState,\n [handle.version]: owner.marker,\n [handle.reservationId]: null,\n },\n );\n yield* readCharges(mapping, ceremony, read.policy);\n yield* guardChargeSet(mapping, ceremony);\n yield* insertCredential(\n mapping,\n subject,\n ceremony,\n input.verified,\n yield* owner.now(mapping.clock),\n );\n yield* owner.update(\n intent.table,\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n { [intent.state]: intent.acceptedState, [intent.version]: owner.marker },\n );\n const flow = mapping.flow;\n\n yield* owner.update(\n flow.table,\n { [flow.moduleId]: mapping.moduleId, [flow.flowId]: ceremony.flowId },\n { [flow.state]: flow.states.RegistrationAccepted, [flow.version]: owner.marker },\n );\n owner.postconditions.push(\n liveCondition(mapping, ceremony, input.claim),\n mapping.registration.finalEligibility({\n registration: data.registration,\n subjectId: nativeId,\n }),\n sql`exists(select 1 from ${table.table} where ${both(owner.exact(table.table, values), table.activeCondition)})`,\n );\n\n return { _tag: \"RegistrationAccepted\" } as const;\n });\n\n return { inspectRegistration, issueRegistration, completeRegistration };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"registration-write.mjs","names":[],"sources":["../../../src/internal/passkey/registration-write.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyLabel,\n PasskeyRevision,\n type PasskeyCeremony,\n type PasskeyClaim,\n type PasskeyRegistrationVerified,\n type PasskeyMethodPolicy,\n} from \"@yielded/auth/Passkey\";\nimport { SubjectId } from \"@yielded/auth/Schema\";\nimport { Effect, Schema } from \"effect\";\n\nimport type { QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport type { makePasskeyAdmissionKernel } from \"./admission\";\nimport type { makePasskeyCredentialsKernel } from \"./credentials\";\nimport type { makePasskeyFlowKernel } from \"./flow\";\nimport type { makePasskeyRegistrationCustodyKernel } from \"./registration-custody\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\nimport type { makePasskeyWriteStateKernel } from \"./write-state\";\n\nexport const makePasskeyRegistrationWriteKernel = (\n operations: QueryOperations,\n admission: Pick<\n ReturnType<typeof makePasskeyAdmissionKernel>,\n \"lockAdmission\" | \"readCharges\" | \"guardChargeSet\"\n >,\n credentials: Pick<\n ReturnType<typeof makePasskeyCredentialsKernel>,\n \"readModule\" | \"readPolicyGuards\"\n >,\n flow: Pick<\n ReturnType<typeof makePasskeyFlowKernel>,\n | \"compatiblePolicy\"\n | \"exactClaim\"\n | \"issueAssertion\"\n | \"liveCondition\"\n | \"readFlow\"\n | \"terminalFlow\"\n >,\n registrationCustody: Pick<\n ReturnType<typeof makePasskeyRegistrationCustodyKernel>,\n \"releaseRegistrationCustody\"\n >,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n \"ceremonyStorage\" | \"credentialKey\" | \"equal\" | \"handleKey\" | \"invariant\"\n >,\n writeState: Pick<\n ReturnType<typeof makePasskeyWriteStateKernel>,\n \"digest\" | \"insertCredential\" | \"jsonStorage\" | \"validRegistration\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n const { lockAdmission, readCharges, guardChargeSet } = admission;\n const { readModule, readPolicyGuards } = credentials;\n\n const { compatiblePolicy, exactClaim, issueAssertion, liveCondition, readFlow, terminalFlow } =\n flow;\n\n const { releaseRegistrationCustody } = registrationCustody;\n const { ceremonyStorage, credentialKey, equal, handleKey } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { digest, insertCredential, jsonStorage, validRegistration } = writeState;\n const { both } = transactions;\n\n const registrationData = Effect.fnUntraced(function* (mapping: any, registration: any) {\n const codec = jsonStorage(mapping.registration.schema);\n const encoded = codec.encode(registration);\n\n invariant(new TextEncoder().encode(encoded).length <= 1048576);\n\n return {\n encoded,\n registration: codec.decode(encoded),\n fingerprint: yield* digest(mapping.registration.schema, registration),\n };\n });\n\n const describe = Effect.fnUntraced(function* (mapping: any, registration: any) {\n const data = yield* registrationData(mapping, registration);\n\n const labels = Schema.decodeSync(\n Schema.Struct({ name: PasskeyLabel, displayName: PasskeyLabel }),\n )(mapping.registration.describe(data.registration));\n\n return { ...data, ...labels };\n });\n\n const inspectRegistration = Effect.fn(\"passkey.inspectRegistration\")(function* (\n mapping: any,\n registration: any,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const value = yield* describe(mapping, registration);\n const current = yield* readModule(mapping);\n\n yield* readPolicyGuards(mapping);\n\n const eligible =\n current !== undefined &&\n (yield* owner.check(mapping.registration.eligible(value.registration)));\n\n return {\n fingerprint: value.fingerprint,\n name: value.name,\n displayName: value.displayName,\n eligible,\n };\n });\n\n const issueRegistration = Effect.fn(\"passkey.issueRegistration\")(function* (\n mapping: any,\n input: {\n readonly ceremony: PasskeyCeremony;\n readonly policy: PasskeyMethodPolicy;\n readonly registration: any;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ceremony = input.ceremony;\n\n if (\n ceremony.moduleId !== mapping.moduleId ||\n ceremony.purpose !== \"registration\" ||\n ceremony.context._tag !== \"Registration\" ||\n !ceremony.profile.primarySignIn ||\n ceremony.profile.userVerification !== \"required\" ||\n ceremony.profile.residentKey !== \"required\" ||\n ceremony.allowedCredentials.length !== 0\n )\n return { _tag: \"Rejected\" } as const;\n const value = yield* describe(mapping, input.registration);\n\n if (\n value.fingerprint !== ceremony.context.fingerprint ||\n value.name !== ceremony.context.name ||\n value.displayName !== ceremony.context.displayName\n )\n return { _tag: \"Rejected\" } as const;\n if ((yield* readModule(mapping)) === undefined) return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const eligible = mapping.registration.eligible(value.registration);\n\n if (!(yield* owner.check(eligible))) return { _tag: \"Rejected\" } as const;\n const handle = mapping.handle;\n const hashed = yield* handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const absentHandle = yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n );\n\n const intent = mapping.intent;\n\n const absentCommand = yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.commandId]: ceremony.commandId,\n }),\n { limit: 1 },\n );\n\n const absentFlow = yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n );\n\n if (\n absentHandle.rows.length !== 0 ||\n absentCommand.rows.length !== 0 ||\n absentFlow.rows.length !== 0\n )\n return { _tag: \"Rejected\" } as const;\n const issued = yield* issueAssertion(mapping, ceremony, input.policy);\n\n if (issued._tag !== \"Issued\") return issued;\n\n const reserved = yield* owner.insert(\n handle.table,\n {\n ...handle.encodeInsert({ ceremony, reservationId: owner.marker }),\n [handle.handleKey]: hashed,\n [handle.rpId]: ceremony.profile.rpId,\n [handle.userHandle]: ceremony.context.userHandle,\n [handle.state]: handle.reservedState,\n [handle.version]: owner.marker,\n [handle.reservationId]: owner.marker,\n [mapping.read.handleOwnership.subjectId]: null,\n },\n { [handle.handleKey]: hashed },\n );\n\n absentHandle.rows = reserved.rows;\n\n const inserted = yield* owner.insert(\n intent.table,\n {\n ...intent.encodeInsert({\n ceremony,\n registration: value.registration,\n reservationId: owner.marker,\n }),\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n [intent.commandId]: ceremony.commandId,\n [intent.state]: intent.pendingState,\n [intent.version]: owner.marker,\n [intent.fingerprint]: value.fingerprint,\n [intent.handleKey]: hashed,\n [intent.reservationId]: owner.marker,\n [intent.ceremonySnapshot]: ceremonyStorage.encode(ceremony),\n [intent.applicationSnapshot]: value.encoded,\n },\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n );\n\n absentCommand.rows = inserted.rows;\n absentFlow.rows = inserted.rows;\n owner.postconditions.push(eligible);\n\n return issued;\n });\n\n const completeRegistration = Effect.fn(\"passkey.completeRegistration\")(function* (\n mapping: any,\n input: {\n readonly claim: PasskeyClaim;\n readonly verified: PasskeyRegistrationVerified;\n readonly nowMillis: number;\n },\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const ceremony = input.claim.ceremony;\n\n if (\n ceremony.moduleId !== mapping.moduleId ||\n ceremony.purpose !== \"registration\" ||\n ceremony.context._tag !== \"Registration\"\n )\n return { _tag: \"Rejected\" } as const;\n const current = yield* readModule(mapping);\n\n if (current === undefined) return { _tag: \"Rejected\" } as const;\n yield* lockAdmission(mapping);\n yield* readPolicyGuards(mapping);\n const tuple = mapping.read.credentialOwnership;\n\n const absentCredential = yield* owner.read(\n tuple.table,\n equal(tuple.table, {\n [tuple.credentialKey]: yield* credentialKey(\n ceremony.profile.rpId,\n input.verified.protocolCredentialId,\n ),\n }),\n { limit: 1, observe: false },\n );\n\n const handle = mapping.handle;\n const hashed = yield* handleKey(ceremony.profile.rpId, ceremony.context.userHandle);\n\n const held = (yield* owner.read(\n handle.table,\n equal(handle.table, { [handle.handleKey]: hashed }),\n { limit: 1 },\n )).rows[0];\n\n const intent = mapping.intent;\n\n const row = (yield* owner.read(\n intent.table,\n equal(intent.table, {\n [intent.moduleId]: mapping.moduleId,\n [intent.flowId]: ceremony.flowId,\n }),\n { limit: 1 },\n )).rows[0];\n\n const read = yield* readFlow(mapping, ceremony.flowId);\n\n if (read === undefined || !exactClaim(read, input.claim)) return { _tag: \"Rejected\" } as const;\n\n const reject = Effect.gen(function* () {\n yield* releaseRegistrationCustody(mapping, ceremony);\n yield* terminalFlow(mapping, read, \"Rejected\");\n\n return { _tag: \"Rejected\" } as const;\n });\n\n if (\n absentCredential.rows.length !== 0 ||\n held === undefined ||\n row === undefined ||\n !handle.isReservedState(held[handle.state]) ||\n !intent.isPendingState(row[intent.state]) ||\n row[intent.commandId] !== ceremony.commandId ||\n row[intent.ceremonySnapshot] !== ceremonyStorage.encode(ceremony) ||\n row[intent.fingerprint] !== ceremony.context.fingerprint ||\n row[intent.handleKey] !== hashed ||\n held[handle.reservationId] !== row[intent.reservationId] ||\n held[handle.rpId] !== ceremony.profile.rpId ||\n held[handle.userHandle] !== ceremony.context.userHandle ||\n !compatiblePolicy(ceremony, read.policy, current) ||\n !validRegistration(ceremony, input.verified) ||\n !input.verified.userVerified ||\n !ceremony.profile.primarySignIn ||\n !(yield* owner.check(liveCondition(mapping, ceremony, input.claim)))\n )\n return yield* reject;\n\n const data = yield* describe(\n mapping,\n jsonStorage(mapping.registration.schema).decode(row[intent.applicationSnapshot]),\n );\n\n if (\n data.encoded !== row[intent.applicationSnapshot] ||\n data.fingerprint !== ceremony.context.fingerprint ||\n data.name !== ceremony.context.name ||\n data.displayName !== ceremony.context.displayName\n )\n return yield* reject;\n const eligible = mapping.registration.eligible(data.registration);\n\n if (!(yield* owner.check(eligible))) return yield* reject;\n\n const provision = mapping.registration.subject({\n registration: data.registration,\n ceremony,\n marker: owner.marker,\n });\n\n const nativeId = provision.subjectId;\n const subjectId = SubjectId.make(mapping.read.subjectIds.toSubject(nativeId));\n\n invariant(\n mapping.read.subjectIds.equals(mapping.read.subjectIds.toNative(subjectId), nativeId),\n );\n const table = mapping.read.subject;\n\n const absentSubject = yield* owner.read(\n table.table,\n equal(table.table, { [table.id]: nativeId }),\n { limit: 1 },\n );\n\n if (absentSubject.rows.length !== 0) return yield* reject;\n\n const values = {\n ...provision.values,\n [table.id]: nativeId,\n [table.status]: mapping.registration.activeStatus,\n [table.securityRevision]: owner.marker,\n };\n\n const inserted = yield* owner.insert(table.table, values, { [table.id]: nativeId });\n\n absentSubject.rows = inserted.rows;\n\n const subject = {\n subjectId,\n nativeId,\n row: values,\n revision: PasskeyRevision.make({\n subjectId,\n securityRevision: PasskeyRevision.fields.securityRevision.make(owner.marker),\n credentials: [],\n }),\n };\n\n yield* owner.update(\n handle.table,\n { [handle.handleKey]: hashed },\n {\n [mapping.read.handleOwnership.subjectId]: nativeId,\n [handle.state]: mapping.write.handleOwnership.ownedState,\n [handle.version]: owner.marker,\n [handle.reservationId]: null,\n },\n );\n yield* readCharges(mapping, ceremony, read.policy);\n yield* guardChargeSet(mapping, ceremony);\n yield* insertCredential(\n mapping,\n subject,\n ceremony,\n input.verified,\n yield* owner.now(mapping.clock),\n );\n yield* owner.update(\n intent.table,\n { [intent.moduleId]: mapping.moduleId, [intent.flowId]: ceremony.flowId },\n { [intent.state]: intent.acceptedState, [intent.version]: owner.marker },\n );\n const flow = mapping.flow;\n\n yield* owner.update(\n flow.table,\n { [flow.moduleId]: mapping.moduleId, [flow.flowId]: ceremony.flowId },\n { [flow.state]: flow.states.RegistrationAccepted, [flow.version]: owner.marker },\n );\n owner.postconditions.push(\n liveCondition(mapping, ceremony, input.claim),\n mapping.registration.finalEligibility({\n registration: data.registration,\n subjectId: nativeId,\n }),\n sql`exists(select 1 from ${table.table} where ${both(owner.exact(table.table, values), table.activeCondition)})`,\n );\n\n return { _tag: \"RegistrationAccepted\" } as const;\n });\n\n return { inspectRegistration, issueRegistration, completeRegistration };\n};\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TransactionOwner } from "../transaction-kernel.mjs";
|
|
2
2
|
import "../query-operations.mjs";
|
|
3
|
-
import { Context, Effect } from "effect";
|
|
3
|
+
import { Context, Crypto, Effect } from "effect";
|
|
4
4
|
import { PasskeyCeremony, PasskeyCredential, PasskeyRevision, PasskeyUnavailable } from "@yielded/auth/Passkey";
|
|
5
5
|
//#region src/internal/passkey/state.d.ts
|
|
6
6
|
export type PasskeyOwner = TransactionOwner<PasskeyUnavailable>;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { randomId } from "../crypto.mjs";
|
|
1
2
|
import { requiredPasskeyCredentialConstraints, requiredPasskeyPersistenceConstraints } from "../models/passkey-model.mjs";
|
|
2
3
|
import { requiredPasskeyRegistrationCeremonyConstraints } from "../models/passkey-registration-ceremony-model.mjs";
|
|
3
|
-
import { Context, Effect, Schema } from "effect";
|
|
4
|
+
import { Context, Crypto, Effect, Schema } from "effect";
|
|
4
5
|
import { PasskeyCeremony, PasskeyConfigurationError, PasskeyCredential, PasskeyMethodPolicy, PasskeyModuleId, PasskeyProfile, PasskeyRevision, PasskeyUnavailable, snapshotPasskeySync } from "@yielded/auth/Passkey";
|
|
5
|
-
import { sha256 } from "@noble/hashes/sha2.js";
|
|
6
|
-
import { randomBytes } from "@noble/hashes/utils.js";
|
|
7
|
-
import { Base64Url } from "effect/encoding";
|
|
8
6
|
//#region src/internal/passkey/state.ts
|
|
9
7
|
var CurrentPasskeyTransaction = class extends Context.Service()("effect-auth/drizzle/CurrentPasskeyTransaction") {};
|
|
10
8
|
const makePasskeyStateKernel = (operations, transactions) => {
|
|
@@ -15,11 +13,11 @@ const makePasskeyStateKernel = (operations, transactions) => {
|
|
|
15
13
|
if (!value) throw unavailable();
|
|
16
14
|
};
|
|
17
15
|
const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);
|
|
18
|
-
const nonce =
|
|
16
|
+
const nonce = randomId;
|
|
19
17
|
const encoder = new TextEncoder();
|
|
20
18
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
21
19
|
/** RP tuples and admission scopes have no collation-dependent string identity. */
|
|
22
|
-
const key = (kind, fields)
|
|
20
|
+
const key = Effect.fnUntraced(function* (kind, fields) {
|
|
23
21
|
const values = ["effect-auth/passkey/" + kind + "/v1", ...fields].map((field) => {
|
|
24
22
|
const bytes = encoder.encode(field);
|
|
25
23
|
invariant(decoder.decode(bytes) === field);
|
|
@@ -33,14 +31,15 @@ const makePasskeyStateKernel = (operations, transactions) => {
|
|
|
33
31
|
packed.set(bytes, offset + 4);
|
|
34
32
|
offset += 4 + bytes.length;
|
|
35
33
|
}
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const hashed = yield* (yield* Crypto.Crypto).digest("SHA-256", packed);
|
|
35
|
+
return "v1:" + Array.from(hashed, (value) => value.toString(16).padStart(2, "0")).join("");
|
|
36
|
+
});
|
|
38
37
|
const credentialKey = (rpId, id) => key("credential", [rpId, id]);
|
|
39
38
|
const handleKey = (rpId, handle) => key("handle", [rpId, handle]);
|
|
40
39
|
const subjectScope = (subjectId) => key("subject", [subjectId]);
|
|
41
40
|
const targetScope = (ceremony) => {
|
|
42
41
|
const context = ceremony.context;
|
|
43
|
-
if (!("target" in context)) return null;
|
|
42
|
+
if (!("target" in context)) return Effect.succeed(null);
|
|
44
43
|
const target = context.target;
|
|
45
44
|
return key("target", [
|
|
46
45
|
target.moduleId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.mjs","names":[],"sources":["../../../src/internal/passkey/state.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\n// oxlint-disable-next-line import/extensions -- Noble exposes its explicit .js subpaths.\nimport { sha256 } from \"@noble/hashes/sha2.js\";\n// oxlint-disable-next-line import/extensions -- Noble exposes its explicit .js subpaths.\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport {\n PasskeyConfigurationError,\n PasskeyUnavailable,\n PasskeyCeremony,\n PasskeyCredential,\n PasskeyModuleId,\n PasskeyProfile,\n PasskeyRevision,\n PasskeyMethodPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport { Context, Effect, Schema } from \"effect\";\nimport { Base64Url } from \"effect/encoding\";\n\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"../models/passkey-model\";\nimport { requiredPasskeyRegistrationCeremonyConstraints } from \"../models/passkey-registration-ceremony-model\";\nimport type { SqlExpression as SQL, QueryOperations } from \"../query-operations\";\nimport type { TransactionOwner, makeTransactionKernel } from \"../transaction-kernel\";\n\nexport type PasskeyOwner = TransactionOwner<PasskeyUnavailable>;\n\nexport class CurrentPasskeyTransaction extends Context.Service<\n CurrentPasskeyTransaction,\n PasskeyOwner\n>()(\"effect-auth/drizzle/CurrentPasskeyTransaction\") {}\n\nexport const makePasskeyStateKernel = (\n operations: QueryOperations,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\" | \"makeTransactionRows\">,\n) => {\n const { getTableColumns, sql } = operations;\n const { both, makeTransactionRows } = transactions;\n\n const unavailable = () => PasskeyUnavailable.make({});\n\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);\n\n const nonce = () => Base64Url.encode(randomBytes(32));\n\n const encoder = new TextEncoder();\n\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n\n /** RP tuples and admission scopes have no collation-dependent string identity. */\n const key = (kind: string, fields: ReadonlyArray<string>) => {\n const values = [\"effect-auth/passkey/\" + kind + \"/v1\", ...fields].map((field) => {\n const bytes = encoder.encode(field);\n\n invariant(decoder.decode(bytes) === field);\n\n return bytes;\n });\n\n const packed = new Uint8Array(values.reduce((size, bytes) => size + 4 + bytes.length, 0));\n const view = new DataView(packed.buffer);\n let offset = 0;\n\n for (const bytes of values) {\n view.setUint32(offset, bytes.length, false);\n packed.set(bytes, offset + 4);\n offset += 4 + bytes.length;\n }\n\n return (\n \"v1:\" + Array.from(sha256(packed), (value) => value.toString(16).padStart(2, \"0\")).join(\"\")\n );\n };\n\n const credentialKey = (rpId: string, id: string) => key(\"credential\", [rpId, id]);\n\n const handleKey = (rpId: string, handle: string) => key(\"handle\", [rpId, handle]);\n\n const subjectScope = (subjectId: string) => key(\"subject\", [subjectId]);\n\n const targetScope = (ceremony: PasskeyCeremony) => {\n const context = ceremony.context;\n\n if (!(\"target\" in context)) return null;\n const target = context.target;\n\n return key(\"target\", [\n target.moduleId,\n target.kind,\n target.flowId,\n target.commandId,\n target.bindingDigest,\n target.revision.subjectId,\n ]);\n };\n\n /** Private storage is decoded Type data, never a consumer wire transform. */\n const storage = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n maximumBytes: number,\n ) => {\n const codec = Schema.fromJsonString(Schema.toCodecJson(Schema.toType(schema)));\n\n const encode = (value: S[\"Type\"]) => {\n const text = \"v1:\" + Schema.encodeSync(codec)(snapshotPasskeySync(schema, value));\n\n invariant(encoder.encode(text).length <= maximumBytes);\n\n return text;\n };\n\n return {\n encode,\n decode: (text: unknown): S[\"Type\"] => {\n invariant(typeof text === \"string\" && text.startsWith(\"v1:\"));\n invariant(encoder.encode(text).length <= maximumBytes);\n const value = snapshotPasskeySync(schema, Schema.decodeSync(codec)(text.slice(3)));\n\n invariant(encode(value) === text);\n\n return value;\n },\n };\n };\n\n // Legal 32 x 16 x 2048 origins require more than one MiB before JSON escaping.\n const policyStorage = storage(PasskeyMethodPolicy, 8 * 1024 * 1024);\n\n const ceremonyStorage = storage(PasskeyCeremony, 512 * 1024);\n\n const credentialStorage = storage(PasskeyCredential, 512 * 1024);\n\n const profileStorage = storage(PasskeyProfile, 256 * 1024);\n\n const revisionStorage = storage(PasskeyRevision, 256 * 1024);\n\n const sameCredential = (left: PasskeyCredential, right: PasskeyCredential) => {\n const semantic = (value: PasskeyCredential) => ({\n ...value,\n counter: 0,\n maximumCounter: 0,\n backupState: false,\n revision: { ...value.revision, credentials: [] },\n });\n\n return credentialStorage.encode(semantic(left)) === credentialStorage.encode(semantic(right));\n };\n\n const sameRevision = (left: typeof PasskeyRevision.Type, right: typeof PasskeyRevision.Type) => {\n const pairs = new Map(left.credentials.map((item) => [item.credentialId, item.revision]));\n\n return (\n left.subjectId === right.subjectId &&\n left.securityRevision === right.securityRevision &&\n pairs.size === left.credentials.length &&\n new Set(right.credentials.map((item) => item.credentialId)).size ===\n right.credentials.length &&\n pairs.size === right.credentials.length &&\n right.credentials.every((item) => pairs.get(item.credentialId) === item.revision)\n );\n };\n\n const semanticCredentialColumns = (mapping: any): string[] =>\n [\n \"credentialId\",\n \"subjectId\",\n \"rpId\",\n \"protocolCredentialId\",\n \"credentialKey\",\n \"handleKey\",\n \"userHandle\",\n \"publicKey\",\n \"algorithm\",\n \"profile\",\n \"credentialRevision\",\n \"status\",\n \"primarySignIn\",\n \"enrollmentUserVerified\",\n \"backupEligible\",\n ].map((name) => mapping[name]);\n\n const mappedColumns = (mapping: any): string[] =>\n Object.entries(mapping)\n .filter(\n ([name, value]) =>\n ![\"table\", \"reservedState\", \"pendingState\", \"acceptedState\", \"rejectedState\"].includes(\n name,\n ) && typeof value === \"string\",\n )\n .map(([, value]) => value as string);\n\n const observeSemantic = (mapping: any, where: SQL, row: any) =>\n Effect.map(CurrentPasskeyTransaction, (owner) => {\n owner.observations.push({\n table: mapping.table,\n where,\n rows: [\n Object.fromEntries(\n semanticCredentialColumns(mapping).map((column) => [column, row[column]]),\n ),\n ],\n });\n });\n\n /** Capture descriptor graphs, preserving native SQL expressions and table objects. */\n const captureMapping = <A>(input: A): A => {\n const seen = new Map<object, unknown>();\n\n const copy = (value: unknown): unknown => {\n if (value === null || typeof value !== \"object\" || Effect.isEffect(value)) return value;\n if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return value;\n const prior = seen.get(value);\n\n if (prior !== undefined) return prior;\n const result: any = Array.isArray(value) ? [] : {};\n\n seen.set(value, result);\n for (const [name, child] of Object.entries(value)) result[name] = copy(child);\n\n return Object.freeze(result);\n };\n\n return copy(input) as A;\n };\n\n const constraints = (actual: any, expected: any) => {\n for (const [name, columns] of Object.entries(expected))\n // oxlint-disable-next-line no-restricted-properties -- exact configured constraint descriptor fingerprint.\n invariant(JSON.stringify(actual?.[name]) === JSON.stringify(columns));\n };\n\n const descriptor = (value: any) => {\n const columns = getTableColumns(value.table);\n\n for (const name of mappedColumns(value)) invariant(columns[name] !== undefined);\n };\n\n const validateMapping = (\n mapping: any,\n kind: \"read\" | \"context\" | \"assertion\" | \"registration\",\n ) => {\n try {\n const read = kind === \"read\" ? mapping : kind !== \"registration\" ? mapping.read : undefined;\n\n if (read !== undefined) {\n for (const name of [\n \"subject\",\n \"credential\",\n \"authority\",\n \"credentialOwnership\",\n \"handleOwnership\",\n ])\n descriptor(read[name]);\n constraints(read.constraints, requiredPasskeyCredentialConstraints);\n }\n if (kind === \"read\") return;\n // oxlint-disable-next-line no-restricted-properties -- validate the untyped captured configuration boundary.\n Schema.decodeUnknownSync(PasskeyModuleId)(mapping.moduleId);\n descriptor(mapping.module);\n invariant(mapping.module.policyColumns.length > 0);\n for (const name of mapping.module.policyColumns) col(mapping.module.table, name);\n const scopes = new Set<string>();\n\n for (const guard of mapping.module.guards ?? []) {\n invariant(\n typeof guard.scope === \"string\" && guard.scope.length > 0 && guard.scope.length <= 256,\n );\n invariant(!scopes.has(guard.scope) && guard.columns.length > 0);\n scopes.add(guard.scope);\n for (const name of guard.columns) col(guard.table, name);\n }\n invariant(scopes.size <= 32);\n if (kind === \"context\") return;\n invariant(\n typeof mapping.authorityScope === \"string\" &&\n mapping.authorityScope.length > 0 &&\n mapping.authorityScope.length <= 256,\n );\n for (const name of [\"flow\", \"admission\", \"charge\"]) descriptor(mapping[name]);\n if (kind === \"registration\") {\n descriptor(mapping.intent);\n descriptor(mapping.handle);\n } else col(mapping.read.credential.table, mapping.telemetry.lastUsedAt);\n constraints(\n mapping.constraints,\n kind === \"registration\"\n ? requiredPasskeyRegistrationCeremonyConstraints\n : requiredPasskeyPersistenceConstraints,\n );\n const states = Object.values(mapping.flow.states);\n\n // oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.\n invariant(\n // oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.\n states.length === 7 && new Set(states.map((value) => JSON.stringify(value))).size === 7,\n );\n for (const value of [0, 1, 123456789, 8640000000000])\n invariant(mapping.clock.decodeInstant(mapping.clock.encodeInstant(value)) === value);\n } catch {\n throw PasskeyConfigurationError.make({});\n }\n };\n\n const assertNow = (condition: SQL) =>\n Effect.flatMap(CurrentPasskeyTransaction, (owner) =>\n Effect.gen(function* () {\n invariant(yield* owner.check(condition));\n owner.postconditions.push(condition);\n }),\n );\n\n const existsExact = (table: any, values: any, condition?: SQL) =>\n Effect.map(\n CurrentPasskeyTransaction,\n (owner) =>\n sql`exists(select 1 from ${table} where ${both(owner.exact(table, values), condition)})`,\n );\n\n return {\n unavailable,\n invariant,\n col,\n equal,\n copiedRow,\n matchesNativeRow,\n nonce,\n key,\n credentialKey,\n handleKey,\n subjectScope,\n targetScope,\n policyStorage,\n ceremonyStorage,\n credentialStorage,\n profileStorage,\n revisionStorage,\n sameCredential,\n sameRevision,\n semanticCredentialColumns,\n mappedColumns,\n observeSemantic,\n captureMapping,\n validateMapping,\n assertNow,\n existsExact,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"state.mjs","names":[],"sources":["../../../src/internal/passkey/state.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport {\n PasskeyConfigurationError,\n PasskeyUnavailable,\n PasskeyCeremony,\n PasskeyCredential,\n PasskeyModuleId,\n PasskeyProfile,\n PasskeyRevision,\n PasskeyMethodPolicy,\n snapshotPasskeySync,\n} from \"@yielded/auth/Passkey\";\nimport { Context, Crypto, Effect, Schema } from \"effect\";\n\nimport { randomId } from \"../crypto\";\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"../models/passkey-model\";\nimport { requiredPasskeyRegistrationCeremonyConstraints } from \"../models/passkey-registration-ceremony-model\";\nimport type { SqlExpression as SQL, QueryOperations } from \"../query-operations\";\nimport type { TransactionOwner, makeTransactionKernel } from \"../transaction-kernel\";\n\nexport type PasskeyOwner = TransactionOwner<PasskeyUnavailable>;\n\nexport class CurrentPasskeyTransaction extends Context.Service<\n CurrentPasskeyTransaction,\n PasskeyOwner\n>()(\"effect-auth/drizzle/CurrentPasskeyTransaction\") {}\n\nexport const makePasskeyStateKernel = (\n operations: QueryOperations,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\" | \"makeTransactionRows\">,\n) => {\n const { getTableColumns, sql } = operations;\n const { both, makeTransactionRows } = transactions;\n\n const unavailable = () => PasskeyUnavailable.make({});\n\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);\n\n const nonce = randomId;\n\n const encoder = new TextEncoder();\n\n const decoder = new TextDecoder(\"utf-8\", { fatal: true });\n\n /** RP tuples and admission scopes have no collation-dependent string identity. */\n const key = Effect.fnUntraced(function* (kind: string, fields: ReadonlyArray<string>) {\n const values = [\"effect-auth/passkey/\" + kind + \"/v1\", ...fields].map((field) => {\n const bytes = encoder.encode(field);\n\n invariant(decoder.decode(bytes) === field);\n\n return bytes;\n });\n\n const packed = new Uint8Array(values.reduce((size, bytes) => size + 4 + bytes.length, 0));\n const view = new DataView(packed.buffer);\n let offset = 0;\n\n for (const bytes of values) {\n view.setUint32(offset, bytes.length, false);\n packed.set(bytes, offset + 4);\n offset += 4 + bytes.length;\n }\n\n const crypto = yield* Crypto.Crypto;\n const hashed = yield* crypto.digest(\"SHA-256\", packed);\n\n return \"v1:\" + Array.from(hashed, (value) => value.toString(16).padStart(2, \"0\")).join(\"\");\n });\n\n const credentialKey = (rpId: string, id: string) => key(\"credential\", [rpId, id]);\n\n const handleKey = (rpId: string, handle: string) => key(\"handle\", [rpId, handle]);\n\n const subjectScope = (subjectId: string) => key(\"subject\", [subjectId]);\n\n const targetScope = (ceremony: PasskeyCeremony) => {\n const context = ceremony.context;\n\n if (!(\"target\" in context)) return Effect.succeed(null);\n const target = context.target;\n\n return key(\"target\", [\n target.moduleId,\n target.kind,\n target.flowId,\n target.commandId,\n target.bindingDigest,\n target.revision.subjectId,\n ]);\n };\n\n /** Private storage is decoded Type data, never a consumer wire transform. */\n const storage = <S extends Schema.Codec<unknown, unknown, never, never>>(\n schema: S,\n maximumBytes: number,\n ) => {\n const codec = Schema.fromJsonString(Schema.toCodecJson(Schema.toType(schema)));\n\n const encode = (value: S[\"Type\"]) => {\n const text = \"v1:\" + Schema.encodeSync(codec)(snapshotPasskeySync(schema, value));\n\n invariant(encoder.encode(text).length <= maximumBytes);\n\n return text;\n };\n\n return {\n encode,\n decode: (text: unknown): S[\"Type\"] => {\n invariant(typeof text === \"string\" && text.startsWith(\"v1:\"));\n invariant(encoder.encode(text).length <= maximumBytes);\n const value = snapshotPasskeySync(schema, Schema.decodeSync(codec)(text.slice(3)));\n\n invariant(encode(value) === text);\n\n return value;\n },\n };\n };\n\n // Legal 32 x 16 x 2048 origins require more than one MiB before JSON escaping.\n const policyStorage = storage(PasskeyMethodPolicy, 8 * 1024 * 1024);\n\n const ceremonyStorage = storage(PasskeyCeremony, 512 * 1024);\n\n const credentialStorage = storage(PasskeyCredential, 512 * 1024);\n\n const profileStorage = storage(PasskeyProfile, 256 * 1024);\n\n const revisionStorage = storage(PasskeyRevision, 256 * 1024);\n\n const sameCredential = (left: PasskeyCredential, right: PasskeyCredential) => {\n const semantic = (value: PasskeyCredential) => ({\n ...value,\n counter: 0,\n maximumCounter: 0,\n backupState: false,\n revision: { ...value.revision, credentials: [] },\n });\n\n return credentialStorage.encode(semantic(left)) === credentialStorage.encode(semantic(right));\n };\n\n const sameRevision = (left: typeof PasskeyRevision.Type, right: typeof PasskeyRevision.Type) => {\n const pairs = new Map(left.credentials.map((item) => [item.credentialId, item.revision]));\n\n return (\n left.subjectId === right.subjectId &&\n left.securityRevision === right.securityRevision &&\n pairs.size === left.credentials.length &&\n new Set(right.credentials.map((item) => item.credentialId)).size ===\n right.credentials.length &&\n pairs.size === right.credentials.length &&\n right.credentials.every((item) => pairs.get(item.credentialId) === item.revision)\n );\n };\n\n const semanticCredentialColumns = (mapping: any): string[] =>\n [\n \"credentialId\",\n \"subjectId\",\n \"rpId\",\n \"protocolCredentialId\",\n \"credentialKey\",\n \"handleKey\",\n \"userHandle\",\n \"publicKey\",\n \"algorithm\",\n \"profile\",\n \"credentialRevision\",\n \"status\",\n \"primarySignIn\",\n \"enrollmentUserVerified\",\n \"backupEligible\",\n ].map((name) => mapping[name]);\n\n const mappedColumns = (mapping: any): string[] =>\n Object.entries(mapping)\n .filter(\n ([name, value]) =>\n ![\"table\", \"reservedState\", \"pendingState\", \"acceptedState\", \"rejectedState\"].includes(\n name,\n ) && typeof value === \"string\",\n )\n .map(([, value]) => value as string);\n\n const observeSemantic = (mapping: any, where: SQL, row: any) =>\n Effect.map(CurrentPasskeyTransaction, (owner) => {\n owner.observations.push({\n table: mapping.table,\n where,\n rows: [\n Object.fromEntries(\n semanticCredentialColumns(mapping).map((column) => [column, row[column]]),\n ),\n ],\n });\n });\n\n /** Capture descriptor graphs, preserving native SQL expressions and table objects. */\n const captureMapping = <A>(input: A): A => {\n const seen = new Map<object, unknown>();\n\n const copy = (value: unknown): unknown => {\n if (value === null || typeof value !== \"object\" || Effect.isEffect(value)) return value;\n if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return value;\n const prior = seen.get(value);\n\n if (prior !== undefined) return prior;\n const result: any = Array.isArray(value) ? [] : {};\n\n seen.set(value, result);\n for (const [name, child] of Object.entries(value)) result[name] = copy(child);\n\n return Object.freeze(result);\n };\n\n return copy(input) as A;\n };\n\n const constraints = (actual: any, expected: any) => {\n for (const [name, columns] of Object.entries(expected))\n // oxlint-disable-next-line no-restricted-properties -- exact configured constraint descriptor fingerprint.\n invariant(JSON.stringify(actual?.[name]) === JSON.stringify(columns));\n };\n\n const descriptor = (value: any) => {\n const columns = getTableColumns(value.table);\n\n for (const name of mappedColumns(value)) invariant(columns[name] !== undefined);\n };\n\n const validateMapping = (\n mapping: any,\n kind: \"read\" | \"context\" | \"assertion\" | \"registration\",\n ) => {\n try {\n const read = kind === \"read\" ? mapping : kind !== \"registration\" ? mapping.read : undefined;\n\n if (read !== undefined) {\n for (const name of [\n \"subject\",\n \"credential\",\n \"authority\",\n \"credentialOwnership\",\n \"handleOwnership\",\n ])\n descriptor(read[name]);\n constraints(read.constraints, requiredPasskeyCredentialConstraints);\n }\n if (kind === \"read\") return;\n // oxlint-disable-next-line no-restricted-properties -- validate the untyped captured configuration boundary.\n Schema.decodeUnknownSync(PasskeyModuleId)(mapping.moduleId);\n descriptor(mapping.module);\n invariant(mapping.module.policyColumns.length > 0);\n for (const name of mapping.module.policyColumns) col(mapping.module.table, name);\n const scopes = new Set<string>();\n\n for (const guard of mapping.module.guards ?? []) {\n invariant(\n typeof guard.scope === \"string\" && guard.scope.length > 0 && guard.scope.length <= 256,\n );\n invariant(!scopes.has(guard.scope) && guard.columns.length > 0);\n scopes.add(guard.scope);\n for (const name of guard.columns) col(guard.table, name);\n }\n invariant(scopes.size <= 32);\n if (kind === \"context\") return;\n invariant(\n typeof mapping.authorityScope === \"string\" &&\n mapping.authorityScope.length > 0 &&\n mapping.authorityScope.length <= 256,\n );\n for (const name of [\"flow\", \"admission\", \"charge\"]) descriptor(mapping[name]);\n if (kind === \"registration\") {\n descriptor(mapping.intent);\n descriptor(mapping.handle);\n } else col(mapping.read.credential.table, mapping.telemetry.lastUsedAt);\n constraints(\n mapping.constraints,\n kind === \"registration\"\n ? requiredPasskeyRegistrationCeremonyConstraints\n : requiredPasskeyPersistenceConstraints,\n );\n const states = Object.values(mapping.flow.states);\n\n // oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.\n invariant(\n // oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.\n states.length === 7 && new Set(states.map((value) => JSON.stringify(value))).size === 7,\n );\n for (const value of [0, 1, 123456789, 8640000000000])\n invariant(mapping.clock.decodeInstant(mapping.clock.encodeInstant(value)) === value);\n } catch {\n throw PasskeyConfigurationError.make({});\n }\n };\n\n const assertNow = (condition: SQL) =>\n Effect.flatMap(CurrentPasskeyTransaction, (owner) =>\n Effect.gen(function* () {\n invariant(yield* owner.check(condition));\n owner.postconditions.push(condition);\n }),\n );\n\n const existsExact = (table: any, values: any, condition?: SQL) =>\n Effect.map(\n CurrentPasskeyTransaction,\n (owner) =>\n sql`exists(select 1 from ${table} where ${both(owner.exact(table, values), condition)})`,\n );\n\n return {\n unavailable,\n invariant,\n col,\n equal,\n copiedRow,\n matchesNativeRow,\n nonce,\n key,\n credentialKey,\n handleKey,\n subjectScope,\n targetScope,\n policyStorage,\n ceremonyStorage,\n credentialStorage,\n profileStorage,\n revisionStorage,\n sameCredential,\n sameRevision,\n semanticCredentialColumns,\n mappedColumns,\n observeSemantic,\n captureMapping,\n validateMapping,\n assertNow,\n existsExact,\n };\n};\n"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import "./credentials.mjs";
|
|
|
8
8
|
import "./flow.mjs";
|
|
9
9
|
import { TransactionCoordinatorError, TransactionExecution, TransactionTargetConfiguration } from "../transaction-execution-kernel.mjs";
|
|
10
10
|
import "./registration-ceremony.mjs";
|
|
11
|
-
import { Effect, Schema } from "effect";
|
|
11
|
+
import { Crypto, Effect, Schema } from "effect";
|
|
12
12
|
import { PasskeyConfigurationError, PasskeyPersistence, PasskeyUnavailable, PreparePasskeyCommit } from "@yielded/auth/Passkey";
|
|
13
13
|
import { LifecycleHooks, PreparedCommit } from "@yielded/auth/Hooks";
|
|
14
14
|
import { Statement } from "effect/sql/Statement";
|