@yielded/auth-persistence 0.1.0-beta.31 → 0.1.0-beta.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +9 -2
  2. package/dist/Adapter.mjs +8 -8
  3. package/dist/OAuthPersistence.mjs +10 -5
  4. package/dist/OAuthPersistence.mjs.map +1 -1
  5. package/dist/internal/configuration.d.mts +31 -5
  6. package/dist/internal/configuration.mjs.map +1 -1
  7. package/dist/internal/email-native.mjs +10 -2
  8. package/dist/internal/email-native.mjs.map +1 -1
  9. package/dist/internal/email-policy.mjs +3 -2
  10. package/dist/internal/email-policy.mjs.map +1 -1
  11. package/dist/internal/models/oauth-model.d.mts +4 -2
  12. package/dist/internal/models/oauth-model.mjs.map +1 -1
  13. package/dist/internal/oauth/native-accounts.d.mts +1 -1
  14. package/dist/internal/oauth/native-accounts.mjs +6 -6
  15. package/dist/internal/oauth/native-accounts.mjs.map +1 -1
  16. package/dist/internal/oauth/native-connected.d.mts +1 -1
  17. package/dist/internal/oauth/native-connected.mjs +1 -1
  18. package/dist/internal/oauth/native-registration.mjs +19 -8
  19. package/dist/internal/oauth/native-registration.mjs.map +1 -1
  20. package/dist/internal/oauth/native-sign-in.d.mts +1 -1
  21. package/dist/internal/oauth/native-sign-in.mjs +1 -1
  22. package/dist/internal/oauth/native-state.mjs +21 -11
  23. package/dist/internal/oauth/native-state.mjs.map +1 -1
  24. package/dist/internal/oauth/state.d.mts +1 -1
  25. package/dist/internal/oauth/state.mjs +1 -1
  26. package/dist/internal/oauth-managed.mjs +182 -0
  27. package/dist/internal/oauth-managed.mjs.map +1 -0
  28. package/dist/internal/passkey-managed.mjs +1 -1
  29. package/dist/internal/passkey-native-registration.mjs +1 -1
  30. package/dist/internal/passkey-native-state.mjs +33 -23
  31. package/dist/internal/passkey-native-state.mjs.map +1 -1
  32. package/dist/internal/passkey-native-write.mjs +3 -3
  33. package/dist/internal/passkey-native-write.mjs.map +1 -1
  34. package/dist/internal/passkey-native.d.mts +15 -7
  35. package/dist/internal/passkey-native.mjs +2 -1
  36. package/dist/internal/passkey-native.mjs.map +1 -1
  37. package/dist/internal/passkey-policy.mjs +9 -7
  38. package/dist/internal/passkey-policy.mjs.map +1 -1
  39. package/dist/internal/password-native.mjs +9 -6
  40. package/dist/internal/password-native.mjs.map +1 -1
  41. package/dist/internal/password-policy.mjs +7 -6
  42. package/dist/internal/password-policy.mjs.map +1 -1
  43. package/dist/internal/persistence.mjs +96 -37
  44. package/dist/internal/persistence.mjs.map +1 -1
  45. package/dist/internal/phone-native.mjs +1 -1
  46. package/dist/internal/phone-native.mjs.map +1 -1
  47. package/dist/internal/session-native-authority.mjs +6 -5
  48. package/dist/internal/session-native-authority.mjs.map +1 -1
  49. package/dist/internal/session-native-login.d.mts +2 -2
  50. package/dist/internal/session-native-login.mjs +2 -1
  51. package/dist/internal/session-native-login.mjs.map +1 -1
  52. package/dist/internal/session-native-pending-read.mjs +26 -20
  53. package/dist/internal/session-native-pending-read.mjs.map +1 -1
  54. package/dist/internal/session-native-record.mjs +6 -4
  55. package/dist/internal/session-native-record.mjs.map +1 -1
  56. package/dist/internal/session-native-state.mjs +3 -0
  57. package/dist/internal/session-native-state.mjs.map +1 -1
  58. package/dist/internal/session-native-stateful.d.mts +2 -2
  59. package/dist/internal/session-native-stateful.mjs +65 -37
  60. package/dist/internal/session-native-stateful.mjs.map +1 -1
  61. package/dist/internal/session-native-step-up.d.mts +2 -2
  62. package/dist/internal/session-native-step-up.mjs +14 -18
  63. package/dist/internal/session-native-step-up.mjs.map +1 -1
  64. package/dist/internal/session-native-validity.mjs +3 -1
  65. package/dist/internal/session-native-validity.mjs.map +1 -1
  66. package/dist/internal/sql-authority.mjs +5 -1
  67. package/dist/internal/sql-authority.mjs.map +1 -1
  68. package/dist/internal/sql-storage-validation.mjs +2 -1
  69. package/dist/internal/sql-storage-validation.mjs.map +1 -1
  70. package/dist/internal/sql-table.d.mts +1 -0
  71. package/dist/internal/sql-table.mjs.map +1 -1
  72. package/dist/internal/step-up-state.mjs +19 -6
  73. package/dist/internal/step-up-state.mjs.map +1 -1
  74. package/dist/internal/storage-mapping.mjs +1 -1
  75. package/dist/internal/storage-tables.d.mts +237 -0
  76. package/dist/internal/storage-tables.mjs +68 -0
  77. package/dist/internal/storage-tables.mjs.map +1 -1
  78. package/dist/internal/storage-validation.d.mts +3 -1
  79. package/dist/internal/storage-validation.mjs +70 -17
  80. package/dist/internal/storage-validation.mjs.map +1 -1
  81. package/dist/internal/totp-native.mjs +1 -1
  82. package/package.json +1 -1
  83. package/src/OAuthPersistence.ts +7 -2
  84. package/src/internal/configuration.ts +46 -7
  85. package/src/internal/email-native.ts +16 -6
  86. package/src/internal/email-policy.ts +12 -3
  87. package/src/internal/models/oauth-model.ts +7 -1
  88. package/src/internal/oauth/native-accounts.ts +3 -3
  89. package/src/internal/oauth/native-registration.ts +17 -6
  90. package/src/internal/oauth/native-state.ts +38 -18
  91. package/src/internal/oauth-managed.ts +277 -0
  92. package/src/internal/passkey-native-state.ts +59 -52
  93. package/src/internal/passkey-native-write.ts +2 -2
  94. package/src/internal/passkey-native.ts +4 -2
  95. package/src/internal/passkey-policy.ts +13 -9
  96. package/src/internal/password-native.ts +17 -9
  97. package/src/internal/password-policy.ts +16 -8
  98. package/src/internal/persistence.ts +216 -70
  99. package/src/internal/phone-native.ts +1 -1
  100. package/src/internal/session-native-authority.ts +23 -7
  101. package/src/internal/session-native-login.ts +6 -3
  102. package/src/internal/session-native-pending-read.ts +46 -33
  103. package/src/internal/session-native-record.ts +6 -4
  104. package/src/internal/session-native-state.ts +3 -0
  105. package/src/internal/session-native-stateful.ts +144 -51
  106. package/src/internal/session-native-step-up.ts +27 -23
  107. package/src/internal/session-native-validity.ts +3 -1
  108. package/src/internal/sql-authority.ts +5 -1
  109. package/src/internal/sql-storage-validation.ts +53 -46
  110. package/src/internal/sql-table.ts +1 -0
  111. package/src/internal/step-up-state.ts +43 -6
  112. package/src/internal/storage-tables.ts +76 -0
  113. package/src/internal/storage-validation.ts +127 -27
