@yielded/auth-persistence 0.1.0-beta.30 → 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.
- package/README.md +11 -7
- package/dist/Adapter.d.mts +1 -1
- package/dist/Adapter.mjs +8 -8
- package/dist/OAuthPersistence.mjs +10 -5
- package/dist/OAuthPersistence.mjs.map +1 -1
- package/dist/internal/configuration.d.mts +49 -13
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/email-native.mjs +10 -2
- package/dist/internal/email-native.mjs.map +1 -1
- package/dist/internal/email-policy.mjs +3 -2
- package/dist/internal/email-policy.mjs.map +1 -1
- package/dist/internal/models/oauth-model.d.mts +4 -2
- package/dist/internal/models/oauth-model.mjs.map +1 -1
- package/dist/internal/models/password-model.d.mts +2 -1
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/oauth/native-accounts.d.mts +1 -1
- package/dist/internal/oauth/native-accounts.mjs +6 -6
- package/dist/internal/oauth/native-accounts.mjs.map +1 -1
- package/dist/internal/oauth/native-connected.d.mts +1 -1
- package/dist/internal/oauth/native-connected.mjs +1 -1
- package/dist/internal/oauth/native-registration.mjs +19 -8
- package/dist/internal/oauth/native-registration.mjs.map +1 -1
- package/dist/internal/oauth/native-sign-in.d.mts +1 -1
- package/dist/internal/oauth/native-sign-in.mjs +1 -1
- package/dist/internal/oauth/native-state.mjs +21 -11
- package/dist/internal/oauth/native-state.mjs.map +1 -1
- package/dist/internal/oauth/state.d.mts +1 -1
- package/dist/internal/oauth/state.mjs +1 -1
- package/dist/internal/oauth-managed.mjs +182 -0
- package/dist/internal/oauth-managed.mjs.map +1 -0
- package/dist/internal/passkey-managed.mjs +5 -3
- package/dist/internal/passkey-managed.mjs.map +1 -1
- package/dist/internal/passkey-native-registration.mjs +1 -1
- package/dist/internal/passkey-native-state.mjs +33 -23
- package/dist/internal/passkey-native-state.mjs.map +1 -1
- package/dist/internal/passkey-native-write.mjs +3 -3
- package/dist/internal/passkey-native-write.mjs.map +1 -1
- package/dist/internal/passkey-native.d.mts +15 -7
- package/dist/internal/passkey-native.mjs +2 -1
- package/dist/internal/passkey-native.mjs.map +1 -1
- package/dist/internal/passkey-policy.mjs +9 -7
- package/dist/internal/passkey-policy.mjs.map +1 -1
- package/dist/internal/password-native.mjs +22 -5
- package/dist/internal/password-native.mjs.map +1 -1
- package/dist/internal/password-policy.mjs +7 -6
- package/dist/internal/password-policy.mjs.map +1 -1
- package/dist/internal/password-registration-native.d.mts +1 -1
- package/dist/internal/persistence.d.mts +3 -0
- package/dist/internal/persistence.mjs +99 -42
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/phone-native.mjs +1 -1
- package/dist/internal/phone-native.mjs.map +1 -1
- package/dist/internal/registration.mjs +17 -8
- package/dist/internal/registration.mjs.map +1 -1
- package/dist/internal/session-native-authority.mjs +8 -6
- package/dist/internal/session-native-authority.mjs.map +1 -1
- package/dist/internal/session-native-login.d.mts +2 -2
- package/dist/internal/session-native-login.mjs +4 -2
- package/dist/internal/session-native-login.mjs.map +1 -1
- package/dist/internal/session-native-pending-read.mjs +26 -20
- package/dist/internal/session-native-pending-read.mjs.map +1 -1
- package/dist/internal/session-native-record.mjs +6 -4
- package/dist/internal/session-native-record.mjs.map +1 -1
- package/dist/internal/session-native-state.mjs +24 -8
- package/dist/internal/session-native-state.mjs.map +1 -1
- package/dist/internal/session-native-stateful.d.mts +2 -2
- package/dist/internal/session-native-stateful.mjs +67 -38
- package/dist/internal/session-native-stateful.mjs.map +1 -1
- package/dist/internal/session-native-step-up.d.mts +2 -2
- package/dist/internal/session-native-step-up.mjs +17 -19
- package/dist/internal/session-native-step-up.mjs.map +1 -1
- package/dist/internal/session-native-validity.mjs +3 -1
- package/dist/internal/session-native-validity.mjs.map +1 -1
- package/dist/internal/sql-authority.mjs +5 -1
- package/dist/internal/sql-authority.mjs.map +1 -1
- package/dist/internal/sql-storage-validation.mjs +2 -1
- package/dist/internal/sql-storage-validation.mjs.map +1 -1
- package/dist/internal/sql-table.d.mts +1 -0
- package/dist/internal/sql-table.mjs.map +1 -1
- package/dist/internal/sqlite-policy-guard.mjs +23 -0
- package/dist/internal/sqlite-policy-guard.mjs.map +1 -0
- package/dist/internal/step-up-state.mjs +19 -6
- package/dist/internal/step-up-state.mjs.map +1 -1
- package/dist/internal/storage-mapping.d.mts +9 -5
- package/dist/internal/storage-mapping.mjs +17 -5
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-tables.d.mts +237 -0
- package/dist/internal/storage-tables.mjs +68 -0
- package/dist/internal/storage-tables.mjs.map +1 -1
- package/dist/internal/storage-validation.d.mts +3 -1
- package/dist/internal/storage-validation.mjs +70 -17
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/totp-native.mjs +1 -1
- package/package.json +1 -1
- package/src/OAuthPersistence.ts +7 -2
- package/src/internal/configuration.ts +72 -15
- package/src/internal/email-native.ts +16 -6
- package/src/internal/email-policy.ts +12 -3
- package/src/internal/models/oauth-model.ts +7 -1
- package/src/internal/models/password-model.ts +2 -1
- package/src/internal/oauth/native-accounts.ts +3 -3
- package/src/internal/oauth/native-registration.ts +17 -6
- package/src/internal/oauth/native-state.ts +38 -18
- package/src/internal/oauth-managed.ts +277 -0
- package/src/internal/passkey-managed.ts +8 -2
- package/src/internal/passkey-native-state.ts +59 -52
- package/src/internal/passkey-native-write.ts +2 -2
- package/src/internal/passkey-native.ts +4 -2
- package/src/internal/passkey-policy.ts +13 -9
- package/src/internal/password-native.ts +45 -11
- package/src/internal/password-policy.ts +16 -8
- package/src/internal/persistence.ts +360 -204
- package/src/internal/phone-native.ts +1 -1
- package/src/internal/registration.ts +53 -40
- package/src/internal/session-native-authority.ts +26 -8
- package/src/internal/session-native-login.ts +9 -4
- package/src/internal/session-native-pending-read.ts +46 -33
- package/src/internal/session-native-record.ts +6 -4
- package/src/internal/session-native-state.ts +32 -10
- package/src/internal/session-native-stateful.ts +147 -52
- package/src/internal/session-native-step-up.ts +31 -24
- package/src/internal/session-native-validity.ts +3 -1
- package/src/internal/sql-authority.ts +5 -1
- package/src/internal/sql-storage-validation.ts +53 -46
- package/src/internal/sql-table.ts +1 -0
- package/src/internal/sqlite-policy-guard.ts +34 -0
- package/src/internal/step-up-state.ts +43 -6
- package/src/internal/storage-mapping.ts +40 -4
- package/src/internal/storage-tables.ts +76 -0
- 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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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PersistenceConfigurationError } from "./configuration.mjs";
|
|
2
|
-
import { CurrentSqlCommit, makeSqlCommitExecutor } from "./sql-commit.mjs";
|
|
2
|
+
import { CurrentSqlCommit, SqlBatchCommit, makeSqlCommitExecutor } from "./sql-commit.mjs";
|
|
3
3
|
import { randomId } from "./crypto.mjs";
|
|
4
4
|
import { PasswordIdentifierTaken, makeNativePasswordRegistrationStore } from "./password-registration-native.mjs";
|
|
5
5
|
import { Cause, Crypto, Effect, Option, Schema } from "effect";
|
|
@@ -7,6 +7,7 @@ import { PasswordUnavailable } from "@yielded/auth/Password";
|
|
|
7
7
|
import { SecurityRevision } from "@yielded/auth/Sessions";
|
|
8
8
|
import { SqlClient } from "effect/sql/SqlClient";
|
|
9
9
|
//#region src/internal/registration.ts
|
|
10
|
+
const SubjectValues = Schema.Record(Schema.String, Schema.Unknown);
|
|
10
11
|
/** Application provisioning and the unverified identifier/password binding share
|
|
11
12
|
* one physical owner. Identifier uniqueness arbitrates registration; correlation
|
|
12
13
|
* request IDs never become a retained credential-issuance receipt. */
|
|
@@ -15,28 +16,36 @@ const makeRegistrationAuthority = Effect.fnUntraced(function* (tables, mapping,
|
|
|
15
16
|
const crypto = yield* Crypto.Crypto;
|
|
16
17
|
const executor = yield* makeSqlCommitExecutor(() => PasswordUnavailable.make({}));
|
|
17
18
|
const parent = yield* Effect.serviceOption(CurrentSqlCommit);
|
|
18
|
-
const
|
|
19
|
+
const batch = yield* SqlBatchCommit;
|
|
20
|
+
const store = yield* makeNativePasswordRegistrationStore(tables, mapping, batch !== void 0);
|
|
19
21
|
const createSubject = (yield* provisioning)[strategy];
|
|
20
22
|
if (createSubject === void 0) return yield* PersistenceConfigurationError.make({ reason: `Missing subject provisioning for ${strategy}` });
|
|
23
|
+
if (batch !== void 0 && typeof createSubject === "function") return yield* PersistenceConfigurationError.make({ reason: `D1 subject provisioning for ${strategy} requires { values }; return the new subject row instead of inserting it` });
|
|
21
24
|
const s = mapping.subject;
|
|
22
25
|
const subject = tables(s.table);
|
|
23
26
|
const lock = sql.onDialectOrElse({
|
|
24
27
|
sqlite: () => sql``,
|
|
25
28
|
orElse: () => sql`for update`
|
|
26
29
|
});
|
|
27
|
-
const run = (work, suppressed) => Option.isSome(parent) ? executor.run(work) : executor.operation(work.pipe(Effect.mapError((error) => error instanceof PasswordIdentifierTaken ? error : PasswordUnavailable.make({})))).pipe(Effect.catchCause((cause) => cause.reasons.length > 0 && cause.reasons.every((reason) => Cause.isFailReason(reason) && reason.error instanceof PasswordIdentifierTaken) ? executor.run(suppressed, "statement") : Effect.failCause(cause)), Effect.mapError(() => PasswordUnavailable.make({})));
|
|
30
|
+
const run = (work, suppressed) => batch !== void 0 ? executor.batch(work).pipe(Effect.provideService(SqlBatchCommit, batch)) : Option.isSome(parent) ? executor.run(work) : executor.operation(work.pipe(Effect.mapError((error) => error instanceof PasswordIdentifierTaken ? error : PasswordUnavailable.make({})))).pipe(Effect.catchCause((cause) => cause.reasons.length > 0 && cause.reasons.every((reason) => Cause.isFailReason(reason) && reason.error instanceof PasswordIdentifierTaken) ? executor.run(suppressed, "statement") : Effect.failCause(cause)), Effect.mapError(() => PasswordUnavailable.make({})));
|
|
28
31
|
return { register: (input, project) => executor.read(store.available(input.identifier)).pipe(Effect.flatMap((existing) => existing.length !== 0 ? executor.run(store.prepare({ _tag: "Suppressed" }, project), "statement") : run(Effect.gen(function* () {
|
|
29
|
-
const
|
|
32
|
+
const provisioningInput = {
|
|
30
33
|
requestId: input.requestId,
|
|
31
34
|
identifier: input.identifier,
|
|
32
35
|
registration: input.registration
|
|
36
|
+
};
|
|
37
|
+
const row = yield* Effect.gen(function* () {
|
|
38
|
+
if (typeof createSubject !== "function") return yield* createSubject.values(provisioningInput).pipe(Effect.flatMap(Schema.decodeEffect(SubjectValues)));
|
|
39
|
+
const subjectId = yield* createSubject(provisioningInput);
|
|
40
|
+
const nativeId = yield* mapping.subjectId.toNative(subjectId);
|
|
41
|
+
const rows = yield* sql`select ${subject.fields("registration_subject_")} from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} limit 2 ${lock}`;
|
|
42
|
+
if (rows.length !== 1) return yield* PasswordUnavailable.make({});
|
|
43
|
+
return subject.decode(rows[0], "registration_subject_");
|
|
33
44
|
});
|
|
34
|
-
const nativeId = yield* mapping.subjectId.toNative(subjectId);
|
|
35
|
-
const rows = yield* sql`select ${subject.fields("registration_subject_")} from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} limit 2 ${lock}`;
|
|
36
|
-
if (rows.length !== 1) return yield* PasswordUnavailable.make({});
|
|
37
|
-
const row = subject.decode(rows[0], "registration_subject_");
|
|
38
45
|
if (!s.isActiveStatus(row[s.status])) return yield* PasswordUnavailable.make({});
|
|
46
|
+
const subjectId = yield* mapping.subjectId.toSubject(row[s.id]);
|
|
39
47
|
const securityRevision = yield* Schema.decodeUnknownEffect(SecurityRevision)(row[s.securityRevision]);
|
|
48
|
+
if (typeof createSubject !== "function") yield* store.stage(sql`${subject.insert(row)}`);
|
|
40
49
|
const identifierRevision = SecurityRevision.make(yield* randomId);
|
|
41
50
|
const credentialId = yield* randomId;
|
|
42
51
|
const credentialRevision = SecurityRevision.make(yield* randomId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registration.mjs","names":[],"sources":["../../src/internal/registration.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport
|
|
1
|
+
{"version":3,"file":"registration.mjs","names":[],"sources":["../../src/internal/registration.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { PasswordUnavailable } from \"@yielded/auth/Password\";\nimport { SecurityRevision } from \"@yielded/auth/Sessions\";\nimport { type Context, Crypto, Effect, Schema, Option, Cause } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\n\nimport { PersistenceConfigurationError, type SubjectProvisioning } from \"./configuration\";\nimport { randomId } from \"./crypto\";\nimport type { AnyPasswordPersistenceMapping } from \"./models/password-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport {\n makeNativePasswordRegistrationStore,\n PasswordIdentifierTaken,\n} from \"./password-registration-native\";\nimport type { PasswordRegistrationAuthority } from \"./registration-contract\";\nimport { makeSqlCommitExecutor, CurrentSqlCommit, SqlBatchCommit } from \"./sql-commit\";\n\nconst SubjectValues = Schema.Record(Schema.String, Schema.Unknown);\n\n/** Application provisioning and the unverified identifier/password binding share\n * one physical owner. Identifier uniqueness arbitrates registration; correlation\n * request IDs never become a retained credential-issuance receipt. */\nexport const makeRegistrationAuthority = Effect.fnUntraced(function* <R>(\n tables: NativeSqlTables,\n mapping: AnyPasswordPersistenceMapping,\n provisioning: Context.Key<R, object>,\n strategy: string,\n): Effect.fn.Return<\n PasswordRegistrationAuthority<unknown>,\n PersistenceConfigurationError,\n LifecycleHooks | SqlClient | Crypto.Crypto | SqlBatchCommit | R\n> {\n const sql = (yield* SqlClient).withoutTransforms();\n const crypto = yield* Crypto.Crypto;\n const executor = yield* makeSqlCommitExecutor(() => PasswordUnavailable.make({}));\n const parent = yield* Effect.serviceOption(CurrentSqlCommit);\n const batch = yield* SqlBatchCommit;\n const store = yield* makeNativePasswordRegistrationStore(tables, mapping, batch !== undefined);\n // The strategy has already decoded its registration Schema. Only its callback\n // signature is erased by the heterogeneous strategy service map.\n const creators = (yield* provisioning) as Readonly<Record<string, SubjectProvisioning<unknown>>>;\n const createSubject = creators[strategy];\n\n if (createSubject === undefined)\n return yield* PersistenceConfigurationError.make({\n reason: `Missing subject provisioning for ${strategy}`,\n });\n if (batch !== undefined && typeof createSubject === \"function\")\n return yield* PersistenceConfigurationError.make({\n reason: `D1 subject provisioning for ${strategy} requires { values }; return the new subject row instead of inserting it`,\n });\n const s = mapping.subject;\n const subject = tables(s.table);\n const lock = sql.onDialectOrElse({ sqlite: () => sql``, orElse: () => sql`for update` });\n\n const run = <A, E, R2>(work: Effect.Effect<A, E, R2>, suppressed: Effect.Effect<A, E, R2>) =>\n batch !== undefined\n ? executor.batch(work).pipe(Effect.provideService(SqlBatchCommit, batch))\n : Option.isSome(parent)\n ? executor.run(work)\n : executor\n .operation(\n work.pipe(\n Effect.mapError((error) =>\n error instanceof PasswordIdentifierTaken ? error : PasswordUnavailable.make({}),\n ),\n ),\n )\n .pipe(\n Effect.catchCause((cause) =>\n cause.reasons.length > 0 &&\n cause.reasons.every(\n (reason) =>\n Cause.isFailReason(reason) && reason.error instanceof PasswordIdentifierTaken,\n )\n ? executor.run(suppressed, \"statement\")\n : Effect.failCause(cause),\n ),\n Effect.mapError(() => PasswordUnavailable.make({})),\n );\n\n return {\n register: (input, project) =>\n executor.read(store.available(input.identifier)).pipe(\n Effect.flatMap((existing) =>\n existing.length !== 0\n ? executor.run(store.prepare({ _tag: \"Suppressed\" }, project), \"statement\")\n : run(\n Effect.gen(function* () {\n const provisioningInput = {\n requestId: input.requestId,\n identifier: input.identifier,\n registration: input.registration,\n };\n\n const row = yield* Effect.gen(function* () {\n if (typeof createSubject !== \"function\")\n return yield* createSubject\n .values(provisioningInput)\n .pipe(Effect.flatMap(Schema.decodeEffect(SubjectValues)));\n const subjectId = yield* createSubject(provisioningInput);\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n const rows =\n yield* sql`select ${subject.fields(\"registration_subject_\")} from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} limit 2 ${lock}`;\n\n if (rows.length !== 1) return yield* PasswordUnavailable.make({});\n\n return subject.decode(rows[0]!, \"registration_subject_\");\n });\n\n if (!s.isActiveStatus(row[s.status])) return yield* PasswordUnavailable.make({});\n const subjectId = yield* mapping.subjectId.toSubject(row[s.id]);\n\n const securityRevision = yield* Schema.decodeUnknownEffect(SecurityRevision)(\n row[s.securityRevision],\n );\n\n if (typeof createSubject !== \"function\")\n yield* store.stage(sql`${subject.insert(row)}`);\n\n const identifierRevision = SecurityRevision.make(yield* randomId);\n const credentialId = yield* randomId;\n const credentialRevision = SecurityRevision.make(yield* randomId);\n const verifierVersion = SecurityRevision.make(yield* randomId);\n\n yield* store.bind({\n ...input,\n subjectId,\n securityRevision,\n identifierRevision,\n credentialId,\n credentialRevision,\n verifierVersion,\n });\n\n return yield* store.prepare({ _tag: \"Created\", subjectId }, project);\n }).pipe(Effect.provideService(Crypto.Crypto, crypto)),\n store.prepare({ _tag: \"Suppressed\" }, project),\n ),\n ),\n ),\n };\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 = [
|
|
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,11 +56,12 @@ 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);
|
|
64
|
+
const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${now} >= ${current.now} and ${now} < ${deadline}`;
|
|
63
65
|
if (batch !== void 0) {
|
|
64
66
|
yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));
|
|
65
67
|
yield* registerSqlBatchPostcondition({
|
|
@@ -75,7 +77,7 @@ const makeNativeAuthenticationAuthorityServices = Effect.fnUntraced(function* (t
|
|
|
75
77
|
if (input.pending !== void 0 && pending !== void 0) {
|
|
76
78
|
if ((yield* sql`select 1 where ${pending.consumedCondition("Login", input.pending, current.native)}`).length !== 1) return yield* PendingAuthenticationInvalid.make({});
|
|
77
79
|
}
|
|
78
|
-
}).pipe(Effect.mapError((cause) => PersistenceMappingError.make({
|
|
80
|
+
}).pipe(Effect.provideService(AuthenticationClock, state.clockPolicy), Effect.mapError((cause) => PersistenceMappingError.make({
|
|
79
81
|
operation: "decode",
|
|
80
82
|
cause
|
|
81
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 const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${state.policyCondition(current.row, 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
|
-
},
|
|
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);
|
|
@@ -43,7 +44,8 @@ const makeNativePendingAuthenticationServices = Effect.fnUntraced(function* (tab
|
|
|
43
44
|
version
|
|
44
45
|
};
|
|
45
46
|
const snapshot = yield* mapping.login.encode(record);
|
|
46
|
-
|
|
47
|
+
yield* state.guardPolicy(current);
|
|
48
|
+
const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${now} >= ${current.now} and ${now} < ${expires}`;
|
|
47
49
|
if (batch !== void 0) yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));
|
|
48
50
|
yield* pending.insert({
|
|
49
51
|
moduleId: mapping.moduleId,
|
|
@@ -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 const condition = sql`${state.authorityCondition(current.revision, current.native)} and ${state.policyCondition(current.row, 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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"}
|