@@ -1 +1 @@
1
- {"version":3,"file":"phone-native.mjs","names":[],"sources":["../../src/internal/phone-native.ts"],"sourcesContent":["import {\n CurrentCommitJournal,\n type LifecycleHooks,\n type PreparedCommit,\n type CommitJournal,\n} from \"@yielded/auth/Hooks\";\nimport * as P from \"@yielded/auth/PhoneOtp\";\nimport { ProofRedemptionInput } from \"@yielded/auth/Proofs\";\nimport {\n AuthenticationRevision,\n AuthenticationRequirement,\n SecurityRevision,\n snapshotAuthenticationEvidence,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Option, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { randomId } from \"./crypto\";\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyPhoneMapping } from \"./models/phone-model\";\nimport type { NativeSqlTables, SqlTable } from \"./native-sql-table\";\nimport { passwordEvidenceSatisfiedAt } from \"./password-policy\";\nimport { makeNativeProofStore } from \"./proof-native\";\nimport { exactSqlText, executeSqlChange } from \"./sql-change\";\nimport {\n appendSqlBatchStatement,\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n registerSqlBatchPostcondition,\n registerSqlCommitReceipt,\n registerSqlPostcondition,\n SqlBatchCommit,\n} from \"./sql-commit\";\n\nconst unavailable = () => P.PhoneOtpUnavailable.make({});\n\nconst ensure: (condition: unknown) => asserts condition = (condition) => {\n if (!condition) throw unavailable();\n};\n\nconst resolve = <A>(\n value: A | Effect.Effect<A, PersistenceMappingError>,\n): Effect.Effect<A, PersistenceMappingError> =>\n Effect.isEffect(value) ? value : Effect.succeed(value);\n\nconst targetCodec = Schema.fromJsonString(P.PhoneLifecycleTarget);\n\nconst sameRevision = (a: AuthenticationRevision, b: AuthenticationRevision) =>\n a.subjectId === b.subjectId &&\n a.securityRevision === b.securityRevision &&\n a.credentials.length === b.credentials.length &&\n a.credentials.every((f) =>\n b.credentials.some((g) => f.credentialId === g.credentialId && f.revision === g.revision),\n );\n\n/** Canonical identifier rows own phone custody, including permanent retired numbers. */\nexport const makeNativePhoneServices = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: AnyPhoneMapping,\n): Effect.fn.Return<\n {\n readonly phonePersistence: P.PhonePersistence[\"Service\"];\n readonly phoneSignInTargets: P.PhoneSignInTargets[\"Service\"];\n },\n P.PhoneOtpUnavailable,\n SqlClient | LifecycleHooks | Crypto.Crypto | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n const sql = (yield* SqlClient).withoutTransforms();\n const executor = yield* makeSqlCommitExecutor(unavailable);\n const crypto = yield* Crypto.Crypto;\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const s = mapping.subject,\n i = mapping.identifier,\n c = mapping.credential;\n\n const subject = tables(s.table),\n identifier = tables(i.table),\n credential = tables(c.table);\n\n const now = tables.expression(mapping.engineNowMillis);\n\n const activeSubject = tables.expression(s.activeCondition),\n activeIdentifier = tables.expression(i.activeCondition),\n activeCredential = tables.expression(c.activeCondition);\n\n const lock = sql.onDialectOrElse({ sqlite: () => sql``, orElse: () => sql`for update` });\n\n const exact = (t: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, t.column(key), t.value(key, value));\n\n const id = (t: SqlTable, key: string, value: unknown) =>\n sql`${t.column(key)} = ${t.value(key, value)}`;\n\n const numberKey = (number: string) =>\n sql.and([exact(identifier, i.namespace, \"phone\"), exact(identifier, i.value, number)]);\n\n const run = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n batch === undefined\n ? executor.run(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n : executor\n .batch(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const stage = Effect.fnUntraced(function* (statement: Fragment) {\n if (batch === undefined) ensure((yield* executeSqlChange(sql, statement)) === 1);\n else {\n yield* appendSqlBatchStatement(sql`${statement}`);\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = 1`));\n }\n });\n\n const prepare = <A>(\n decision: P.PhoneMutationDecision,\n project: (decision: P.PhoneMutationDecision, journal: CommitJournal) => PreparedCommit<A>,\n ) =>\n Effect.gen(function* () {\n const result = project(decision, yield* CurrentCommitJournal);\n\n yield* registerSqlCommitReceipt(result);\n\n return result;\n });\n\n const capture = Effect.fnUntraced(function* (\n input: Parameters<P.PhonePersistence[\"Service\"][\"target\"]>[0],\n ) {\n ensure(input.moduleId === mapping.moduleId);\n\n const numbers =\n input.sourcePhoneNumber === undefined || input.sourcePhoneNumber === input.phoneNumber\n ? [input.phoneNumber]\n : [input.phoneNumber, input.sourcePhoneNumber];\n\n const rows =\n yield* sql`select ${identifier.fields(\"phone_identifier_\")}, case when ${activeIdentifier} then 1 else 0 end as identifier_active from ${identifier.name} where ${sql.or(numbers.map(numberKey))} limit 3`;\n\n ensure(rows.length <= numbers.length);\n\n const identifiers = rows.map((row) => ({\n row: identifier.decode(row, \"phone_identifier_\"),\n active: Number(row.identifier_active) === 1,\n }));\n\n ensure(\n identifiers.every(\n ({ row }) => row[i.namespace] === \"phone\" && numbers.includes(String(row[i.value])),\n ),\n );\n const destination = identifiers.find(({ row }) => row[i.value] === input.phoneNumber);\n const source = identifiers.find(({ row }) => row[i.value] === input.sourcePhoneNumber);\n\n const subjectId =\n input.subjectId ??\n (destination === undefined\n ? undefined\n : yield* resolve(mapping.subjectIds.toSubject(destination.row[i.subjectId])));\n\n let subjectRow: Record<string, unknown> | undefined;\n let revision: AuthenticationRevision | null = null;\n const factors: { readonly row: Record<string, unknown>; readonly active: boolean }[] = [];\n let instant: number;\n\n if (subjectId === undefined) {\n const clock = yield* sql`select ${now} as engine_now`;\n\n instant = Number(clock[0]?.engine_now);\n } else {\n const native = yield* resolve(mapping.subjectIds.toNative(subjectId));\n\n ensure((yield* resolve(mapping.subjectIds.toSubject(native))) === subjectId);\n\n const joined =\n yield* sql`select ${subject.fields(\"phone_subject_\")}, ${credential.fields(\"phone_factor_\")}, case when ${activeSubject} then 1 else 0 end as subject_active, case when ${activeCredential} then 1 else 0 end as credential_active, ${now} as engine_now from ${subject.name} left join ${credential.name} on ${id(credential, c.subjectId, native)} and (${activeCredential} ${identifiers.length === 0 ? sql`` : sql`or ${sql.or(identifiers.map((value) => exact(credential, c.id, value.row[i.credentialId])))}`}) where ${id(subject, s.id, native)} limit 67`;\n\n ensure(joined.length <= 66);\n const first = joined[0];\n\n instant = Number(first?.engine_now);\n if (first !== undefined) {\n subjectRow = subject.decode(first, \"phone_subject_\");\n ensure((yield* resolve(mapping.subjectIds.toSubject(subjectRow[s.id]))) === subjectId);\n for (const joinedRow of joined) {\n const row = credential.decode(joinedRow, \"phone_factor_\");\n\n if (row[c.id] === null || row[c.id] === undefined) continue;\n ensure((yield* resolve(mapping.subjectIds.toSubject(row[c.subjectId]))) === subjectId);\n ensure(!factors.some((f) => f.row[c.id] === row[c.id]));\n factors.push({ row, active: Number(joinedRow.credential_active) === 1 });\n }\n ensure(factors.filter((f) => f.active).length <= 64);\n if (Number(first.subject_active) === 1)\n revision = yield* Schema.decodeUnknownEffect(AuthenticationRevision)({\n subjectId,\n securityRevision: subjectRow[s.securityRevision],\n credentials: factors\n .filter((f) => f.active)\n .map((f) => ({ credentialId: f.row[c.id], revision: f.row[c.revision] }))\n .sort((a, b) => String(a.credentialId).localeCompare(String(b.credentialId))),\n });\n }\n }\n ensure(Number.isSafeInteger(instant));\n\n const custody = Effect.fnUntraced(function* (value: typeof destination) {\n if (value === undefined || value.row[i.moduleId] !== mapping.moduleId) return null;\n const factor = factors.find((f) => f.row[c.id] === value.row[i.credentialId]);\n\n if (factor === undefined) return null;\n const owner = yield* resolve(mapping.subjectIds.toSubject(value.row[i.subjectId]));\n\n if (owner !== subjectId) return null;\n\n return yield* Schema.decodeUnknownEffect(P.PhoneCustody)({\n phoneNumber: value.row[i.value],\n subjectId: owner,\n credentialId: value.row[i.credentialId],\n custodyRevision: value.row[i.revision],\n credentialRevision: factor.row[c.revision],\n verifiedAtMillis:\n value.row[i.verifiedAt] === null || value.row[i.verifiedAt] === undefined\n ? null\n : mapping.proofs.clock.decodeInstant(value.row[i.verifiedAt]),\n state:\n !value.active || !factor.active\n ? \"retired\"\n : value.row[i.verifiedAt] === null || value.row[i.verifiedAt] === undefined\n ? \"unverified\"\n : \"verified\",\n });\n });\n\n const targetCustody = yield* custody(destination);\n const sourceCustody = yield* custody(source);\n\n const eligible =\n input.action === \"register\"\n ? destination === undefined\n : revision !== null &&\n input.subjectId === subjectId &&\n (destination === undefined ||\n (targetCustody !== null &&\n targetCustody.subjectId === subjectId &&\n targetCustody.state === \"unverified\")) &&\n (input.action !== \"change\" ||\n (input.sourcePhoneNumber !== input.phoneNumber &&\n sourceCustody !== null &&\n sourceCustody.subjectId === subjectId &&\n sourceCustody.state === \"verified\"));\n\n const target = yield* Schema.decodeEffect(P.PhoneLifecycleTarget)({\n phoneNumber: input.phoneNumber,\n custody: targetCustody,\n source: sourceCustody,\n revision,\n eligible,\n });\n\n return { target, subjectRow, destination, source, factors, now: instant };\n });\n\n const phoneSignInTargets: P.PhoneSignInTargets[\"Service\"] = {\n lookup: (input) =>\n executor.read(\n Effect.gen(function* () {\n const current = yield* capture({ ...input, action: \"verify\" });\n const { custody, revision } = current.target;\n\n if (\n custody === null ||\n custody.state !== \"verified\" ||\n custody.verifiedAtMillis === null ||\n revision === null ||\n current.subjectRow === undefined ||\n !revision.credentials.some(\n (f) =>\n f.credentialId === custody.credentialId &&\n f.revision === custody.credentialRevision,\n )\n )\n return Option.none();\n\n return Option.some(\n yield* Schema.decodeEffect(P.PhoneCredentialSnapshot)({\n moduleId: input.moduleId,\n phoneNumber: input.phoneNumber,\n custodyRevision: custody.custodyRevision,\n verifiedAtMillis: custody.verifiedAtMillis,\n credentialId: custody.credentialId,\n credentialRevision: custody.credentialRevision,\n revision,\n requirement: yield* resolve(mapping.subject.decodeRequirement(current.subjectRow)),\n }),\n );\n }),\n ),\n };\n\n const phonePersistence: P.PhonePersistence[\"Service\"] = {\n target: (input) => executor.read(Effect.map(capture({ ...input }), (value) => value.target)),\n mutate: (original, project) =>\n run(\n Effect.gen(function* () {\n const target = yield* Schema.decodeEffect(targetCodec)(\n yield* Schema.encodeEffect(targetCodec)(original.target),\n );\n\n const redemption = yield* Schema.decodeEffect(Schema.toCodecIso(ProofRedemptionInput))(\n yield* Schema.encodeEffect(Schema.toCodecIso(ProofRedemptionInput))(\n original.redemption.input,\n ),\n );\n\n const input = {\n ...original,\n target,\n redemption: { input: redemption, prepare: original.redemption.prepare },\n };\n\n const rejected = () => prepare({ _tag: \"Rejected\" }, project);\n\n ensure(input.moduleId === mapping.moduleId);\n if (\n input.action === \"register\" &&\n (mapping.subjectIds.allocate === undefined || s.encodeInsert === undefined)\n )\n return yield* rejected();\n const policy = yield* Schema.decodeEffect(P.PhoneLifecyclePolicy)(input.policy);\n\n if (\n policy.maximumEvidenceAgeMillis !== mapping.policy.maximumEvidenceAgeMillis ||\n policy.requireImmediateInvalidation !== mapping.policy.requireImmediateInvalidation\n )\n return yield* rejected();\n if (target.revision !== null) {\n const native = yield* resolve(mapping.subjectIds.toNative(target.revision.subjectId));\n\n const rows =\n yield* sql`select ${subject.fields(\"phone_locked_\")} from ${subject.name} where ${id(subject, s.id, native)} limit 2 ${batch === undefined ? lock : sql``}`;\n\n if (\n rows.length !== 1 ||\n (yield* resolve(\n mapping.subjectIds.toSubject(subject.decode(rows[0]!, \"phone_locked_\")[s.id]),\n )) !== target.revision.subjectId\n )\n return yield* rejected();\n }\n\n const captured = yield* capture({\n moduleId: input.moduleId,\n action: input.action,\n phoneNumber: target.phoneNumber,\n ...(target.revision === null ? {} : { subjectId: target.revision.subjectId }),\n ...(target.source === null ? {} : { sourcePhoneNumber: target.source.phoneNumber }),\n });\n\n if (\n !captured.target.eligible ||\n (yield* Schema.encodeEffect(targetCodec)(captured.target)) !==\n (yield* Schema.encodeEffect(targetCodec)(target))\n )\n return yield* rejected();\n const binding = redemption.binding;\n\n if (\n redemption.moduleId !== `${mapping.moduleId}/lifecycle` ||\n redemption.purpose !== \"phone-lifecycle\" ||\n binding.identifier.namespace !== \"phone\" ||\n binding.identifier.value !== target.phoneNumber\n )\n return yield* rejected();\n if (\n input.action === \"register\"\n ? binding._tag !== \"Identifier\"\n : binding._tag === \"Identifier\" ||\n target.revision === null ||\n !sameRevision(binding.revision, target.revision)\n )\n return yield* rejected();\n if (\n (input.action === \"verify\" && binding._tag !== \"Subject\") ||\n (input.action === \"change\" && binding._tag !== \"IdentifierChange\")\n )\n return yield* rejected();\n const freshness: Fragment[] = [];\n\n if (input.action !== \"register\") {\n const authorization = input.authorization;\n\n if (\n authorization === undefined ||\n target.revision === null ||\n captured.subjectRow === undefined\n )\n return yield* rejected();\n const evidence = yield* snapshotAuthenticationEvidence(authorization.evidence);\n const challenge = authorization.challenge;\n\n if (\n challenge.moduleId !== mapping.moduleId ||\n challenge.action !== input.action ||\n challenge.commandId !== input.commandId ||\n challenge.phoneNumber !== target.phoneNumber ||\n challenge.sourcePhoneNumber !== target.source?.phoneNumber ||\n challenge.flowId !== binding.flowId ||\n challenge.bindingDigest !== binding.contextDigest ||\n challenge.flowId !== evidence.flowId ||\n challenge.bindingDigest !== evidence.bindingDigest ||\n !sameRevision(challenge.revision, target.revision) ||\n !sameRevision(evidence.revision, target.revision)\n )\n return yield* rejected();\n\n const requirements = [\n authorization.requirement,\n authorization.actionRequirement,\n yield* resolve(s.decodeRequirement(captured.subjectRow)),\n ...(s.decodeActionRequirement === undefined\n ? []\n : [yield* resolve(s.decodeActionRequirement(captured.subjectRow, input.action))]),\n ];\n\n for (const originalRequirement of requirements) {\n const requirement =\n yield* Schema.decodeEffect(AuthenticationRequirement)(originalRequirement);\n\n if (!passwordEvidenceSatisfiedAt(evidence, requirement, captured.now))\n return yield* rejected();\n }\n if (evidence.proofs.length === 0) return yield* rejected();\n for (const proof of evidence.proofs) {\n const at = DateTime.toEpochMillis(proof.verifiedAt);\n\n const age = Math.min(\n policy.maximumEvidenceAgeMillis,\n ...requirements.map((r) => r.maximumAgeMillis),\n );\n\n if (\n !target.revision.credentials.some((f) => f.credentialId === proof.credentialId) ||\n at > captured.now ||\n captured.now >= at + age\n )\n return yield* rejected();\n freshness.push(sql`${now} >= ${at} and ${now} < ${at + age}`);\n }\n }\n const store = yield* makeNativeProofStore(tables, mapping.proofs, batch !== undefined);\n const result = yield* store.redeemLocked(redemption);\n\n const proofReceipt = input.redemption.prepare(\n result.decision,\n yield* CurrentCommitJournal,\n (value) => value,\n );\n\n yield* registerSqlCommitReceipt(proofReceipt);\n if (result.decision !== \"redeemed\") return yield* rejected();\n freshness.push(result.validUntil);\n\n const subjectId =\n input.action === \"register\"\n ? yield* resolve(\n mapping.subjectIds.toSubject(yield* resolve(mapping.subjectIds.allocate!())),\n )\n : target.revision!.subjectId;\n\n const native = yield* resolve(mapping.subjectIds.toNative(subjectId));\n\n ensure((yield* resolve(mapping.subjectIds.toSubject(native))) === subjectId);\n\n const securityRevision = SecurityRevision.make(yield* randomId),\n credentialRevision = SecurityRevision.make(yield* randomId),\n custodyRevision = SecurityRevision.make(yield* randomId);\n\n const credentialId = target.custody?.credentialId ?? (yield* randomId);\n\n const factorCondition = (revision: AuthenticationRevision) =>\n sql.and([\n sql`(select count(*) from ${credential.name} where ${id(credential, c.subjectId, native)} and ${activeCredential}) = ${revision.credentials.length}`,\n ...revision.credentials.map(\n (f) =>\n sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, f.credentialId)} and ${exact(credential, c.revision, f.revision)} and ${activeCredential})`,\n ),\n ]);\n\n if (target.revision !== null && batch !== undefined)\n yield* appendSqlBatchStatement(\n sqlBatchAssertion(\n sql,\n sql.and([\n sql`exists(select 1 from ${subject.name} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, target.revision.securityRevision)} and ${activeSubject})`,\n factorCondition(target.revision),\n ...freshness,\n ]),\n ),\n );\n let subjectRow = captured.subjectRow;\n\n if (input.action === \"register\") {\n const inserted = s.encodeInsert!({\n id: native,\n securityRevision,\n phoneNumber: target.phoneNumber,\n });\n\n yield* stage(subject.insert(inserted));\n // Native provisioning decodes database defaults in the final guarded\n // read; fixed batches require complete encoder values.\n subjectRow = inserted;\n }\n ensure(subjectRow !== undefined);\n const finalConditions: Fragment[] = [...freshness];\n\n if (target.source !== null) {\n const source = target.source;\n\n const retiredCustody = SecurityRevision.make(yield* randomId),\n retiredCredential = SecurityRevision.make(yield* randomId);\n\n yield* stage(\n sql`${identifier.update({ [i.status]: i.encodeStatus(false), [i.revision]: retiredCustody })} where ${numberKey(source.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, source.credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, source.custodyRevision)} and ${activeIdentifier}`,\n );\n yield* stage(\n sql`${credential.update({ [c.status]: c.encodeStatus(false), [c.revision]: retiredCredential })} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, source.credentialId)} and ${exact(credential, c.revision, source.credentialRevision)} and ${activeCredential}`,\n );\n finalConditions.push(\n sql`exists(select 1 from ${identifier.name} where ${numberKey(source.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, source.credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, retiredCustody)} and not (${activeIdentifier}))`,\n sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, source.credentialId)} and ${exact(credential, c.revision, retiredCredential)} and not (${activeCredential}))`,\n );\n }\n\n const values = {\n [i.moduleId]: mapping.moduleId,\n [i.credentialId]: credentialId,\n [i.namespace]: \"phone\",\n [i.value]: target.phoneNumber,\n [i.subjectId]: native,\n [i.revision]: custodyRevision,\n [i.verifiedAt]: mapping.encodeInstant(captured.now),\n [i.status]: i.encodeStatus(true),\n };\n\n if (target.custody === null) {\n yield* stage(\n identifier.insert({\n ...i.encodeInsert({\n moduleId: mapping.moduleId,\n credentialId,\n phoneNumber: target.phoneNumber,\n subjectId: native,\n revision: custodyRevision,\n verifiedAtMillis: captured.now,\n active: true,\n }),\n ...values,\n }),\n );\n yield* stage(\n credential.insert(\n c.encodeInsert({\n credentialId,\n subjectId: native,\n revision: credentialRevision,\n active: true,\n }),\n ),\n );\n } else {\n yield* stage(\n sql`${identifier.update(values)} where ${numberKey(target.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, target.custody.custodyRevision)} and ${identifier.column(i.verifiedAt)} is null and ${activeIdentifier}`,\n );\n yield* stage(\n sql`${credential.update({ [c.revision]: credentialRevision, [c.status]: c.encodeStatus(true) })} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, credentialId)} and ${exact(credential, c.revision, target.custody.credentialRevision)} and ${activeCredential}`,\n );\n }\n\n const revision = yield* Schema.decodeEffect(AuthenticationRevision)({\n subjectId,\n securityRevision,\n credentials: [\n ...(target.revision?.credentials ?? []).filter(\n (f) =>\n f.credentialId !== credentialId && f.credentialId !== target.source?.credentialId,\n ),\n { credentialId, revision: credentialRevision },\n ].sort((a, b) => a.credentialId.localeCompare(b.credentialId)),\n });\n\n finalConditions.push(\n factorCondition(revision),\n sql`exists(select 1 from ${identifier.name} where ${numberKey(target.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, custodyRevision)} and ${id(identifier, i.verifiedAt, mapping.encodeInstant(captured.now))} and ${activeIdentifier})`,\n );\n const resultingState = sql.and(finalConditions);\n\n if (input.action !== \"register\") {\n ensure(target.revision !== null);\n yield* stage(\n sql`${subject.update({ [s.securityRevision]: securityRevision })} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, target.revision.securityRevision)} and ${activeSubject} and ${resultingState}`,\n );\n subjectRow = { ...subjectRow, [s.securityRevision]: securityRevision };\n }\n finalConditions.push(\n sql`exists(select 1 from ${subject.name} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, securityRevision)} and ${activeSubject})`,\n );\n if (input.action === \"register\" && batch === undefined) {\n const rows =\n yield* sql`select ${subject.fields(\"provisioned_\")} from ${subject.name} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, securityRevision)} and ${activeSubject} and ${resultingState} limit 2`;\n\n ensure(rows.length === 1);\n subjectRow = subject.decode(rows[0]!, \"provisioned_\");\n }\n const final = sql.and(finalConditions);\n\n if (batch !== undefined)\n yield* registerSqlBatchPostcondition({\n name: \"phone-custody-authority\",\n statement: sqlBatchAssertion(sql, final),\n });\n else if (Option.isSome(external))\n yield* registerSqlPostcondition({\n name: \"phone-custody-authority\",\n check: Effect.gen(function* () {\n const rows = yield* sql`select 1 as valid where ${final}`;\n\n ensure(rows.length === 1);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepare(\n {\n _tag: \"Accepted\",\n credential: {\n moduleId: mapping.moduleId,\n phoneNumber: target.phoneNumber,\n custodyRevision,\n verifiedAtMillis: captured.now,\n credentialId,\n credentialRevision,\n revision,\n requirement: yield* resolve(mapping.subject.decodeRequirement(subjectRow)),\n },\n },\n project,\n );\n }),\n ),\n };\n\n return { phonePersistence, phoneSignInTargets };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"phone-native.mjs","names":[],"sources":["../../src/internal/phone-native.ts"],"sourcesContent":["import {\n CurrentCommitJournal,\n type LifecycleHooks,\n type PreparedCommit,\n type CommitJournal,\n} from \"@yielded/auth/Hooks\";\nimport * as P from \"@yielded/auth/PhoneOtp\";\nimport { ProofRedemptionInput } from \"@yielded/auth/Proofs\";\nimport {\n AuthenticationRevision,\n AuthenticationRequirement,\n SecurityRevision,\n snapshotAuthenticationEvidence,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Option, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { randomId } from \"./crypto\";\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyPhoneMapping } from \"./models/phone-model\";\nimport type { NativeSqlTables, SqlTable } from \"./native-sql-table\";\nimport { passwordEvidenceSatisfiedAt } from \"./password-policy\";\nimport { makeNativeProofStore } from \"./proof-native\";\nimport { exactSqlText, executeSqlChange } from \"./sql-change\";\nimport {\n appendSqlBatchStatement,\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n registerSqlBatchPostcondition,\n registerSqlCommitReceipt,\n registerSqlPostcondition,\n SqlBatchCommit,\n} from \"./sql-commit\";\n\nconst unavailable = () => P.PhoneOtpUnavailable.make({});\n\nconst ensure: (condition: unknown) => asserts condition = (condition) => {\n if (!condition) throw unavailable();\n};\n\nconst resolve = <A>(\n value: A | Effect.Effect<A, PersistenceMappingError>,\n): Effect.Effect<A, PersistenceMappingError> =>\n Effect.isEffect(value) ? value : Effect.succeed(value);\n\nconst targetCodec = Schema.fromJsonString(P.PhoneLifecycleTarget);\n\nconst sameRevision = (a: AuthenticationRevision, b: AuthenticationRevision) =>\n a.subjectId === b.subjectId &&\n a.securityRevision === b.securityRevision &&\n a.credentials.length === b.credentials.length &&\n a.credentials.every((f) =>\n b.credentials.some((g) => f.credentialId === g.credentialId && f.revision === g.revision),\n );\n\n/** Canonical identifier rows own phone custody, including permanent retired numbers. */\nexport const makeNativePhoneServices = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: AnyPhoneMapping,\n): Effect.fn.Return<\n {\n readonly phonePersistence: P.PhonePersistence[\"Service\"];\n readonly phoneSignInTargets: P.PhoneSignInTargets[\"Service\"];\n },\n P.PhoneOtpUnavailable,\n SqlClient | LifecycleHooks | Crypto.Crypto | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n const sql = (yield* SqlClient).withoutTransforms();\n const executor = yield* makeSqlCommitExecutor(unavailable);\n const crypto = yield* Crypto.Crypto;\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const s = mapping.subject,\n i = mapping.identifier,\n c = mapping.credential;\n\n const subject = tables(s.table),\n identifier = tables(i.table),\n credential = tables(c.table);\n\n const now = tables.expression(mapping.engineNowMillis);\n\n const activeSubject = tables.expression(s.activeCondition),\n activeIdentifier = tables.expression(i.activeCondition),\n activeCredential = tables.expression(c.activeCondition);\n\n const lock = sql.onDialectOrElse({ sqlite: () => sql``, orElse: () => sql`for update` });\n\n const exact = (t: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, t.column(key), t.value(key, value));\n\n const id = (t: SqlTable, key: string, value: unknown) =>\n sql`${t.column(key)} = ${t.value(key, value)}`;\n\n const numberKey = (number: string) =>\n sql.and([exact(identifier, i.namespace, \"phone\"), exact(identifier, i.value, number)]);\n\n const run = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n batch === undefined\n ? executor.run(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n : executor\n .batch(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const stage = Effect.fnUntraced(function* (statement: Fragment) {\n if (batch === undefined) ensure((yield* executeSqlChange(sql, statement)) === 1);\n else {\n yield* appendSqlBatchStatement(sql`${statement}`);\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = 1`));\n }\n });\n\n const prepare = <A>(\n decision: P.PhoneMutationDecision,\n project: (decision: P.PhoneMutationDecision, journal: CommitJournal) => PreparedCommit<A>,\n ) =>\n Effect.gen(function* () {\n const result = project(decision, yield* CurrentCommitJournal);\n\n yield* registerSqlCommitReceipt(result);\n\n return result;\n });\n\n const capture = Effect.fnUntraced(function* (\n input: Parameters<P.PhonePersistence[\"Service\"][\"target\"]>[0],\n ) {\n ensure(input.moduleId === mapping.moduleId);\n\n const numbers =\n input.sourcePhoneNumber === undefined || input.sourcePhoneNumber === input.phoneNumber\n ? [input.phoneNumber]\n : [input.phoneNumber, input.sourcePhoneNumber];\n\n const rows =\n yield* sql`select ${identifier.fields(\"phone_identifier_\")}, case when ${activeIdentifier} then 1 else 0 end as identifier_active from ${identifier.name} where ${sql.or(numbers.map(numberKey))} limit 3`;\n\n ensure(rows.length <= numbers.length);\n\n const identifiers = rows.map((row) => ({\n row: identifier.decode(row, \"phone_identifier_\"),\n active: Number(row.identifier_active) === 1,\n }));\n\n ensure(\n identifiers.every(\n ({ row }) => row[i.namespace] === \"phone\" && numbers.includes(String(row[i.value])),\n ),\n );\n const destination = identifiers.find(({ row }) => row[i.value] === input.phoneNumber);\n const source = identifiers.find(({ row }) => row[i.value] === input.sourcePhoneNumber);\n\n const subjectId =\n input.subjectId ??\n (destination === undefined\n ? undefined\n : yield* resolve(mapping.subjectIds.toSubject(destination.row[i.subjectId])));\n\n let subjectRow: Record<string, unknown> | undefined;\n let revision: AuthenticationRevision | null = null;\n const factors: { readonly row: Record<string, unknown>; readonly active: boolean }[] = [];\n let instant: number;\n\n if (subjectId === undefined) {\n const clock = yield* sql`select ${now} as engine_now`;\n\n instant = Number(clock[0]?.engine_now);\n } else {\n const native = yield* resolve(mapping.subjectIds.toNative(subjectId));\n\n ensure((yield* resolve(mapping.subjectIds.toSubject(native))) === subjectId);\n\n const joined =\n yield* sql`select ${subject.fields(\"phone_subject_\")}, ${credential.fields(\"phone_factor_\")}, case when ${activeSubject} then 1 else 0 end as subject_active, case when ${activeCredential} then 1 else 0 end as credential_active, ${now} as engine_now from ${subject.name} left join ${credential.name} on ${id(credential, c.subjectId, native)} and (${activeCredential} ${identifiers.length === 0 ? sql`` : sql`or ${sql.or(identifiers.map((value) => exact(credential, c.id, value.row[i.credentialId])))}`}) where ${id(subject, s.id, native)} limit 67`;\n\n ensure(joined.length <= 66);\n const first = joined[0];\n\n instant = Number(first?.engine_now);\n if (first !== undefined) {\n subjectRow = subject.decode(first, \"phone_subject_\");\n ensure((yield* resolve(mapping.subjectIds.toSubject(subjectRow[s.id]))) === subjectId);\n for (const joinedRow of joined) {\n const row = credential.decode(joinedRow, \"phone_factor_\");\n\n if (row[c.id] === null || row[c.id] === undefined) continue;\n ensure((yield* resolve(mapping.subjectIds.toSubject(row[c.subjectId]))) === subjectId);\n ensure(!factors.some((f) => f.row[c.id] === row[c.id]));\n factors.push({ row, active: Number(joinedRow.credential_active) === 1 });\n }\n ensure(factors.filter((f) => f.active).length <= 64);\n if (Number(first.subject_active) === 1)\n revision = yield* Schema.decodeUnknownEffect(AuthenticationRevision)({\n subjectId,\n securityRevision: subjectRow[s.securityRevision],\n credentials: factors\n .filter((f) => f.active)\n .map((f) => ({ credentialId: f.row[c.id], revision: f.row[c.revision] }))\n .sort((a, b) => String(a.credentialId).localeCompare(String(b.credentialId))),\n });\n }\n }\n ensure(Number.isSafeInteger(instant));\n\n const custody = Effect.fnUntraced(function* (value: typeof destination) {\n if (value === undefined || value.row[i.moduleId] !== mapping.moduleId) return null;\n const factor = factors.find((f) => f.row[c.id] === value.row[i.credentialId]);\n\n if (factor === undefined) return null;\n const owner = yield* resolve(mapping.subjectIds.toSubject(value.row[i.subjectId]));\n\n if (owner !== subjectId) return null;\n\n return yield* Schema.decodeUnknownEffect(P.PhoneCustody)({\n phoneNumber: value.row[i.value],\n subjectId: owner,\n credentialId: value.row[i.credentialId],\n custodyRevision: value.row[i.revision],\n credentialRevision: factor.row[c.revision],\n verifiedAtMillis:\n value.row[i.verifiedAt] === null || value.row[i.verifiedAt] === undefined\n ? null\n : mapping.proofs.clock.decodeInstant(value.row[i.verifiedAt]),\n state:\n !value.active || !factor.active\n ? \"retired\"\n : value.row[i.verifiedAt] === null || value.row[i.verifiedAt] === undefined\n ? \"unverified\"\n : \"verified\",\n });\n });\n\n const targetCustody = yield* custody(destination);\n const sourceCustody = yield* custody(source);\n\n const eligible =\n input.action === \"register\"\n ? destination === undefined\n : revision !== null &&\n input.subjectId === subjectId &&\n (destination === undefined ||\n (targetCustody !== null &&\n targetCustody.subjectId === subjectId &&\n targetCustody.state === \"unverified\")) &&\n (input.action !== \"change\" ||\n (input.sourcePhoneNumber !== input.phoneNumber &&\n sourceCustody !== null &&\n sourceCustody.subjectId === subjectId &&\n sourceCustody.state === \"verified\"));\n\n const target = yield* Schema.decodeEffect(P.PhoneLifecycleTarget)({\n phoneNumber: input.phoneNumber,\n custody: targetCustody,\n source: sourceCustody,\n revision,\n eligible,\n });\n\n return { target, subjectRow, destination, source, factors, now: instant };\n });\n\n const phoneSignInTargets: P.PhoneSignInTargets[\"Service\"] = {\n lookup: (input) =>\n executor.read(\n Effect.gen(function* () {\n const current = yield* capture({ ...input, action: \"verify\" });\n const { custody, revision } = current.target;\n\n if (\n custody === null ||\n custody.state !== \"verified\" ||\n custody.verifiedAtMillis === null ||\n revision === null ||\n current.subjectRow === undefined ||\n !revision.credentials.some(\n (f) =>\n f.credentialId === custody.credentialId &&\n f.revision === custody.credentialRevision,\n )\n )\n return Option.none();\n\n return Option.some(\n yield* Schema.decodeEffect(P.PhoneCredentialSnapshot)({\n moduleId: input.moduleId,\n phoneNumber: input.phoneNumber,\n custodyRevision: custody.custodyRevision,\n verifiedAtMillis: custody.verifiedAtMillis,\n credentialId: custody.credentialId,\n credentialRevision: custody.credentialRevision,\n revision,\n requirement: yield* resolve(mapping.subject.decodeRequirement(current.subjectRow)),\n }),\n );\n }),\n ),\n };\n\n const phonePersistence: P.PhonePersistence[\"Service\"] = {\n target: (input) => executor.read(Effect.map(capture({ ...input }), (value) => value.target)),\n mutate: (original, project) =>\n run(\n Effect.gen(function* () {\n const target = yield* Schema.decodeEffect(targetCodec)(\n yield* Schema.encodeEffect(targetCodec)(original.target),\n );\n\n const redemption = yield* Schema.decodeEffect(Schema.toCodecIso(ProofRedemptionInput))(\n yield* Schema.encodeEffect(Schema.toCodecIso(ProofRedemptionInput))(\n original.redemption.input,\n ),\n );\n\n const input = {\n ...original,\n target,\n redemption: { input: redemption, prepare: original.redemption.prepare },\n };\n\n const rejected = () => prepare({ _tag: \"Rejected\" }, project);\n\n ensure(input.moduleId === mapping.moduleId);\n if (\n input.action === \"register\" &&\n (mapping.subjectIds.allocate === undefined || s.encodeInsert === undefined)\n )\n return yield* rejected();\n const policy = yield* Schema.decodeEffect(P.PhoneLifecyclePolicy)(input.policy);\n\n if (\n policy.maximumEvidenceAgeMillis !== mapping.policy.maximumEvidenceAgeMillis ||\n policy.requireImmediateInvalidation !== mapping.policy.requireImmediateInvalidation\n )\n return yield* rejected();\n if (target.revision !== null) {\n const native = yield* resolve(mapping.subjectIds.toNative(target.revision.subjectId));\n\n const rows =\n yield* sql`select ${subject.fields(\"phone_locked_\")} from ${subject.name} where ${id(subject, s.id, native)} limit 2 ${batch === undefined ? lock : sql``}`;\n\n if (\n rows.length !== 1 ||\n (yield* resolve(\n mapping.subjectIds.toSubject(subject.decode(rows[0]!, \"phone_locked_\")[s.id]),\n )) !== target.revision.subjectId\n )\n return yield* rejected();\n }\n\n const captured = yield* capture({\n moduleId: input.moduleId,\n action: input.action,\n phoneNumber: target.phoneNumber,\n ...(target.revision === null ? {} : { subjectId: target.revision.subjectId }),\n ...(target.source === null ? {} : { sourcePhoneNumber: target.source.phoneNumber }),\n });\n\n if (\n !captured.target.eligible ||\n (yield* Schema.encodeEffect(targetCodec)(captured.target)) !==\n (yield* Schema.encodeEffect(targetCodec)(target))\n )\n return yield* rejected();\n const binding = redemption.binding;\n\n if (\n redemption.moduleId !== `${mapping.moduleId}/lifecycle` ||\n redemption.purpose !== \"phone-lifecycle\" ||\n binding.identifier.namespace !== \"phone\" ||\n binding.identifier.value !== target.phoneNumber\n )\n return yield* rejected();\n if (\n input.action === \"register\"\n ? binding._tag !== \"Identifier\"\n : binding._tag === \"Identifier\" ||\n target.revision === null ||\n !sameRevision(binding.revision, target.revision)\n )\n return yield* rejected();\n if (\n (input.action === \"verify\" && binding._tag !== \"Subject\") ||\n (input.action === \"change\" && binding._tag !== \"IdentifierChange\")\n )\n return yield* rejected();\n const freshness: Fragment[] = [];\n\n if (input.action !== \"register\") {\n const authorization = input.authorization;\n\n if (\n authorization === undefined ||\n target.revision === null ||\n captured.subjectRow === undefined\n )\n return yield* rejected();\n const evidence = yield* snapshotAuthenticationEvidence(authorization.evidence);\n const challenge = authorization.challenge;\n\n if (\n challenge.moduleId !== mapping.moduleId ||\n challenge.action !== input.action ||\n challenge.commandId !== input.commandId ||\n challenge.phoneNumber !== target.phoneNumber ||\n challenge.sourcePhoneNumber !== target.source?.phoneNumber ||\n challenge.flowId !== binding.flowId ||\n challenge.bindingDigest !== binding.contextDigest ||\n challenge.flowId !== evidence.flowId ||\n challenge.bindingDigest !== evidence.bindingDigest ||\n !sameRevision(challenge.revision, target.revision) ||\n !sameRevision(evidence.revision, target.revision)\n )\n return yield* rejected();\n\n const requirements = [\n authorization.requirement,\n authorization.actionRequirement,\n yield* resolve(s.decodeRequirement(captured.subjectRow)),\n ...(s.decodeActionRequirement === undefined\n ? []\n : [yield* resolve(s.decodeActionRequirement(captured.subjectRow, input.action))]),\n ];\n\n for (const originalRequirement of requirements) {\n const requirement =\n yield* Schema.decodeEffect(AuthenticationRequirement)(originalRequirement);\n\n if (!(yield* passwordEvidenceSatisfiedAt(evidence, requirement, captured.now)))\n return yield* rejected();\n }\n if (evidence.proofs.length === 0) return yield* rejected();\n for (const proof of evidence.proofs) {\n const at = DateTime.toEpochMillis(proof.verifiedAt);\n\n const age = Math.min(\n policy.maximumEvidenceAgeMillis,\n ...requirements.map((r) => r.maximumAgeMillis),\n );\n\n if (\n !target.revision.credentials.some((f) => f.credentialId === proof.credentialId) ||\n at > captured.now ||\n captured.now >= at + age\n )\n return yield* rejected();\n freshness.push(sql`${now} >= ${at} and ${now} < ${at + age}`);\n }\n }\n const store = yield* makeNativeProofStore(tables, mapping.proofs, batch !== undefined);\n const result = yield* store.redeemLocked(redemption);\n\n const proofReceipt = input.redemption.prepare(\n result.decision,\n yield* CurrentCommitJournal,\n (value) => value,\n );\n\n yield* registerSqlCommitReceipt(proofReceipt);\n if (result.decision !== \"redeemed\") return yield* rejected();\n freshness.push(result.validUntil);\n\n const subjectId =\n input.action === \"register\"\n ? yield* resolve(\n mapping.subjectIds.toSubject(yield* resolve(mapping.subjectIds.allocate!())),\n )\n : target.revision!.subjectId;\n\n const native = yield* resolve(mapping.subjectIds.toNative(subjectId));\n\n ensure((yield* resolve(mapping.subjectIds.toSubject(native))) === subjectId);\n\n const securityRevision = SecurityRevision.make(yield* randomId),\n credentialRevision = SecurityRevision.make(yield* randomId),\n custodyRevision = SecurityRevision.make(yield* randomId);\n\n const credentialId = target.custody?.credentialId ?? (yield* randomId);\n\n const factorCondition = (revision: AuthenticationRevision) =>\n sql.and([\n sql`(select count(*) from ${credential.name} where ${id(credential, c.subjectId, native)} and ${activeCredential}) = ${revision.credentials.length}`,\n ...revision.credentials.map(\n (f) =>\n sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, f.credentialId)} and ${exact(credential, c.revision, f.revision)} and ${activeCredential})`,\n ),\n ]);\n\n if (target.revision !== null && batch !== undefined)\n yield* appendSqlBatchStatement(\n sqlBatchAssertion(\n sql,\n sql.and([\n sql`exists(select 1 from ${subject.name} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, target.revision.securityRevision)} and ${activeSubject})`,\n factorCondition(target.revision),\n ...freshness,\n ]),\n ),\n );\n let subjectRow = captured.subjectRow;\n\n if (input.action === \"register\") {\n const inserted = s.encodeInsert!({\n id: native,\n securityRevision,\n phoneNumber: target.phoneNumber,\n });\n\n yield* stage(subject.insert(inserted));\n // Native provisioning decodes database defaults in the final guarded\n // read; fixed batches require complete encoder values.\n subjectRow = inserted;\n }\n ensure(subjectRow !== undefined);\n const finalConditions: Fragment[] = [...freshness];\n\n if (target.source !== null) {\n const source = target.source;\n\n const retiredCustody = SecurityRevision.make(yield* randomId),\n retiredCredential = SecurityRevision.make(yield* randomId);\n\n yield* stage(\n sql`${identifier.update({ [i.status]: i.encodeStatus(false), [i.revision]: retiredCustody })} where ${numberKey(source.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, source.credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, source.custodyRevision)} and ${activeIdentifier}`,\n );\n yield* stage(\n sql`${credential.update({ [c.status]: c.encodeStatus(false), [c.revision]: retiredCredential })} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, source.credentialId)} and ${exact(credential, c.revision, source.credentialRevision)} and ${activeCredential}`,\n );\n finalConditions.push(\n sql`exists(select 1 from ${identifier.name} where ${numberKey(source.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, source.credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, retiredCustody)} and not (${activeIdentifier}))`,\n sql`exists(select 1 from ${credential.name} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, source.credentialId)} and ${exact(credential, c.revision, retiredCredential)} and not (${activeCredential}))`,\n );\n }\n\n const values = {\n [i.moduleId]: mapping.moduleId,\n [i.credentialId]: credentialId,\n [i.namespace]: \"phone\",\n [i.value]: target.phoneNumber,\n [i.subjectId]: native,\n [i.revision]: custodyRevision,\n [i.verifiedAt]: mapping.encodeInstant(captured.now),\n [i.status]: i.encodeStatus(true),\n };\n\n if (target.custody === null) {\n yield* stage(\n identifier.insert({\n ...i.encodeInsert({\n moduleId: mapping.moduleId,\n credentialId,\n phoneNumber: target.phoneNumber,\n subjectId: native,\n revision: custodyRevision,\n verifiedAtMillis: captured.now,\n active: true,\n }),\n ...values,\n }),\n );\n yield* stage(\n credential.insert(\n c.encodeInsert({\n credentialId,\n subjectId: native,\n revision: credentialRevision,\n active: true,\n }),\n ),\n );\n } else {\n yield* stage(\n sql`${identifier.update(values)} where ${numberKey(target.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, target.custody.custodyRevision)} and ${identifier.column(i.verifiedAt)} is null and ${activeIdentifier}`,\n );\n yield* stage(\n sql`${credential.update({ [c.revision]: credentialRevision, [c.status]: c.encodeStatus(true) })} where ${id(credential, c.subjectId, native)} and ${exact(credential, c.id, credentialId)} and ${exact(credential, c.revision, target.custody.credentialRevision)} and ${activeCredential}`,\n );\n }\n\n const revision = yield* Schema.decodeEffect(AuthenticationRevision)({\n subjectId,\n securityRevision,\n credentials: [\n ...(target.revision?.credentials ?? []).filter(\n (f) =>\n f.credentialId !== credentialId && f.credentialId !== target.source?.credentialId,\n ),\n { credentialId, revision: credentialRevision },\n ].sort((a, b) => a.credentialId.localeCompare(b.credentialId)),\n });\n\n finalConditions.push(\n factorCondition(revision),\n sql`exists(select 1 from ${identifier.name} where ${numberKey(target.phoneNumber)} and ${exact(identifier, i.moduleId, mapping.moduleId)} and ${exact(identifier, i.credentialId, credentialId)} and ${id(identifier, i.subjectId, native)} and ${exact(identifier, i.revision, custodyRevision)} and ${id(identifier, i.verifiedAt, mapping.encodeInstant(captured.now))} and ${activeIdentifier})`,\n );\n const resultingState = sql.and(finalConditions);\n\n if (input.action !== \"register\") {\n ensure(target.revision !== null);\n yield* stage(\n sql`${subject.update({ [s.securityRevision]: securityRevision })} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, target.revision.securityRevision)} and ${activeSubject} and ${resultingState}`,\n );\n subjectRow = { ...subjectRow, [s.securityRevision]: securityRevision };\n }\n finalConditions.push(\n sql`exists(select 1 from ${subject.name} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, securityRevision)} and ${activeSubject})`,\n );\n if (input.action === \"register\" && batch === undefined) {\n const rows =\n yield* sql`select ${subject.fields(\"provisioned_\")} from ${subject.name} where ${id(subject, s.id, native)} and ${exact(subject, s.securityRevision, securityRevision)} and ${activeSubject} and ${resultingState} limit 2`;\n\n ensure(rows.length === 1);\n subjectRow = subject.decode(rows[0]!, \"provisioned_\");\n }\n const final = sql.and(finalConditions);\n\n if (batch !== undefined)\n yield* registerSqlBatchPostcondition({\n name: \"phone-custody-authority\",\n statement: sqlBatchAssertion(sql, final),\n });\n else if (Option.isSome(external))\n yield* registerSqlPostcondition({\n name: \"phone-custody-authority\",\n check: Effect.gen(function* () {\n const rows = yield* sql`select 1 as valid where ${final}`;\n\n ensure(rows.length === 1);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepare(\n {\n _tag: \"Accepted\",\n credential: {\n moduleId: mapping.moduleId,\n phoneNumber: target.phoneNumber,\n custodyRevision,\n verifiedAtMillis: captured.now,\n credentialId,\n credentialRevision,\n revision,\n requirement: yield* resolve(mapping.subject.decodeRequirement(subjectRow)),\n },\n },\n project,\n );\n }),\n ),\n };\n\n return { phonePersistence, phoneSignInTargets };\n});\n"],"mappings":"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"}
@@ -7,11 +7,12 @@ import { preservesRevision } from "./session-policy.mjs";
7
7
  import { DateTime, Effect, Option } from "effect";
8
8
  import "@yielded/auth/Hooks";
9
9
  import { PendingAuthenticationInvalid, StaleAuthentication } from "@yielded/auth/Sessions";
10
+ import { AuthenticationClock } from "@yielded/auth/Operations";
10
11
  //#region src/internal/session-native-authority.ts
11
12
  const sessionEvidenceDeadline = Effect.fnUntraced(function* (evidence, requirement, now) {
12
13
  const assessed = yield* assessSessionAt(evidence, requirement, now);
13
14
  if (!assessed.satisfied) return yield* StaleAuthentication.make({});
14
- const boundaries = [...new Set(evidence.proofs.map((proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis))].filter((n) => n > now).sort((a, b) => a - b);
15
+ const boundaries = [.../* @__PURE__ */ new Set([...evidence.proofs.map((proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis), ...evidence.completionExpiresAt === void 0 ? [] : [DateTime.toEpochMillis(evidence.completionExpiresAt)]])].filter((n) => n > now).sort((a, b) => a - b);
15
16
  for (const boundary of boundaries) {
16
17
  const after = yield* assessSessionAt(evidence, requirement, boundary).pipe(Effect.result);
17
18
  if (after._tag === "Failure" || !after.success.satisfied) return {
@@ -44,7 +45,7 @@ const makeNativeAuthenticationAuthorityServices = Effect.fnUntraced(function* (t
44
45
  yield* pending.consume("Login", input, native);
45
46
  return found.record.expiresAtMillis;
46
47
  });
47
- const native = (work) => batch === void 0 ? executor.operation(normalizeSessionOperation(work)) : executor.operationBatch(normalizeSessionOperation(work)).pipe(Effect.provideService(SqlBatchCommit, batch));
48
+ const native = (work) => (batch === void 0 ? executor.operation(normalizeSessionOperation(work)) : executor.operationBatch(normalizeSessionOperation(work)).pipe(Effect.provideService(SqlBatchCommit, batch))).pipe(Effect.provideService(AuthenticationClock, state.clockPolicy));
48
49
  return { authenticationAuthority: {
49
50
  capture: (subjectId, credentialIds) => executor.read(state.read(subjectId)).pipe(Effect.flatMap((current) => current === void 0 || new Set(credentialIds).size !== credentialIds.length || credentialIds.some((id) => !current.revision.credentials.some((f) => f.credentialId === id)) ? Effect.fail(StaleAuthentication.make({})) : Effect.succeed({
50
51
  revision: current.revision,
@@ -55,8 +56,8 @@ const makeNativeAuthenticationAuthorityServices = Effect.fnUntraced(function* (t
55
56
  const current = yield* readEvidence(input.evidence, true);
56
57
  if (current === void 0) return yield* StaleAuthentication.make({});
57
58
  const assessment = yield* sessionEvidenceDeadline(input.evidence, current.requirement, current.now);
58
- const expires = DateTime.toEpochMillis(input.expiresAt), absolute = DateTime.toEpochMillis(input.absoluteExpiresAt);
59
- if (current.now >= expires || expires > absolute) return yield* StaleAuthentication.make({});
59
+ const issued = DateTime.toEpochMillis(input.issuedAt), expires = DateTime.toEpochMillis(input.expiresAt), absolute = DateTime.toEpochMillis(input.absoluteExpiresAt);
60
+ if (Math.max(issued, DateTime.toEpochMillis(input.now)) - current.now > state.clockPolicy.futureToleranceMillis || input.evidence.proofs.some((proof) => DateTime.toEpochMillis(proof.verifiedAt) > issued) || issued >= expires || current.now >= expires || expires > absolute) return yield* StaleAuthentication.make({});
60
61
  const pendingExpiry = input.pending === void 0 ? void 0 : yield* consume(input.pending, input.evidence, current.native);
61
62
  const deadline = Math.min(assessment.validUntil, expires, pendingExpiry ?? Infinity);
62
63
  yield* state.guardPolicy(current);
@@ -76,7 +77,7 @@ const makeNativeAuthenticationAuthorityServices = Effect.fnUntraced(function* (t
76
77
  if (input.pending !== void 0 && pending !== void 0) {
77
78
  if ((yield* sql`select 1 where ${pending.consumedCondition("Login", input.pending, current.native)}`).length !== 1) return yield* PendingAuthenticationInvalid.make({});
78
79
  }
79
- }).pipe(Effect.mapError((cause) => PersistenceMappingError.make({
80
+ }).pipe(Effect.provideService(AuthenticationClock, state.clockPolicy), Effect.mapError((cause) => PersistenceMappingError.make({
80
81
  operation: "decode",
81
82
  cause
82
83
  })))
@@ -1 +1 @@
1
- {"version":3,"file":"session-native-authority.mjs","names":[],"sources":["../../src/internal/session-native-authority.ts"],"sourcesContent":["import { type LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport {\n type AuthenticationAuthority,\n StaleAuthentication,\n PendingAuthenticationInvalid,\n type AuthenticationEvidence,\n type PendingConsumption,\n type AuthenticationRequirement,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AuthenticationAuthorityMapping } from \"./models/session-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativeSessionPending, prepareNativeSession } from \"./session-native-pending\";\nimport {\n makeNativeSessionAuthorityState,\n normalizeSessionOperation,\n sessionUnavailable,\n sameSessionRevision,\n assessSessionAt,\n} from \"./session-native-state\";\nimport { preservesRevision } from \"./session-policy\";\nimport {\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n SqlBatchCommit,\n registerSqlPostcondition,\n registerSqlBatchPostcondition,\n appendSqlBatchStatement,\n} from \"./sql-commit\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativeAuthenticationAuthorityMapping<Claims> = AuthenticationAuthorityMapping<\n Claims,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown\n>;\n\nexport const sessionEvidenceDeadline = Effect.fnUntraced(function* (\n evidence: AuthenticationEvidence,\n requirement: AuthenticationRequirement,\n now: number,\n) {\n const assessed = yield* assessSessionAt(evidence, requirement, now);\n\n if (!assessed.satisfied) return yield* StaleAuthentication.make({});\n\n const boundaries = [\n ...new Set(\n evidence.proofs.map(\n (proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis,\n ),\n ),\n ]\n .filter((n) => n > now)\n .sort((a, b) => a - b);\n\n for (const boundary of boundaries) {\n const after = yield* assessSessionAt(evidence, requirement, boundary).pipe(Effect.result);\n\n if (after._tag === \"Failure\" || !after.success.satisfied)\n return { assessed, validUntil: boundary };\n }\n\n return yield* StaleAuthentication.make({});\n});\n\nexport const makeNativeAuthenticationAuthorityServices = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativeAuthenticationAuthorityMapping<Claims>,\n): Effect.fn.Return<\n { readonly authenticationAuthority: AuthenticationAuthority[\"Service\"] },\n never,\n SqlClient | LifecycleHooks | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n\n const state = yield* makeNativeSessionAuthorityState(tables, mapping, batch !== undefined);\n const executor = yield* makeSqlCommitExecutor(sessionUnavailable);\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const pending =\n mapping.pending === undefined\n ? undefined\n : yield* makeNativeSessionPending(\n tables,\n { ...mapping, ...mapping.pending },\n batch !== undefined,\n );\n\n const { sql, now } = state;\n\n const readEvidence = Effect.fnUntraced(function* (\n evidence: AuthenticationEvidence,\n locking: boolean,\n ) {\n const current = yield* state.read(evidence.revision.subjectId, locking);\n\n return current !== undefined && sameSessionRevision(current.revision, evidence.revision)\n ? current\n : undefined;\n });\n\n const consume = Effect.fnUntraced(function* (\n input: PendingConsumption,\n evidence: AuthenticationEvidence,\n native: unknown,\n ) {\n if (pending === undefined || mapping.pending === undefined)\n return yield* PendingAuthenticationInvalid.make({});\n const found = yield* pending.read(\"Login\", input.digest);\n\n if (\n found === undefined ||\n found.record.version !== input.version ||\n found.record.flowId !== input.flowId ||\n found.record.bindingDigest !== input.bindingDigest ||\n found.record.subjectId !== evidence.revision.subjectId\n )\n return yield* PendingAuthenticationInvalid.make({});\n const original = yield* mapping.pending.login.decode(found.record.snapshot);\n\n if (\n original.digest !== input.digest ||\n original.version !== input.version ||\n !preservesRevision(evidence, original.evidence) ||\n DateTime.toEpochMillis(original.expiresAt) !== found.record.expiresAtMillis ||\n original.attemptLimit !== found.record.attemptLimit\n )\n return yield* PendingAuthenticationInvalid.make({});\n yield* pending.consume(\"Login\", input, native);\n\n return found.record.expiresAtMillis;\n });\n\n const native = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n batch === undefined\n ? executor.operation(normalizeSessionOperation(work))\n : executor\n .operationBatch(normalizeSessionOperation(work))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const authenticationAuthority: AuthenticationAuthority[\"Service\"] = {\n capture: (subjectId, credentialIds) =>\n executor\n .read(state.read(subjectId))\n .pipe(\n Effect.flatMap((current) =>\n current === undefined ||\n new Set(credentialIds).size !== credentialIds.length ||\n credentialIds.some(\n (id) => !current.revision.credentials.some((f) => f.credentialId === id),\n )\n ? Effect.fail(StaleAuthentication.make({}))\n : Effect.succeed({ revision: current.revision, requirement: current.requirement }),\n ),\n ),\n requirements: (evidence) =>\n executor\n .read(readEvidence(evidence, false))\n .pipe(\n Effect.flatMap((current) =>\n current === undefined\n ? Effect.fail(StaleAuthentication.make({}))\n : Effect.succeed(current.requirement),\n ),\n ),\n approve: (input, project) =>\n native(\n Effect.gen(function* () {\n const current = yield* readEvidence(input.evidence, true);\n\n if (current === undefined) return yield* StaleAuthentication.make({});\n\n const assessment = yield* sessionEvidenceDeadline(\n input.evidence,\n current.requirement,\n current.now,\n );\n\n const expires = DateTime.toEpochMillis(input.expiresAt),\n absolute = DateTime.toEpochMillis(input.absoluteExpiresAt);\n\n if (current.now >= expires || expires > absolute)\n return yield* StaleAuthentication.make({});\n\n const pendingExpiry =\n input.pending === undefined\n ? undefined\n : yield* consume(input.pending, input.evidence, current.native);\n\n const deadline = Math.min(assessment.validUntil, expires, pendingExpiry ?? Infinity);\n\n yield* state.guardPolicy(current);\n const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${now} >= ${current.now} and ${now} < ${deadline}`;\n\n if (batch !== undefined) {\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));\n yield* registerSqlBatchPostcondition({\n name: \"signed-session-issuance-authority\",\n statement: sqlBatchAssertion(\n sql,\n input.pending === undefined || pending === undefined\n ? condition\n : sql`${condition} and ${pending.consumedCondition(\"Login\", input.pending, current.native)}`,\n ),\n });\n } else if (Option.isSome(external)) {\n yield* registerSqlPostcondition({\n name: \"signed-session-issuance-authority\",\n check: Effect.gen(function* () {\n const final = yield* readEvidence(input.evidence, false);\n\n if (final === undefined || final.now < current.now || final.now >= deadline)\n return yield* StaleAuthentication.make({});\n yield* sessionEvidenceDeadline(input.evidence, final.requirement, final.now);\n if (input.pending !== undefined && pending !== undefined) {\n const rows =\n yield* sql`select 1 where ${pending.consumedCondition(\"Login\", input.pending, current.native)}`;\n\n if (rows.length !== 1) return yield* PendingAuthenticationInvalid.make({});\n }\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n } else {\n // A signed credential has no session INSERT that can enforce the\n // original evidence deadline. Check that deadline at this owner.\n const rows =\n yield* sql`select 1 where ${now} >= ${current.now} and ${now} < ${deadline}`;\n\n if (rows.length !== 1) return yield* StaleAuthentication.make({});\n }\n\n return yield* prepareNativeSession(undefined, project);\n }),\n ),\n };\n\n return { authenticationAuthority };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-native-authority.mjs","names":[],"sources":["../../src/internal/session-native-authority.ts"],"sourcesContent":["import { type LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { AuthenticationClock } from \"@yielded/auth/Operations\";\nimport {\n type AuthenticationAuthority,\n StaleAuthentication,\n PendingAuthenticationInvalid,\n type AuthenticationEvidence,\n type PendingConsumption,\n type AuthenticationRequirement,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AuthenticationAuthorityMapping } from \"./models/session-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativeSessionPending, prepareNativeSession } from \"./session-native-pending\";\nimport {\n makeNativeSessionAuthorityState,\n normalizeSessionOperation,\n sessionUnavailable,\n sameSessionRevision,\n assessSessionAt,\n} from \"./session-native-state\";\nimport { preservesRevision } from \"./session-policy\";\nimport {\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n SqlBatchCommit,\n registerSqlPostcondition,\n registerSqlBatchPostcondition,\n appendSqlBatchStatement,\n} from \"./sql-commit\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativeAuthenticationAuthorityMapping<Claims> = AuthenticationAuthorityMapping<\n Claims,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown\n>;\n\nexport const sessionEvidenceDeadline = Effect.fnUntraced(function* (\n evidence: AuthenticationEvidence,\n requirement: AuthenticationRequirement,\n now: number,\n) {\n const assessed = yield* assessSessionAt(evidence, requirement, now);\n\n if (!assessed.satisfied) return yield* StaleAuthentication.make({});\n\n const boundaries = [\n ...new Set([\n ...evidence.proofs.map(\n (proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis,\n ),\n ...(evidence.completionExpiresAt === undefined\n ? []\n : [DateTime.toEpochMillis(evidence.completionExpiresAt)]),\n ]),\n ]\n .filter((n) => n > now)\n .sort((a, b) => a - b);\n\n for (const boundary of boundaries) {\n const after = yield* assessSessionAt(evidence, requirement, boundary).pipe(Effect.result);\n\n if (after._tag === \"Failure\" || !after.success.satisfied)\n return { assessed, validUntil: boundary };\n }\n\n return yield* StaleAuthentication.make({});\n});\n\nexport const makeNativeAuthenticationAuthorityServices = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativeAuthenticationAuthorityMapping<Claims>,\n): Effect.fn.Return<\n { readonly authenticationAuthority: AuthenticationAuthority[\"Service\"] },\n never,\n SqlClient | LifecycleHooks | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n\n const state = yield* makeNativeSessionAuthorityState(tables, mapping, batch !== undefined);\n const executor = yield* makeSqlCommitExecutor(sessionUnavailable);\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const pending =\n mapping.pending === undefined\n ? undefined\n : yield* makeNativeSessionPending(\n tables,\n { ...mapping, ...mapping.pending },\n batch !== undefined,\n );\n\n const { sql, now } = state;\n\n const readEvidence = Effect.fnUntraced(function* (\n evidence: AuthenticationEvidence,\n locking: boolean,\n ) {\n const current = yield* state.read(evidence.revision.subjectId, locking);\n\n return current !== undefined && sameSessionRevision(current.revision, evidence.revision)\n ? current\n : undefined;\n });\n\n const consume = Effect.fnUntraced(function* (\n input: PendingConsumption,\n evidence: AuthenticationEvidence,\n native: unknown,\n ) {\n if (pending === undefined || mapping.pending === undefined)\n return yield* PendingAuthenticationInvalid.make({});\n const found = yield* pending.read(\"Login\", input.digest);\n\n if (\n found === undefined ||\n found.record.version !== input.version ||\n found.record.flowId !== input.flowId ||\n found.record.bindingDigest !== input.bindingDigest ||\n found.record.subjectId !== evidence.revision.subjectId\n )\n return yield* PendingAuthenticationInvalid.make({});\n const original = yield* mapping.pending.login.decode(found.record.snapshot);\n\n if (\n original.digest !== input.digest ||\n original.version !== input.version ||\n !preservesRevision(evidence, original.evidence) ||\n DateTime.toEpochMillis(original.expiresAt) !== found.record.expiresAtMillis ||\n original.attemptLimit !== found.record.attemptLimit\n )\n return yield* PendingAuthenticationInvalid.make({});\n yield* pending.consume(\"Login\", input, native);\n\n return found.record.expiresAtMillis;\n });\n\n const native = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n (batch === undefined\n ? executor.operation(normalizeSessionOperation(work))\n : executor\n .operationBatch(normalizeSessionOperation(work))\n .pipe(Effect.provideService(SqlBatchCommit, batch))\n ).pipe(Effect.provideService(AuthenticationClock, state.clockPolicy));\n\n const authenticationAuthority: AuthenticationAuthority[\"Service\"] = {\n capture: (subjectId, credentialIds) =>\n executor\n .read(state.read(subjectId))\n .pipe(\n Effect.flatMap((current) =>\n current === undefined ||\n new Set(credentialIds).size !== credentialIds.length ||\n credentialIds.some(\n (id) => !current.revision.credentials.some((f) => f.credentialId === id),\n )\n ? Effect.fail(StaleAuthentication.make({}))\n : Effect.succeed({ revision: current.revision, requirement: current.requirement }),\n ),\n ),\n requirements: (evidence) =>\n executor\n .read(readEvidence(evidence, false))\n .pipe(\n Effect.flatMap((current) =>\n current === undefined\n ? Effect.fail(StaleAuthentication.make({}))\n : Effect.succeed(current.requirement),\n ),\n ),\n approve: (input, project) =>\n native(\n Effect.gen(function* () {\n const current = yield* readEvidence(input.evidence, true);\n\n if (current === undefined) return yield* StaleAuthentication.make({});\n\n const assessment = yield* sessionEvidenceDeadline(\n input.evidence,\n current.requirement,\n current.now,\n );\n\n const issued = DateTime.toEpochMillis(input.issuedAt),\n expires = DateTime.toEpochMillis(input.expiresAt),\n absolute = DateTime.toEpochMillis(input.absoluteExpiresAt);\n\n if (\n Math.max(issued, DateTime.toEpochMillis(input.now)) - current.now >\n state.clockPolicy.futureToleranceMillis ||\n input.evidence.proofs.some(\n (proof) => DateTime.toEpochMillis(proof.verifiedAt) > issued,\n ) ||\n issued >= expires ||\n current.now >= expires ||\n expires > absolute\n )\n return yield* StaleAuthentication.make({});\n\n const pendingExpiry =\n input.pending === undefined\n ? undefined\n : yield* consume(input.pending, input.evidence, current.native);\n\n const deadline = Math.min(assessment.validUntil, expires, pendingExpiry ?? Infinity);\n\n yield* state.guardPolicy(current);\n const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${now} >= ${current.now} and ${now} < ${deadline}`;\n\n if (batch !== undefined) {\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));\n yield* registerSqlBatchPostcondition({\n name: \"signed-session-issuance-authority\",\n statement: sqlBatchAssertion(\n sql,\n input.pending === undefined || pending === undefined\n ? condition\n : sql`${condition} and ${pending.consumedCondition(\"Login\", input.pending, current.native)}`,\n ),\n });\n } else if (Option.isSome(external)) {\n yield* registerSqlPostcondition({\n name: \"signed-session-issuance-authority\",\n check: Effect.gen(function* () {\n const final = yield* readEvidence(input.evidence, false);\n\n if (final === undefined || final.now < current.now || final.now >= deadline)\n return yield* StaleAuthentication.make({});\n yield* sessionEvidenceDeadline(input.evidence, final.requirement, final.now);\n if (input.pending !== undefined && pending !== undefined) {\n const rows =\n yield* sql`select 1 where ${pending.consumedCondition(\"Login\", input.pending, current.native)}`;\n\n if (rows.length !== 1) return yield* PendingAuthenticationInvalid.make({});\n }\n }).pipe(\n Effect.provideService(AuthenticationClock, state.clockPolicy),\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n } else {\n // A signed credential has no session INSERT that can enforce the\n // original evidence deadline. Check that deadline at this owner.\n const rows =\n yield* sql`select 1 where ${now} >= ${current.now} and ${now} < ${deadline}`;\n\n if (rows.length !== 1) return yield* StaleAuthentication.make({});\n }\n\n return yield* prepareNativeSession(undefined, project);\n }),\n ),\n };\n\n return { authenticationAuthority };\n});\n"],"mappings":"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"}
@@ -4,12 +4,12 @@ import { NativeSqlTables } from "./native-sql-table.mjs";
4
4
  import { SqlBatchCommit } from "./sql-commit.mjs";
5
5
  import { Effect } from "effect";
6
6
  import { LifecycleHooks } from "@yielded/auth/Hooks";
7
- import { PendingAuthentication } from "@yielded/auth/Sessions";
7
+ import { PendingAuthentication, SessionUnavailable } from "@yielded/auth/Sessions";
8
8
  import { SqlClient } from "effect/sql/SqlClient";
9
9
  //#region src/internal/session-native-login.d.ts
10
10
  export type NativePendingAuthenticationMapping<Claims> = PendingAuthenticationMapping<Claims, AnyTableModel, AnyTableModel, AnyTableModel, unknown>;
11
11
  export declare const makeNativePendingAuthenticationServices: <Claims>(tables: NativeSqlTables, mapping: NativePendingAuthenticationMapping<Claims>) => Effect.Effect<{
12
12
  readonly pendingAuthentication: PendingAuthentication<Claims>;
13
- }, never, LifecycleHooks | SqlBatchCommit | SqlClient>;
13
+ }, SessionUnavailable, LifecycleHooks | SqlBatchCommit | SqlClient>;
14
14
  //#endregion
15
15
  //# sourceMappingURL=session-native-login.d.mts.map
@@ -7,6 +7,7 @@ import { allocateSessionValue } from "./session-policy.mjs";
7
7
  import { makeNativeSessionPendingReader } from "./session-native-pending-read.mjs";
8
8
  import { DateTime, Effect, Option } from "effect";
9
9
  import { PendingAuthenticationInvalid, SessionConflict, StaleAuthentication } from "@yielded/auth/Sessions";
10
+ import { AuthenticationClock } from "@yielded/auth/Operations";
10
11
  //#region src/internal/session-native-login.ts
11
12
  const makeNativePendingAuthenticationServices = Effect.fnUntraced(function* (tables, mapping) {
12
13
  const batch = yield* SqlBatchCommit;
@@ -14,7 +15,7 @@ const makeNativePendingAuthenticationServices = Effect.fnUntraced(function* (tab
14
15
  const executor = yield* makeSqlCommitExecutor(sessionUnavailable);
15
16
  const external = yield* Effect.serviceOption(CurrentSqlCommit);
16
17
  const { sql, now } = state, { p, table } = pending;
17
- const native = (work, mode = "transaction") => batch === void 0 ? executor.operation(normalizeSessionOperation(work), mode) : executor.operationBatch(normalizeSessionOperation(work)).pipe(Effect.provideService(SqlBatchCommit, batch));
18
+ const native = (work, mode = "transaction") => (batch === void 0 ? executor.operation(normalizeSessionOperation(work), mode) : executor.operationBatch(normalizeSessionOperation(work)).pipe(Effect.provideService(SqlBatchCommit, batch))).pipe(Effect.provideService(AuthenticationClock, state.clockPolicy));
18
19
  const verifyPayload = Effect.fnUntraced(function* (stored) {
19
20
  const record = yield* mapping.login.decode(stored.snapshot);
20
21
  sessionInvariant(record.digest === stored.digest && record.version === stored.version && record.evidence.flowId === stored.flowId && record.evidence.bindingDigest === stored.bindingDigest && record.evidence.revision.subjectId === stored.subjectId && DateTime.toEpochMillis(record.expiresAt) === stored.expiresAtMillis && record.attemptLimit === stored.attemptLimit);
@@ -1 +1 @@
1
- {"version":3,"file":"session-native-login.mjs","names":[],"sources":["../../src/internal/session-native-login.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport {\n PendingAuthenticationInvalid,\n StaleAuthentication,\n SessionConflict,\n type PendingAuthentication,\n type PendingAuthenticationRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError, isMappedConstraintConflict } from \"./mapping-error\";\nimport type { PendingAuthenticationMapping } from \"./models/session-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { prepareNativeSession } from \"./session-native-pending\";\nimport { makeNativeSessionPendingReader } from \"./session-native-pending-read\";\nimport {\n normalizeSessionOperation,\n sessionUnavailable,\n sameSessionRevision,\n assessSessionAt,\n sessionInvariant,\n} from \"./session-native-state\";\nimport { allocateSessionValue } from \"./session-policy\";\nimport {\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n SqlBatchCommit,\n registerSqlPostcondition,\n registerSqlBatchPostcondition,\n appendSqlBatchStatement,\n} from \"./sql-commit\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativePendingAuthenticationMapping<Claims> = PendingAuthenticationMapping<\n Claims,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown\n>;\n\nexport const makeNativePendingAuthenticationServices = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativePendingAuthenticationMapping<Claims>,\n): Effect.fn.Return<\n { readonly pendingAuthentication: PendingAuthentication<Claims> },\n never,\n SqlClient | LifecycleHooks | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n\n const {\n state,\n pending,\n read: readPending,\n } = yield* makeNativeSessionPendingReader(tables, mapping, batch !== undefined);\n\n const executor = yield* makeSqlCommitExecutor(sessionUnavailable);\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const { sql, now } = state,\n { p, table } = pending;\n\n const native = <A, E, R>(\n work: Effect.Effect<A, E, R>,\n mode: \"transaction\" | \"statement\" = \"transaction\",\n ) =>\n batch === undefined\n ? executor.operation(normalizeSessionOperation(work), mode)\n : executor\n .operationBatch(normalizeSessionOperation(work))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const verifyPayload = Effect.fnUntraced(function* (\n stored: import(\"./session-native-pending\").StoredSessionPending,\n ) {\n const record = yield* mapping.login.decode(stored.snapshot);\n\n sessionInvariant(\n record.digest === stored.digest &&\n record.version === stored.version &&\n record.evidence.flowId === stored.flowId &&\n record.evidence.bindingDigest === stored.bindingDigest &&\n record.evidence.revision.subjectId === stored.subjectId &&\n DateTime.toEpochMillis(record.expiresAt) === stored.expiresAtMillis &&\n record.attemptLimit === stored.attemptLimit,\n );\n\n return record;\n });\n\n const read = Effect.fnUntraced(function* (\n digest: Parameters<PendingAuthentication<Claims>[\"read\"]>[0][\"digest\"],\n ) {\n const selected = yield* readPending(\"Login\", digest);\n\n if (selected === undefined) return undefined;\n const record = yield* verifyPayload(selected.record);\n\n if (!sameSessionRevision(selected.authority.revision, record.evidence.revision))\n return undefined;\n\n return { record, requirement: selected.authority.requirement };\n });\n\n const pendingAuthentication: PendingAuthentication<Claims> = {\n create: (input, _now, project) =>\n native(\n Effect.gen(function* () {\n const current = yield* state.read(input.evidence.revision.subjectId, true);\n\n if (\n current === undefined ||\n !sameSessionRevision(current.revision, input.evidence.revision)\n )\n return yield* StaleAuthentication.make({});\n yield* assessSessionAt(input.evidence, current.requirement, current.now);\n const expires = DateTime.toEpochMillis(input.expiresAt);\n\n if (expires <= current.now) return yield* StaleAuthentication.make({});\n\n const version = yield* allocateSessionValue(\n \"interactive\",\n mapping.pending.allocateVersion,\n mapping.pending.allocateVersionSync,\n );\n\n const record: PendingAuthenticationRecord<Claims> = { ...input, version };\n const snapshot = yield* mapping.login.encode(record);\n\n yield* state.guardPolicy(current);\n const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${now} >= ${current.now} and ${now} < ${expires}`;\n\n if (batch !== undefined)\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));\n yield* pending\n .insert(\n {\n moduleId: mapping.moduleId,\n kind: \"Login\",\n digest: record.digest,\n version,\n flowId: record.evidence.flowId,\n subjectId: current.native,\n bindingDigest: record.evidence.bindingDigest,\n snapshot,\n expiresAt: record.expiresAt,\n attemptLimit: record.attemptLimit,\n },\n condition,\n )\n .pipe(\n Effect.mapError((error) =>\n isMappedConstraintConflict(mapping.isConstraintConflict, error)\n ? SessionConflict.make({})\n : error,\n ),\n );\n const final = sql`${condition} and exists(select 1 from ${table.name} where ${pending.predicate(table, \"Login\", record.digest)} and ${state.exact(table, p.version, version)} and ${state.exact(table, p.snapshot, snapshot)})`;\n\n if (batch !== undefined)\n yield* registerSqlBatchPostcondition({\n name: \"pending-login-issuance\",\n statement: sqlBatchAssertion(sql, final),\n });\n else if (Option.isSome(external))\n yield* registerSqlPostcondition({\n name: \"pending-login-issuance\",\n check: Effect.gen(function* () {\n const rows = yield* sql`select 1 where ${final}`;\n\n sessionInvariant(rows.length === 1);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepareNativeSession(record, project);\n }),\n ),\n read: ({ digest }) =>\n executor\n .read(read(digest))\n .pipe(\n Effect.flatMap((result) =>\n result === undefined\n ? Effect.fail(PendingAuthenticationInvalid.make({}))\n : Effect.succeed(result),\n ),\n ),\n reject: ({ digest }, project) =>\n native(\n Effect.gen(function* () {\n yield* pending.reject(\"Login\", digest);\n\n return yield* prepareNativeSession({ _tag: \"Rejected\" } as const, project);\n }),\n \"statement\",\n ),\n };\n\n return { pendingAuthentication };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-native-login.mjs","names":[],"sources":["../../src/internal/session-native-login.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { AuthenticationClock } from \"@yielded/auth/Operations\";\nimport {\n PendingAuthenticationInvalid,\n StaleAuthentication,\n SessionConflict,\n type PendingAuthentication,\n type PendingAuthenticationRecord,\n type SessionUnavailable,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError, isMappedConstraintConflict } from \"./mapping-error\";\nimport type { PendingAuthenticationMapping } from \"./models/session-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { prepareNativeSession } from \"./session-native-pending\";\nimport { makeNativeSessionPendingReader } from \"./session-native-pending-read\";\nimport {\n normalizeSessionOperation,\n sessionUnavailable,\n sameSessionRevision,\n assessSessionAt,\n sessionInvariant,\n} from \"./session-native-state\";\nimport { allocateSessionValue } from \"./session-policy\";\nimport {\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n SqlBatchCommit,\n registerSqlPostcondition,\n registerSqlBatchPostcondition,\n appendSqlBatchStatement,\n} from \"./sql-commit\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativePendingAuthenticationMapping<Claims> = PendingAuthenticationMapping<\n Claims,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown\n>;\n\nexport const makeNativePendingAuthenticationServices = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativePendingAuthenticationMapping<Claims>,\n): Effect.fn.Return<\n { readonly pendingAuthentication: PendingAuthentication<Claims> },\n SessionUnavailable,\n SqlClient | LifecycleHooks | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n\n const {\n state,\n pending,\n read: readPending,\n } = yield* makeNativeSessionPendingReader(tables, mapping, batch !== undefined);\n\n const executor = yield* makeSqlCommitExecutor(sessionUnavailable);\n const external = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const { sql, now } = state,\n { p, table } = pending;\n\n const native = <A, E, R>(\n work: Effect.Effect<A, E, R>,\n mode: \"transaction\" | \"statement\" = \"transaction\",\n ) =>\n (batch === undefined\n ? executor.operation(normalizeSessionOperation(work), mode)\n : executor\n .operationBatch(normalizeSessionOperation(work))\n .pipe(Effect.provideService(SqlBatchCommit, batch))\n ).pipe(Effect.provideService(AuthenticationClock, state.clockPolicy));\n\n const verifyPayload = Effect.fnUntraced(function* (\n stored: import(\"./session-native-pending\").StoredSessionPending,\n ) {\n const record = yield* mapping.login.decode(stored.snapshot);\n\n sessionInvariant(\n record.digest === stored.digest &&\n record.version === stored.version &&\n record.evidence.flowId === stored.flowId &&\n record.evidence.bindingDigest === stored.bindingDigest &&\n record.evidence.revision.subjectId === stored.subjectId &&\n DateTime.toEpochMillis(record.expiresAt) === stored.expiresAtMillis &&\n record.attemptLimit === stored.attemptLimit,\n );\n\n return record;\n });\n\n const read = Effect.fnUntraced(function* (\n digest: Parameters<PendingAuthentication<Claims>[\"read\"]>[0][\"digest\"],\n ) {\n const selected = yield* readPending(\"Login\", digest);\n\n if (selected === undefined) return undefined;\n const record = yield* verifyPayload(selected.record);\n\n if (!sameSessionRevision(selected.authority.revision, record.evidence.revision))\n return undefined;\n\n return { record, requirement: selected.authority.requirement };\n });\n\n const pendingAuthentication: PendingAuthentication<Claims> = {\n create: (input, _now, project) =>\n native(\n Effect.gen(function* () {\n const current = yield* state.read(input.evidence.revision.subjectId, true);\n\n if (\n current === undefined ||\n !sameSessionRevision(current.revision, input.evidence.revision)\n )\n return yield* StaleAuthentication.make({});\n yield* assessSessionAt(input.evidence, current.requirement, current.now);\n const expires = DateTime.toEpochMillis(input.expiresAt);\n\n if (expires <= current.now) return yield* StaleAuthentication.make({});\n\n const version = yield* allocateSessionValue(\n \"interactive\",\n mapping.pending.allocateVersion,\n mapping.pending.allocateVersionSync,\n );\n\n const record: PendingAuthenticationRecord<Claims> = { ...input, version };\n const snapshot = yield* mapping.login.encode(record);\n\n yield* state.guardPolicy(current);\n const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${now} >= ${current.now} and ${now} < ${expires}`;\n\n if (batch !== undefined)\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));\n yield* pending\n .insert(\n {\n moduleId: mapping.moduleId,\n kind: \"Login\",\n digest: record.digest,\n version,\n flowId: record.evidence.flowId,\n subjectId: current.native,\n bindingDigest: record.evidence.bindingDigest,\n snapshot,\n expiresAt: record.expiresAt,\n attemptLimit: record.attemptLimit,\n },\n condition,\n )\n .pipe(\n Effect.mapError((error) =>\n isMappedConstraintConflict(mapping.isConstraintConflict, error)\n ? SessionConflict.make({})\n : error,\n ),\n );\n const final = sql`${condition} and exists(select 1 from ${table.name} where ${pending.predicate(table, \"Login\", record.digest)} and ${state.exact(table, p.version, version)} and ${state.exact(table, p.snapshot, snapshot)})`;\n\n if (batch !== undefined)\n yield* registerSqlBatchPostcondition({\n name: \"pending-login-issuance\",\n statement: sqlBatchAssertion(sql, final),\n });\n else if (Option.isSome(external))\n yield* registerSqlPostcondition({\n name: \"pending-login-issuance\",\n check: Effect.gen(function* () {\n const rows = yield* sql`select 1 where ${final}`;\n\n sessionInvariant(rows.length === 1);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepareNativeSession(record, project);\n }),\n ),\n read: ({ digest }) =>\n executor\n .read(read(digest))\n .pipe(\n Effect.flatMap((result) =>\n result === undefined\n ? Effect.fail(PendingAuthenticationInvalid.make({}))\n : Effect.succeed(result),\n ),\n ),\n reject: ({ digest }, project) =>\n native(\n Effect.gen(function* () {\n yield* pending.reject(\"Login\", digest);\n\n return yield* prepareNativeSession({ _tag: \"Rejected\" } as const, project);\n }),\n \"statement\",\n ),\n };\n\n return { pendingAuthentication };\n});\n"],"mappings":"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"}
@@ -1,36 +1,42 @@
1
1
  import { exactSqlText } from "./sql-change.mjs";
2
- import { makeNativeSessionAuthorityState } from "./session-native-state.mjs";
2
+ import { makeNativeSessionAuthorityState, sessionInvariant, sessionUnavailable } from "./session-native-state.mjs";
3
+ import { canJoinTextColumns } from "./storage-validation.mjs";
3
4
  import { makeNativeSessionPending } from "./session-native-pending.mjs";
4
5
  import { Effect } from "effect";
5
6
  //#region src/internal/session-native-pending-read.ts
6
- /** Both semantic pending ports use the same kind-bound physical snapshot.
7
- * Distinct native ID encodings use their own bound reads; joined rows still
8
- * validate each decoded owner before returning current policy. */
7
+ /** Both ports re-read the pending row with current authority in one snapshot.
8
+ * Discovery only selects the independently bound owner for incompatible IDs. */
9
9
  const makeNativeSessionPendingReader = Effect.fnUntraced(function* (tables, mapping, batch = false) {
10
10
  const state = yield* makeNativeSessionAuthorityState(tables, mapping, batch);
11
11
  const pending = yield* makeNativeSessionPending(tables, mapping, batch);
12
+ const joinedIds = yield* canJoinTextColumns([
13
+ pending.table.unencodedTextColumn?.(pending.p.subjectId),
14
+ state.subject.unencodedTextColumn?.(mapping.subject.id),
15
+ state.credential.unencodedTextColumn?.(mapping.credential.subjectId)
16
+ ]).pipe(Effect.mapError(sessionUnavailable));
12
17
  return {
13
18
  state,
14
19
  pending,
15
20
  read: Effect.fnUntraced(function* (kind, digest) {
16
21
  const { sql, now } = state, { p, table } = pending;
17
- if (table.unencodedTextColumn?.(p.subjectId) !== void 0 && state.subject.unencodedTextColumn?.(mapping.subject.id) !== void 0 && state.credential.unencodedTextColumn?.(mapping.credential.subjectId) !== void 0) {
18
- const pt = table.as("authentication_pending"), st = state.joinedSubject, ct = state.joinedCredential, s = mapping.subject, c = mapping.credential;
19
- const rows = yield* sql`select ${pt.fields("p_")}, ${st.fields("s_")}, ${ct.fields("c_")}, ${now} as engine_now from ${pt.name} join ${st.name} on ${exactSqlText(sql, st.column(s.id), pt.column(p.subjectId))} and ${state.activeSubject(st)} left join ${ct.name} on ${exactSqlText(sql, ct.column(c.subjectId), pt.column(p.subjectId))} and ${state.activeCredential(ct)} where ${pending.predicate(pt, kind, digest)} limit 65`;
20
- if (rows[0] === void 0) return void 0;
21
- const record = yield* pending.decode(kind, pt.decode(rows[0], "p_"));
22
- const owner = yield* mapping.subjectId.toNative(record.subjectId);
23
- const authority = yield* state.decode(record.subjectId, owner, rows[0], rows.map((row) => ct.decode(row, "c_")), rows[0].engine_now);
24
- return authority === void 0 ? void 0 : {
25
- record,
26
- authority
27
- };
22
+ let discoveredSubjectId;
23
+ let nativeId;
24
+ if (!joinedIds) {
25
+ const selected = yield* pending.read(kind, digest);
26
+ if (selected === void 0) return void 0;
27
+ discoveredSubjectId = selected.record.subjectId;
28
+ nativeId = yield* mapping.subjectId.toNative(discoveredSubjectId);
28
29
  }
29
- const selected = yield* pending.read(kind, digest);
30
- if (selected === void 0) return void 0;
31
- const authority = yield* state.read(selected.record.subjectId);
32
- return authority === void 0 || authority.now >= selected.record.expiresAtMillis ? void 0 : {
33
- record: selected.record,
30
+ const pt = table.as("authentication_pending"), st = state.joinedSubject, ct = state.joinedCredential, s = mapping.subject, c = mapping.credential;
31
+ const bindOwner = (table, key) => joinedIds ? exactSqlText(sql, table.column(key), pt.column(p.subjectId)) : state.id(table, key, nativeId);
32
+ const rows = yield* sql`select ${pt.fields("p_")}, ${st.fields("s_")}, ${ct.fields("c_")}, ${now} as engine_now from ${pt.name} join ${st.name} on ${bindOwner(st, s.id)} and ${state.activeSubject(st)} left join ${ct.name} on ${bindOwner(ct, c.subjectId)} and ${state.activeCredential(ct)} where ${pending.predicate(pt, kind, digest)}${joinedIds ? sql`` : sql` and ${bindOwner(pt, p.subjectId)}`} limit 65`;
33
+ if (rows[0] === void 0) return void 0;
34
+ const record = yield* pending.decode(kind, pt.decode(rows[0], "p_"));
35
+ sessionInvariant(joinedIds || record.subjectId === discoveredSubjectId);
36
+ const owner = yield* mapping.subjectId.toNative(record.subjectId);
37
+ const authority = yield* state.decode(record.subjectId, owner, rows[0], rows.map((row) => ct.decode(row, "c_")), rows[0].engine_now);
38
+ return authority === void 0 || authority.now >= record.expiresAtMillis ? void 0 : {
39
+ record,
34
40
  authority
35
41
  };
36
42
  })
@@ -1 +1 @@
1
- {"version":3,"file":"session-native-pending-read.mjs","names":[],"sources":["../../src/internal/session-native-pending-read.ts"],"sourcesContent":["import type { TokenDigest } from \"@yielded/auth/Schema\";\nimport type { PendingAuthenticationKind } from \"@yielded/auth/Sessions\";\nimport { Effect } from \"effect\";\n\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativeSessionPending } from \"./session-native-pending\";\nimport {\n makeNativeSessionAuthorityState,\n type NativeSessionAuthorityMapping,\n} from \"./session-native-state\";\nimport { exactSqlText } from \"./sql-change\";\n\n/** Both semantic pending ports use the same kind-bound physical snapshot.\n * Distinct native ID encodings use their own bound reads; joined rows still\n * validate each decoded owner before returning current policy. */\nexport const makeNativeSessionPendingReader = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: NativeSessionAuthorityMapping & Parameters<typeof makeNativeSessionPending>[1],\n batch = false,\n) {\n const state = yield* makeNativeSessionAuthorityState(tables, mapping, batch);\n const pending = yield* makeNativeSessionPending(tables, mapping, batch);\n\n const read = Effect.fnUntraced(function* (kind: PendingAuthenticationKind, digest: TokenDigest) {\n const { sql, now } = state,\n { p, table } = pending;\n\n const compatible =\n table.unencodedTextColumn?.(p.subjectId) !== undefined &&\n state.subject.unencodedTextColumn?.(mapping.subject.id) !== undefined &&\n state.credential.unencodedTextColumn?.(mapping.credential.subjectId) !== undefined;\n\n if (compatible) {\n const pt = table.as(\"authentication_pending\"),\n st = state.joinedSubject,\n ct = state.joinedCredential,\n s = mapping.subject,\n c = mapping.credential;\n\n const rows =\n yield* sql`select ${pt.fields(\"p_\")}, ${st.fields(\"s_\")}, ${ct.fields(\"c_\")}, ${now} as engine_now from ${pt.name} join ${st.name} on ${exactSqlText(sql, st.column(s.id), pt.column(p.subjectId))} and ${state.activeSubject(st)} left join ${ct.name} on ${exactSqlText(sql, ct.column(c.subjectId), pt.column(p.subjectId))} and ${state.activeCredential(ct)} where ${pending.predicate(pt, kind, digest)} limit 65`;\n\n if (rows[0] === undefined) return undefined;\n const record = yield* pending.decode(kind, pt.decode(rows[0], \"p_\"));\n const owner = yield* mapping.subjectId.toNative(record.subjectId);\n\n const authority = yield* state.decode(\n record.subjectId,\n owner,\n rows[0],\n rows.map((row) => ct.decode(row, \"c_\")),\n rows[0].engine_now,\n );\n\n return authority === undefined ? undefined : { record, authority };\n }\n const selected = yield* pending.read(kind, digest);\n\n if (selected === undefined) return undefined;\n const authority = yield* state.read(selected.record.subjectId);\n\n return authority === undefined || authority.now >= selected.record.expiresAtMillis\n ? undefined\n : { record: selected.record, authority };\n });\n\n return { state, pending, read };\n});\n"],"mappings":";;;;;;;;AAeA,MAAa,iCAAiC,OAAO,WAAW,WAC9D,QACA,SACA,QAAQ,OACR;CACA,MAAM,QAAQ,OAAO,gCAAgC,QAAQ,SAAS,KAAK;CAC3E,MAAM,UAAU,OAAO,yBAAyB,QAAQ,SAAS,KAAK;CA6CtE,OAAO;EAAE;EAAO;EAAS,MA3CZ,OAAO,WAAW,WAAW,MAAiC,QAAqB;GAC9F,MAAM,EAAE,KAAK,QAAQ,OACnB,EAAE,GAAG,UAAU;GAOjB,IAJE,MAAM,sBAAsB,EAAE,SAAS,MAAM,KAAA,KAC7C,MAAM,QAAQ,sBAAsB,QAAQ,QAAQ,EAAE,MAAM,KAAA,KAC5D,MAAM,WAAW,sBAAsB,QAAQ,WAAW,SAAS,MAAM,KAAA,GAE3D;IACd,MAAM,KAAK,MAAM,GAAG,wBAAwB,GAC1C,KAAK,MAAM,eACX,KAAK,MAAM,kBACX,IAAI,QAAQ,SACZ,IAAI,QAAQ;IAEd,MAAM,OACJ,OAAO,GAAG,UAAU,GAAG,OAAO,IAAI,EAAE,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,IAAI,sBAAsB,GAAG,KAAK,QAAQ,GAAG,KAAK,MAAM,aAAa,KAAK,GAAG,OAAO,EAAE,EAAE,GAAG,GAAG,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,MAAM,cAAc,EAAE,EAAE,aAAa,GAAG,KAAK,MAAM,aAAa,KAAK,GAAG,OAAO,EAAE,SAAS,GAAG,GAAG,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,MAAM,iBAAiB,EAAE,EAAE,SAAS,QAAQ,UAAU,IAAI,MAAM,MAAM,EAAE;IAEhZ,IAAI,KAAK,OAAO,KAAA,GAAW,OAAO,KAAA;IAClC,MAAM,SAAS,OAAO,QAAQ,OAAO,MAAM,GAAG,OAAO,KAAK,IAAI,IAAI,CAAC;IACnE,MAAM,QAAQ,OAAO,QAAQ,UAAU,SAAS,OAAO,SAAS;IAEhE,MAAM,YAAY,OAAO,MAAM,OAC7B,OAAO,WACP,OACA,KAAK,IACL,KAAK,KAAK,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAC,GACtC,KAAK,EAAE,CAAC,UACV;IAEA,OAAO,cAAc,KAAA,IAAY,KAAA,IAAY;KAAE;KAAQ;IAAU;GACnE;GACA,MAAM,WAAW,OAAO,QAAQ,KAAK,MAAM,MAAM;GAEjD,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;GACnC,MAAM,YAAY,OAAO,MAAM,KAAK,SAAS,OAAO,SAAS;GAE7D,OAAO,cAAc,KAAA,KAAa,UAAU,OAAO,SAAS,OAAO,kBAC/D,KAAA,IACA;IAAE,QAAQ,SAAS;IAAQ;GAAU;EAC3C,CAE4B;CAAE;AAChC,CAAC"}
1
+ {"version":3,"file":"session-native-pending-read.mjs","names":[],"sources":["../../src/internal/session-native-pending-read.ts"],"sourcesContent":["import type { SubjectId, TokenDigest } from \"@yielded/auth/Schema\";\nimport type { PendingAuthenticationKind } from \"@yielded/auth/Sessions\";\nimport { Effect } from \"effect\";\n\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativeSessionPending } from \"./session-native-pending\";\nimport {\n makeNativeSessionAuthorityState,\n sessionInvariant,\n sessionUnavailable,\n type NativeSessionAuthorityMapping,\n} from \"./session-native-state\";\nimport { exactSqlText } from \"./sql-change\";\nimport { canJoinTextColumns } from \"./storage-validation\";\n\n/** Both ports re-read the pending row with current authority in one snapshot.\n * Discovery only selects the independently bound owner for incompatible IDs. */\nexport const makeNativeSessionPendingReader = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: NativeSessionAuthorityMapping & Parameters<typeof makeNativeSessionPending>[1],\n batch = false,\n) {\n const state = yield* makeNativeSessionAuthorityState(tables, mapping, batch);\n const pending = yield* makeNativeSessionPending(tables, mapping, batch);\n\n const joinedIds = yield* canJoinTextColumns([\n pending.table.unencodedTextColumn?.(pending.p.subjectId),\n state.subject.unencodedTextColumn?.(mapping.subject.id),\n state.credential.unencodedTextColumn?.(mapping.credential.subjectId),\n ]).pipe(Effect.mapError(sessionUnavailable));\n\n const read = Effect.fnUntraced(function* (kind: PendingAuthenticationKind, digest: TokenDigest) {\n const { sql, now } = state,\n { p, table } = pending;\n\n let discoveredSubjectId: SubjectId | undefined;\n let nativeId: unknown;\n\n if (!joinedIds) {\n const selected = yield* pending.read(kind, digest);\n\n if (selected === undefined) return undefined;\n discoveredSubjectId = selected.record.subjectId;\n nativeId = yield* mapping.subjectId.toNative(discoveredSubjectId);\n }\n\n const pt = table.as(\"authentication_pending\"),\n st = state.joinedSubject,\n ct = state.joinedCredential,\n s = mapping.subject,\n c = mapping.credential;\n\n const bindOwner = (table: typeof pt, key: string) =>\n joinedIds\n ? exactSqlText(sql, table.column(key), pt.column(p.subjectId))\n : state.id(table, key, nativeId);\n\n const rows =\n yield* sql`select ${pt.fields(\"p_\")}, ${st.fields(\"s_\")}, ${ct.fields(\"c_\")}, ${now} as engine_now from ${pt.name} join ${st.name} on ${bindOwner(st, s.id)} and ${state.activeSubject(st)} left join ${ct.name} on ${bindOwner(ct, c.subjectId)} and ${state.activeCredential(ct)} where ${pending.predicate(pt, kind, digest)}${joinedIds ? sql`` : sql` and ${bindOwner(pt, p.subjectId)}`} limit 65`;\n\n if (rows[0] === undefined) return undefined;\n const record = yield* pending.decode(kind, pt.decode(rows[0], \"p_\"));\n\n sessionInvariant(joinedIds || record.subjectId === discoveredSubjectId);\n const owner = yield* mapping.subjectId.toNative(record.subjectId);\n\n const authority = yield* state.decode(\n record.subjectId,\n owner,\n rows[0],\n rows.map((row) => ct.decode(row, \"c_\")),\n rows[0].engine_now,\n );\n\n return authority === undefined || authority.now >= record.expiresAtMillis\n ? undefined\n : { record, authority };\n });\n\n return { state, pending, read };\n});\n"],"mappings":";;;;;;;;AAiBA,MAAa,iCAAiC,OAAO,WAAW,WAC9D,QACA,SACA,QAAQ,OACR;CACA,MAAM,QAAQ,OAAO,gCAAgC,QAAQ,SAAS,KAAK;CAC3E,MAAM,UAAU,OAAO,yBAAyB,QAAQ,SAAS,KAAK;CAEtE,MAAM,YAAY,OAAO,mBAAmB;EAC1C,QAAQ,MAAM,sBAAsB,QAAQ,EAAE,SAAS;EACvD,MAAM,QAAQ,sBAAsB,QAAQ,QAAQ,EAAE;EACtD,MAAM,WAAW,sBAAsB,QAAQ,WAAW,SAAS;CACrE,CAAC,CAAC,CAAC,KAAK,OAAO,SAAS,kBAAkB,CAAC;CAkD3C,OAAO;EAAE;EAAO;EAAS,MAhDZ,OAAO,WAAW,WAAW,MAAiC,QAAqB;GAC9F,MAAM,EAAE,KAAK,QAAQ,OACnB,EAAE,GAAG,UAAU;GAEjB,IAAI;GACJ,IAAI;GAEJ,IAAI,CAAC,WAAW;IACd,MAAM,WAAW,OAAO,QAAQ,KAAK,MAAM,MAAM;IAEjD,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;IACnC,sBAAsB,SAAS,OAAO;IACtC,WAAW,OAAO,QAAQ,UAAU,SAAS,mBAAmB;GAClE;GAEA,MAAM,KAAK,MAAM,GAAG,wBAAwB,GAC1C,KAAK,MAAM,eACX,KAAK,MAAM,kBACX,IAAI,QAAQ,SACZ,IAAI,QAAQ;GAEd,MAAM,aAAa,OAAkB,QACnC,YACI,aAAa,KAAK,MAAM,OAAO,GAAG,GAAG,GAAG,OAAO,EAAE,SAAS,CAAC,IAC3D,MAAM,GAAG,OAAO,KAAK,QAAQ;GAEnC,MAAM,OACJ,OAAO,GAAG,UAAU,GAAG,OAAO,IAAI,EAAE,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,IAAI,sBAAsB,GAAG,KAAK,QAAQ,GAAG,KAAK,MAAM,UAAU,IAAI,EAAE,EAAE,EAAE,OAAO,MAAM,cAAc,EAAE,EAAE,aAAa,GAAG,KAAK,MAAM,UAAU,IAAI,EAAE,SAAS,EAAE,OAAO,MAAM,iBAAiB,EAAE,EAAE,SAAS,QAAQ,UAAU,IAAI,MAAM,MAAM,IAAI,YAAY,GAAG,KAAK,GAAG,QAAQ,UAAU,IAAI,EAAE,SAAS,IAAI;GAEhY,IAAI,KAAK,OAAO,KAAA,GAAW,OAAO,KAAA;GAClC,MAAM,SAAS,OAAO,QAAQ,OAAO,MAAM,GAAG,OAAO,KAAK,IAAI,IAAI,CAAC;GAEnE,iBAAiB,aAAa,OAAO,cAAc,mBAAmB;GACtE,MAAM,QAAQ,OAAO,QAAQ,UAAU,SAAS,OAAO,SAAS;GAEhE,MAAM,YAAY,OAAO,MAAM,OAC7B,OAAO,WACP,OACA,KAAK,IACL,KAAK,KAAK,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAC,GACtC,KAAK,EAAE,CAAC,UACV;GAEA,OAAO,cAAc,KAAA,KAAa,UAAU,OAAO,OAAO,kBACtD,KAAA,IACA;IAAE;IAAQ;GAAU;EAC1B,CAE4B;CAAE;AAChC,CAAC"}
@@ -3,6 +3,7 @@ import { sessionInvariant } from "./session-native-state.mjs";
3
3
  import { DateTime, Effect, Schema } from "effect";
4
4
  import { SecurityRevision, SessionAuthenticationProvenance, SessionCredentialVersion, SessionId, SessionMetadata } from "@yielded/auth/Sessions";
5
5
  import { SqlClient } from "effect/sql/SqlClient";
6
+ import { AuthenticationClock } from "@yielded/auth/Operations";
6
7
  import { TokenDigest } from "@yielded/auth/Schema";
7
8
  //#region src/internal/session-native-record.ts
8
9
  const metadataJson = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));
@@ -16,16 +17,17 @@ const makeConditionalSqlInsert = Effect.fnUntraced(function* () {
16
17
  return sql`insert into ${table.name} (${sql.join(", ", false)(fields.map(([key]) => table.columnName(key)))}) select ${sql.join(", ", false)(fields.map(([key, value]) => table.value(key, value)))} where ${condition}`;
17
18
  };
18
19
  });
19
- /** Security columns identify the row and own its live time bounds. In particular
20
- * issuedAt may be assigned by UPDATE's database clock without rewriting a JSON
21
- * copy of prepared metadata. Claims/provenance decoding remains consumer-owned. */
20
+ /** Security columns own live time bounds and commit-time issuedAt adjustments,
21
+ * independently of prepared JSON metadata. Claims/provenance decoding remains consumer-owned. */
22
22
  const makeNativeSessionRecords = Effect.fnUntraced(function* (tables, mapping) {
23
23
  const sql = (yield* SqlClient).withoutTransforms();
24
+ const { futureToleranceMillis } = yield* AuthenticationClock;
24
25
  const s = mapping.session, table = tables(s.table), now = tables.expression(mapping.clock.engineNowMillis);
25
26
  const exact = (t, key, value) => exactSqlText(sql, t.column(key), t.value(key, value));
26
27
  const millis = (t, key) => tables.expression(mapping.clock.toMillis(t.column(key)));
27
28
  const live = (t) => sql.and([
28
- sql`${millis(t, s.issuedAt)} <= ${now}`,
29
+ sql`${millis(t, s.issuedAt)} <= ${now} + ${futureToleranceMillis}`,
30
+ sql`${millis(t, s.issuedAt)} < ${millis(t, s.expiresAt)}`,
29
31
  sql`${millis(t, s.expiresAt)} > ${now}`,
30
32
  sql`${millis(t, s.absoluteExpiresAt)} >= ${millis(t, s.expiresAt)}`
31
33
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"session-native-record.mjs","names":[],"sources":["../../src/internal/session-native-record.ts"],"sourcesContent":["import { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n SecurityRevision,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport type { SubjectIdCodec } from \"./models/common\";\nimport type { StatefulSessionTables, SessionExecution } from \"./models/session-model\";\nimport type { NativeSqlTables, SqlTable } from \"./native-sql-table\";\nimport { sessionInvariant } from \"./session-native-state\";\nimport { exactSqlText } from \"./sql-change\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativeSessionRecordMapping<Claims> = StatefulSessionTables<\n Claims,\n AnyTableModel,\n unknown,\n unknown\n> &\n Pick<SessionExecution, \"clock\"> & { readonly subjectId: SubjectIdCodec<unknown> };\n\nconst metadataJson = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));\nconst provenanceJson = Schema.encodeSync(Schema.fromJsonString(SessionAuthenticationProvenance));\n\nexport const sameSessionRecord = <Claims>(\n left: StatefulSessionRecord<Claims>,\n right: StatefulSessionRecord<Claims>,\n) =>\n left.digest === right.digest &&\n left.credentialVersion === right.credentialVersion &&\n metadataJson(left) === metadataJson(right) &&\n provenanceJson(left.provenance) === provenanceJson(right.provenance);\n\nexport const makeConditionalSqlInsert = Effect.fnUntraced(function* () {\n const sql = (yield* SqlClient).withoutTransforms();\n\n return (table: SqlTable, values: Readonly<Record<string, unknown>>, condition: Fragment) => {\n const fields = Object.entries(values).filter(([, value]) => value !== undefined);\n\n sessionInvariant(fields.length > 0);\n\n return sql`insert into ${table.name} (${sql.join(\", \", false)(fields.map(([key]) => table.columnName(key)))}) select ${sql.join(\", \", false)(fields.map(([key, value]) => table.value(key, value)))} where ${condition}`;\n };\n});\n\n/** Security columns identify the row and own its live time bounds. In particular\n * issuedAt may be assigned by UPDATE's database clock without rewriting a JSON\n * copy of prepared metadata. Claims/provenance decoding remains consumer-owned. */\nexport const makeNativeSessionRecords = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativeSessionRecordMapping<Claims>,\n) {\n const sql = (yield* SqlClient).withoutTransforms();\n\n const s = mapping.session,\n table = tables(s.table),\n now = tables.expression(mapping.clock.engineNowMillis);\n\n const exact = (t: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, t.column(key), t.value(key, value));\n\n const millis = (t: SqlTable, key: string) =>\n tables.expression(mapping.clock.toMillis(t.column(key)));\n\n const live = (t: SqlTable) =>\n sql.and([\n sql`${millis(t, s.issuedAt)} <= ${now}`,\n sql`${millis(t, s.expiresAt)} > ${now}`,\n sql`${millis(t, s.absoluteExpiresAt)} >= ${millis(t, s.expiresAt)}`,\n ]);\n\n const decode = Effect.fnUntraced(function* (row: Record<string, unknown>) {\n const decoded = yield* s.decode(row);\n const subjectId = yield* mapping.subjectId.toSubject(row[s.subjectId]);\n const sessionId = yield* mapping.sessionId.toSession(row[s.sessionId]);\n const digest = yield* Schema.decodeUnknownEffect(TokenDigest)(row[s.digest]);\n const revision = yield* Schema.decodeUnknownEffect(SecurityRevision)(row[s.securityRevision]);\n const issuedAt = DateTime.makeUnsafe(mapping.clock.decodeInstant(row[s.issuedAt]));\n const expiresAt = mapping.clock.decodeInstant(row[s.expiresAt]);\n const absolute = mapping.clock.decodeInstant(row[s.absoluteExpiresAt]);\n\n sessionInvariant(\n decoded.subjectId === subjectId &&\n decoded.sessionId === sessionId &&\n decoded.digest === digest &&\n decoded.securityRevision === revision &&\n DateTime.toEpochMillis(decoded.expiresAt) === expiresAt &&\n DateTime.toEpochMillis(decoded.absoluteExpiresAt) === absolute,\n );\n yield* Schema.decodeEffect(SessionId)(sessionId);\n yield* Schema.decodeEffect(SessionCredentialVersion)(decoded.credentialVersion);\n\n return { ...decoded, issuedAt };\n });\n\n const owner = Effect.fnUntraced(function* (\n record: Pick<StatefulSessionRecord<Claims>, \"subjectId\" | \"sessionId\" | \"digest\">,\n ) {\n const nativeSubject = yield* mapping.subjectId.toNative(record.subjectId),\n nativeSession = yield* mapping.sessionId.toNative(record.sessionId);\n\n return sql.and([\n sql`${table.column(s.subjectId)} = ${table.value(s.subjectId, nativeSubject)}`,\n sql`${table.column(s.sessionId)} = ${table.value(s.sessionId, nativeSession)}`,\n exact(table, s.digest, record.digest),\n ]);\n });\n\n // These are the semantic values supplied by the session encoder for this\n // write, not an observed whole-row snapshot or unrelated application columns.\n const encodedCondition = (values: Readonly<Record<string, unknown>>) =>\n sql.and(\n Object.entries(values)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) =>\n value === null\n ? sql`${table.column(key)} is null`\n : typeof value === \"string\"\n ? exact(table, key, value)\n : sql`${table.column(key)} = ${table.value(key, value)}`,\n ),\n );\n\n const read = Effect.fnUntraced(function* (digest: TokenDigest) {\n const rows =\n yield* sql`select ${table.fields(\"session_\")} from ${table.name} where ${exact(table, s.digest, digest)} and ${live(table)} limit 2`;\n\n sessionInvariant(rows.length <= 1);\n\n return rows[0] === undefined ? undefined : yield* decode(table.decode(rows[0], \"session_\"));\n });\n\n return { sql, s, table, now, exact, millis, live, decode, owner, read, encodedCondition };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-native-record.mjs","names":[],"sources":["../../src/internal/session-native-record.ts"],"sourcesContent":["import { AuthenticationClock } from \"@yielded/auth/Operations\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n SecurityRevision,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport type { SubjectIdCodec } from \"./models/common\";\nimport type { StatefulSessionTables, SessionExecution } from \"./models/session-model\";\nimport type { NativeSqlTables, SqlTable } from \"./native-sql-table\";\nimport { sessionInvariant } from \"./session-native-state\";\nimport { exactSqlText } from \"./sql-change\";\nimport type { AnyTableModel } from \"./table-model\";\n\nexport type NativeSessionRecordMapping<Claims> = StatefulSessionTables<\n Claims,\n AnyTableModel,\n unknown,\n unknown\n> &\n Pick<SessionExecution, \"clock\"> & { readonly subjectId: SubjectIdCodec<unknown> };\n\nconst metadataJson = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));\nconst provenanceJson = Schema.encodeSync(Schema.fromJsonString(SessionAuthenticationProvenance));\n\nexport const sameSessionRecord = <Claims>(\n left: StatefulSessionRecord<Claims>,\n right: StatefulSessionRecord<Claims>,\n) =>\n left.digest === right.digest &&\n left.credentialVersion === right.credentialVersion &&\n metadataJson(left) === metadataJson(right) &&\n provenanceJson(left.provenance) === provenanceJson(right.provenance);\n\nexport const makeConditionalSqlInsert = Effect.fnUntraced(function* () {\n const sql = (yield* SqlClient).withoutTransforms();\n\n return (table: SqlTable, values: Readonly<Record<string, unknown>>, condition: Fragment) => {\n const fields = Object.entries(values).filter(([, value]) => value !== undefined);\n\n sessionInvariant(fields.length > 0);\n\n return sql`insert into ${table.name} (${sql.join(\", \", false)(fields.map(([key]) => table.columnName(key)))}) select ${sql.join(\", \", false)(fields.map(([key, value]) => table.value(key, value)))} where ${condition}`;\n };\n});\n\n/** Security columns own live time bounds and commit-time issuedAt adjustments,\n * independently of prepared JSON metadata. Claims/provenance decoding remains consumer-owned. */\nexport const makeNativeSessionRecords = Effect.fnUntraced(function* <Claims>(\n tables: NativeSqlTables,\n mapping: NativeSessionRecordMapping<Claims>,\n) {\n const sql = (yield* SqlClient).withoutTransforms();\n const { futureToleranceMillis } = yield* AuthenticationClock;\n\n const s = mapping.session,\n table = tables(s.table),\n now = tables.expression(mapping.clock.engineNowMillis);\n\n const exact = (t: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, t.column(key), t.value(key, value));\n\n const millis = (t: SqlTable, key: string) =>\n tables.expression(mapping.clock.toMillis(t.column(key)));\n\n const live = (t: SqlTable) =>\n sql.and([\n sql`${millis(t, s.issuedAt)} <= ${now} + ${futureToleranceMillis}`,\n sql`${millis(t, s.issuedAt)} < ${millis(t, s.expiresAt)}`,\n sql`${millis(t, s.expiresAt)} > ${now}`,\n sql`${millis(t, s.absoluteExpiresAt)} >= ${millis(t, s.expiresAt)}`,\n ]);\n\n const decode = Effect.fnUntraced(function* (row: Record<string, unknown>) {\n const decoded = yield* s.decode(row);\n const subjectId = yield* mapping.subjectId.toSubject(row[s.subjectId]);\n const sessionId = yield* mapping.sessionId.toSession(row[s.sessionId]);\n const digest = yield* Schema.decodeUnknownEffect(TokenDigest)(row[s.digest]);\n const revision = yield* Schema.decodeUnknownEffect(SecurityRevision)(row[s.securityRevision]);\n const issuedAt = DateTime.makeUnsafe(mapping.clock.decodeInstant(row[s.issuedAt]));\n const expiresAt = mapping.clock.decodeInstant(row[s.expiresAt]);\n const absolute = mapping.clock.decodeInstant(row[s.absoluteExpiresAt]);\n\n sessionInvariant(\n decoded.subjectId === subjectId &&\n decoded.sessionId === sessionId &&\n decoded.digest === digest &&\n decoded.securityRevision === revision &&\n DateTime.toEpochMillis(decoded.expiresAt) === expiresAt &&\n DateTime.toEpochMillis(decoded.absoluteExpiresAt) === absolute,\n );\n yield* Schema.decodeEffect(SessionId)(sessionId);\n yield* Schema.decodeEffect(SessionCredentialVersion)(decoded.credentialVersion);\n\n return { ...decoded, issuedAt };\n });\n\n const owner = Effect.fnUntraced(function* (\n record: Pick<StatefulSessionRecord<Claims>, \"subjectId\" | \"sessionId\" | \"digest\">,\n ) {\n const nativeSubject = yield* mapping.subjectId.toNative(record.subjectId),\n nativeSession = yield* mapping.sessionId.toNative(record.sessionId);\n\n return sql.and([\n sql`${table.column(s.subjectId)} = ${table.value(s.subjectId, nativeSubject)}`,\n sql`${table.column(s.sessionId)} = ${table.value(s.sessionId, nativeSession)}`,\n exact(table, s.digest, record.digest),\n ]);\n });\n\n // These are the semantic values supplied by the session encoder for this\n // write, not an observed whole-row snapshot or unrelated application columns.\n const encodedCondition = (values: Readonly<Record<string, unknown>>) =>\n sql.and(\n Object.entries(values)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) =>\n value === null\n ? sql`${table.column(key)} is null`\n : typeof value === \"string\"\n ? exact(table, key, value)\n : sql`${table.column(key)} = ${table.value(key, value)}`,\n ),\n );\n\n const read = Effect.fnUntraced(function* (digest: TokenDigest) {\n const rows =\n yield* sql`select ${table.fields(\"session_\")} from ${table.name} where ${exact(table, s.digest, digest)} and ${live(table)} limit 2`;\n\n sessionInvariant(rows.length <= 1);\n\n return rows[0] === undefined ? undefined : yield* decode(table.decode(rows[0], \"session_\"));\n });\n\n return { sql, s, table, now, exact, millis, live, decode, owner, read, encodedCondition };\n});\n"],"mappings":"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"}