@yielded/auth-persistence 0.1.0-beta.31 → 0.1.0-beta.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- 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 +31 -5
- 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/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 +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 +9 -6
- 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/persistence.mjs +96 -37
- 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/session-native-authority.mjs +6 -5
- 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 +2 -1
- 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 +3 -0
- 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 +65 -37
- 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 +14 -18
- 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/step-up-state.mjs +19 -6
- package/dist/internal/step-up-state.mjs.map +1 -1
- package/dist/internal/storage-mapping.mjs +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 +46 -7
- 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/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-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 +17 -9
- package/src/internal/password-policy.ts +16 -8
- package/src/internal/persistence.ts +216 -70
- package/src/internal/phone-native.ts +1 -1
- package/src/internal/session-native-authority.ts +23 -7
- package/src/internal/session-native-login.ts +6 -3
- 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 +3 -0
- package/src/internal/session-native-stateful.ts +144 -51
- package/src/internal/session-native-step-up.ts +27 -23
- 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/step-up-state.ts +43 -6
- package/src/internal/storage-tables.ts +76 -0
- package/src/internal/storage-validation.ts +127 -27
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-policy.mjs","names":[],"sources":["../../src/internal/password-policy.ts"],"sourcesContent":["import type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordUnavailable,\n snapshotPasswordCredential,\n snapshotPasswordRequirement,\n snapshotPasswordRevision,\n type PasswordAction,\n type PasswordCredentialSnapshot,\n type PasswordMutationInput,\n} from \"@yielded/auth/Password\";\nimport type { ProofRedemptionPlan } from \"@yielded/auth/Proofs\";\nimport {\n snapshotAuthenticationEvidence,\n type AuthenticationEvidence,\n type AuthenticationRequirement,\n type SecurityRevision,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Redacted } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyProofPersistenceMapping } from \"./models/proof-model\";\n\nconst unavailable = () => PasswordUnavailable.make({});\n\nexport const samePasswordIdentifier = (left: LoginIdentifier, right: LoginIdentifier) =>\n left.namespace === right.namespace && left.value === right.value;\n\nexport const samePasswordCredential = (\n left: PasswordCredentialSnapshot,\n right: PasswordCredentialSnapshot,\n) =>\n left.moduleId === right.moduleId &&\n left.revision.subjectId === right.revision.subjectId &&\n left.revision.securityRevision === right.revision.securityRevision &&\n left.credentialId === right.credentialId &&\n left.credentialRevision === right.credentialRevision &&\n left.verifierVersion === right.verifierVersion &&\n left.normalization === right.normalization &&\n left.identifierVerifiedAtMillis === right.identifierVerifiedAtMillis &&\n left.revision.credentials.length === right.revision.credentials.length &&\n left.revision.credentials.every((entry) =>\n right.revision.credentials.some(\n (other) => other.credentialId === entry.credentialId && other.revision === entry.revision,\n ),\n ) &&\n left.identifierBindingRevision === right.identifierBindingRevision &&\n samePasswordIdentifier(left.identifier, right.identifier) &&\n Redacted.value(left.verifier) === Redacted.value(right.verifier);\n\nexport const passwordEvidenceSatisfiedAt = (\n evidence: AuthenticationEvidence,\n requirement: AuthenticationRequirement,\n now: number,\n): boolean => {\n if (evidence.proofs.length > 64 || evidence.revision.credentials.length > 64) return false;\n const revisions = new Map<string, string>();\n\n for (const item of evidence.revision.credentials) {\n if (revisions.has(item.credentialId)) return false;\n revisions.set(item.credentialId, item.revision);\n }\n if (evidence.proofs.some((proof) => !revisions.has(proof.credentialId))) return false;\n if (evidence.proofs.some((proof) => now < DateTime.toEpochMillis(proof.verifiedAt))) return false;\n\n const eligible = evidence.proofs.filter((proof) => {\n const age = now - DateTime.toEpochMillis(proof.verifiedAt);\n\n return age >= 0 && age < requirement.maximumAgeMillis;\n });\n\n const factors = new Set(eligible.flatMap((proof) => proof.factors));\n const credentials = new Set(eligible.map((proof) => proof.credentialId));\n\n return requirement.alternatives.some(\n (alternative) =>\n alternative.factors.every((factor) => factors.has(factor)) &&\n credentials.size >= alternative.minimumCredentials &&\n eligible.some(\n (proof) =>\n (!alternative.userVerified || proof.userVerified) &&\n (!alternative.phishingResistant || proof.phishingResistant),\n ),\n );\n};\n\nexport const snapshotPasswordMutation = Effect.fn(\"PasswordPersistence.snapshotMutation\")(\n function* (input: PasswordMutationInput) {\n const expectedRevision = snapshotPasswordRevision(input.expectedRevision);\n\n const evidence = yield* snapshotAuthenticationEvidence(input.authorization.evidence).pipe(\n Effect.mapError(unavailable),\n );\n\n const requirement = yield* snapshotPasswordRequirement(input.authorization.requirement);\n\n const credential =\n input.credential === undefined\n ? undefined\n : yield* snapshotPasswordCredential(input.credential);\n\n return Object.freeze({\n ...input,\n expectedRevision,\n replacement: Object.freeze({\n verifier: Redacted.make(Redacted.value(input.replacement.verifier)),\n normalization: input.replacement.normalization,\n }),\n invalidation: Object.freeze({ ...input.invalidation }),\n ...(credential === undefined ? {} : { credential }),\n authorization: Object.freeze({\n challenge: Object.freeze({\n ...input.authorization.challenge,\n revision: snapshotPasswordRevision(input.authorization.challenge.revision),\n }),\n evidence,\n requirement,\n }),\n });\n },\n);\n\nexport const passwordProofRedemptionMatches = (\n input: PasswordMutationInput & { readonly redemption: ProofRedemptionPlan },\n) => {\n const completion = input.redemption.input;\n const binding = completion.binding;\n\n if (\n completion.moduleId !== `${input.moduleId}/reset` ||\n completion.purpose !== \"password-reset\" ||\n binding._tag !== \"Subject\" ||\n input.credential === undefined ||\n binding.revision.subjectId !== input.expectedRevision.subjectId ||\n binding.revision.securityRevision !== input.expectedRevision.securityRevision ||\n !samePasswordIdentifier(binding.identifier, input.credential.identifier)\n )\n return false;\n\n const expected = [...input.expectedRevision.credentials].sort((left, right) =>\n left.credentialId.localeCompare(right.credentialId),\n );\n\n const actual = [...binding.revision.credentials].sort((left, right) =>\n left.credentialId.localeCompare(right.credentialId),\n );\n\n return (\n expected.length === actual.length &&\n expected.every(\n (item, index) =>\n item.credentialId === actual[index]!.credentialId &&\n item.revision === actual[index]!.revision,\n )\n );\n};\n\nexport interface PasswordWorkflowPolicy {\n readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;\n readonly allocateCredentialIdSync?: () => string;\n readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateRevisionSync?: () => SecurityRevision;\n readonly subject: {\n readonly nextSecurityRevision?: (\n current: SecurityRevision,\n ) => Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly nextSecurityRevisionSync?: (current: SecurityRevision) => SecurityRevision;\n };\n readonly sessionInvalidation: \"same-authority-immediate\" | \"original-absolute-expiry\";\n}\n\nexport interface PasswordWorkflowOptions {\n readonly mode: \"interactive\" | \"synchronous\";\n readonly locking: boolean;\n readonly coordinated?: boolean;\n readonly standaloneGuard: Effect.Effect<void, PasswordUnavailable>;\n readonly proof?: AnyProofPersistenceMapping;\n}\n\nexport const allocatePasswordValue = <A>(\n mode: PasswordWorkflowOptions[\"mode\"],\n asynchronous: Effect.Effect<A, PersistenceMappingError> | undefined,\n synchronous: (() => A) | undefined,\n): Effect.Effect<A, PasswordUnavailable | PersistenceMappingError> =>\n mode !== \"synchronous\" && asynchronous !== undefined\n ? asynchronous\n : synchronous === undefined\n ? Effect.fail(unavailable())\n : Effect.try({\n try: synchronous,\n catch: (cause) => PersistenceMappingError.make({ operation: \"mapping\", cause }),\n });\n\nexport const allocatePasswordNextSecurityRevision = (\n policy: PasswordWorkflowPolicy,\n mode: PasswordWorkflowOptions[\"mode\"],\n current: SecurityRevision,\n) =>\n allocatePasswordValue(\n mode,\n policy.subject.nextSecurityRevision?.(current) ?? policy.allocateRevision,\n policy.subject.nextSecurityRevisionSync === undefined\n ? policy.allocateRevisionSync\n : () => policy.subject.nextSecurityRevisionSync!(current),\n );\n\nexport const passwordMutationEvidenceMatches = (input: PasswordMutationInput) =>\n !input.expectedRevision.credentials.some(\n (expected) =>\n !input.authorization.evidence.revision.credentials.some(\n (actual) =>\n actual.credentialId === expected.credentialId && actual.revision === expected.revision,\n ),\n );\n\nexport const validatePasswordMutation = Effect.fnUntraced(function* (\n policy: PasswordWorkflowPolicy,\n input: PasswordMutationInput,\n action: PasswordAction,\n current: Pick<\n PasswordMutationRead,\n \"subject\" | \"identifierCurrent\" | \"credentials\" | \"snapshot\" | \"requirement\"\n >,\n now: number,\n) {\n if (\n current.subject === undefined ||\n !current.subject.active ||\n current.subject.securityRevision !== input.expectedRevision.securityRevision\n )\n return undefined;\n const { challenge, evidence } = input.authorization;\n\n if (\n challenge.moduleId !== input.moduleId ||\n challenge.action !== action ||\n challenge.commandId !== input.commandId ||\n challenge.revision.subjectId !== input.expectedRevision.subjectId ||\n challenge.revision.securityRevision !== input.expectedRevision.securityRevision ||\n (evidence.flowId as string) !== (input.commandId as string) ||\n evidence.bindingDigest !== challenge.bindingDigest ||\n evidence.revision.subjectId !== input.expectedRevision.subjectId ||\n evidence.revision.securityRevision !== input.expectedRevision.securityRevision ||\n (action === \"add-password\" &&\n (input.credential !== undefined || challenge.targetCredentialId !== undefined))\n )\n return undefined;\n if (!passwordMutationEvidenceMatches(input)) return undefined;\n if (input.credential !== undefined && !current.identifierCurrent) return undefined;\n\n const expected = new Map(\n [...input.expectedRevision.credentials, ...evidence.revision.credentials].map((item) => [\n item.credentialId,\n item,\n ]),\n );\n\n if (current.credentials.length !== expected.size) return undefined;\n for (const credential of current.credentials) {\n const wanted = expected.get(credential.credentialId);\n\n if (wanted === undefined || credential.revision !== wanted.revision || !credential.active)\n return undefined;\n expected.delete(wanted.credentialId);\n }\n if (expected.size !== 0) return undefined;\n if (input.credential !== undefined) {\n const snapshot = yield* current.snapshot;\n\n if (snapshot === undefined || !samePasswordCredential(snapshot, input.credential))\n return undefined;\n }\n const requirement = yield* current.requirement;\n\n if (\n !passwordEvidenceSatisfiedAt(evidence, input.authorization.requirement, now) ||\n !passwordEvidenceSatisfiedAt(evidence, requirement, now)\n )\n return undefined;\n if (\n input.invalidation.existingSessions === \"immediate\" &&\n policy.sessionInvalidation !== \"same-authority-immediate\"\n )\n return undefined;\n\n return now;\n});\n\n/** Decoded subject-first snapshot used only for this action's policy decision. */\nexport interface PasswordMutationRead {\n readonly subject:\n | { readonly active: boolean; readonly securityRevision: SecurityRevision }\n | undefined;\n readonly identifierCurrent: boolean;\n readonly credentials: ReadonlyArray<{\n readonly credentialId: string;\n readonly revision: SecurityRevision;\n readonly active: boolean;\n }>;\n readonly snapshot: Effect.Effect<\n PasswordCredentialSnapshot | undefined,\n PasswordUnavailable | PersistenceMappingError\n >;\n readonly requirement: Effect.Effect<\n AuthenticationRequirement,\n PasswordUnavailable | PersistenceMappingError\n >;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"password-policy.mjs","names":[],"sources":["../../src/internal/password-policy.ts"],"sourcesContent":["import type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport { AuthenticationClock } from \"@yielded/auth/Operations\";\nimport {\n PasswordUnavailable,\n snapshotPasswordCredential,\n snapshotPasswordRequirement,\n snapshotPasswordRevision,\n type PasswordAction,\n type PasswordCredentialSnapshot,\n type PasswordMutationInput,\n} from \"@yielded/auth/Password\";\nimport type { ProofRedemptionPlan } from \"@yielded/auth/Proofs\";\nimport {\n snapshotAuthenticationEvidence,\n type AuthenticationEvidence,\n type AuthenticationRequirement,\n type SecurityRevision,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Redacted } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyProofPersistenceMapping } from \"./models/proof-model\";\n\nconst unavailable = () => PasswordUnavailable.make({});\n\nexport const samePasswordIdentifier = (left: LoginIdentifier, right: LoginIdentifier) =>\n left.namespace === right.namespace && left.value === right.value;\n\nexport const samePasswordCredential = (\n left: PasswordCredentialSnapshot,\n right: PasswordCredentialSnapshot,\n) =>\n left.moduleId === right.moduleId &&\n left.revision.subjectId === right.revision.subjectId &&\n left.revision.securityRevision === right.revision.securityRevision &&\n left.credentialId === right.credentialId &&\n left.credentialRevision === right.credentialRevision &&\n left.verifierVersion === right.verifierVersion &&\n left.normalization === right.normalization &&\n left.identifierVerifiedAtMillis === right.identifierVerifiedAtMillis &&\n left.revision.credentials.length === right.revision.credentials.length &&\n left.revision.credentials.every((entry) =>\n right.revision.credentials.some(\n (other) => other.credentialId === entry.credentialId && other.revision === entry.revision,\n ),\n ) &&\n left.identifierBindingRevision === right.identifierBindingRevision &&\n samePasswordIdentifier(left.identifier, right.identifier) &&\n Redacted.value(left.verifier) === Redacted.value(right.verifier);\n\nexport const passwordEvidenceSatisfiedAt = Effect.fnUntraced(function* (\n evidence: AuthenticationEvidence,\n requirement: AuthenticationRequirement,\n now: number,\n): Effect.fn.Return<boolean> {\n const { futureToleranceMillis } = yield* AuthenticationClock;\n\n if (evidence.proofs.length > 64 || evidence.revision.credentials.length > 64) return false;\n const revisions = new Map<string, string>();\n\n for (const item of evidence.revision.credentials) {\n if (revisions.has(item.credentialId)) return false;\n revisions.set(item.credentialId, item.revision);\n }\n if (evidence.proofs.some((proof) => !revisions.has(proof.credentialId))) return false;\n if (\n evidence.proofs.some(\n (proof) => DateTime.toEpochMillis(proof.verifiedAt) - now > futureToleranceMillis,\n )\n )\n return false;\n\n const eligible = evidence.proofs.filter((proof) => {\n const age = Math.max(0, now - DateTime.toEpochMillis(proof.verifiedAt));\n\n return age < requirement.maximumAgeMillis;\n });\n\n const factors = new Set(eligible.flatMap((proof) => proof.factors));\n const credentials = new Set(eligible.map((proof) => proof.credentialId));\n\n return requirement.alternatives.some(\n (alternative) =>\n alternative.factors.every((factor) => factors.has(factor)) &&\n credentials.size >= alternative.minimumCredentials &&\n eligible.some(\n (proof) =>\n (!alternative.userVerified || proof.userVerified) &&\n (!alternative.phishingResistant || proof.phishingResistant),\n ),\n );\n});\n\nexport const snapshotPasswordMutation = Effect.fn(\"PasswordPersistence.snapshotMutation\")(\n function* (input: PasswordMutationInput) {\n const expectedRevision = snapshotPasswordRevision(input.expectedRevision);\n\n const evidence = yield* snapshotAuthenticationEvidence(input.authorization.evidence).pipe(\n Effect.mapError(unavailable),\n );\n\n const requirement = yield* snapshotPasswordRequirement(input.authorization.requirement);\n\n const credential =\n input.credential === undefined\n ? undefined\n : yield* snapshotPasswordCredential(input.credential);\n\n return Object.freeze({\n ...input,\n expectedRevision,\n replacement: Object.freeze({\n verifier: Redacted.make(Redacted.value(input.replacement.verifier)),\n normalization: input.replacement.normalization,\n }),\n invalidation: Object.freeze({ ...input.invalidation }),\n ...(credential === undefined ? {} : { credential }),\n authorization: Object.freeze({\n challenge: Object.freeze({\n ...input.authorization.challenge,\n revision: snapshotPasswordRevision(input.authorization.challenge.revision),\n }),\n evidence,\n requirement,\n }),\n });\n },\n);\n\nexport const passwordProofRedemptionMatches = (\n input: PasswordMutationInput & { readonly redemption: ProofRedemptionPlan },\n) => {\n const completion = input.redemption.input;\n const binding = completion.binding;\n\n if (\n completion.moduleId !== `${input.moduleId}/reset` ||\n completion.purpose !== \"password-reset\" ||\n binding._tag !== \"Subject\" ||\n input.credential === undefined ||\n binding.revision.subjectId !== input.expectedRevision.subjectId ||\n binding.revision.securityRevision !== input.expectedRevision.securityRevision ||\n !samePasswordIdentifier(binding.identifier, input.credential.identifier)\n )\n return false;\n\n const expected = [...input.expectedRevision.credentials].sort((left, right) =>\n left.credentialId.localeCompare(right.credentialId),\n );\n\n const actual = [...binding.revision.credentials].sort((left, right) =>\n left.credentialId.localeCompare(right.credentialId),\n );\n\n return (\n expected.length === actual.length &&\n expected.every(\n (item, index) =>\n item.credentialId === actual[index]!.credentialId &&\n item.revision === actual[index]!.revision,\n )\n );\n};\n\nexport interface PasswordWorkflowPolicy {\n readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;\n readonly allocateCredentialIdSync?: () => string;\n readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateRevisionSync?: () => SecurityRevision;\n readonly subject: {\n readonly nextSecurityRevision?: (\n current: SecurityRevision,\n ) => Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly nextSecurityRevisionSync?: (current: SecurityRevision) => SecurityRevision;\n };\n readonly sessionInvalidation: \"same-authority-immediate\" | \"original-absolute-expiry\";\n}\n\nexport interface PasswordWorkflowOptions {\n readonly mode: \"interactive\" | \"synchronous\";\n readonly locking: boolean;\n readonly coordinated?: boolean;\n readonly standaloneGuard: Effect.Effect<void, PasswordUnavailable>;\n readonly proof?: AnyProofPersistenceMapping;\n}\n\nexport const allocatePasswordValue = <A>(\n mode: PasswordWorkflowOptions[\"mode\"],\n asynchronous: Effect.Effect<A, PersistenceMappingError> | undefined,\n synchronous: (() => A) | undefined,\n): Effect.Effect<A, PasswordUnavailable | PersistenceMappingError> =>\n mode !== \"synchronous\" && asynchronous !== undefined\n ? asynchronous\n : synchronous === undefined\n ? Effect.fail(unavailable())\n : Effect.try({\n try: synchronous,\n catch: (cause) => PersistenceMappingError.make({ operation: \"mapping\", cause }),\n });\n\nexport const allocatePasswordNextSecurityRevision = (\n policy: PasswordWorkflowPolicy,\n mode: PasswordWorkflowOptions[\"mode\"],\n current: SecurityRevision,\n) =>\n allocatePasswordValue(\n mode,\n policy.subject.nextSecurityRevision?.(current) ?? policy.allocateRevision,\n policy.subject.nextSecurityRevisionSync === undefined\n ? policy.allocateRevisionSync\n : () => policy.subject.nextSecurityRevisionSync!(current),\n );\n\nexport const passwordMutationEvidenceMatches = (input: PasswordMutationInput) =>\n !input.expectedRevision.credentials.some(\n (expected) =>\n !input.authorization.evidence.revision.credentials.some(\n (actual) =>\n actual.credentialId === expected.credentialId && actual.revision === expected.revision,\n ),\n );\n\nexport const validatePasswordMutation = Effect.fnUntraced(function* (\n policy: PasswordWorkflowPolicy,\n input: PasswordMutationInput,\n action: PasswordAction,\n current: Pick<\n PasswordMutationRead,\n \"subject\" | \"identifierCurrent\" | \"credentials\" | \"snapshot\" | \"requirement\"\n >,\n now: number,\n) {\n if (\n current.subject === undefined ||\n !current.subject.active ||\n current.subject.securityRevision !== input.expectedRevision.securityRevision\n )\n return undefined;\n const { challenge, evidence } = input.authorization;\n\n if (\n challenge.moduleId !== input.moduleId ||\n challenge.action !== action ||\n challenge.commandId !== input.commandId ||\n challenge.revision.subjectId !== input.expectedRevision.subjectId ||\n challenge.revision.securityRevision !== input.expectedRevision.securityRevision ||\n (evidence.flowId as string) !== (input.commandId as string) ||\n evidence.bindingDigest !== challenge.bindingDigest ||\n evidence.revision.subjectId !== input.expectedRevision.subjectId ||\n evidence.revision.securityRevision !== input.expectedRevision.securityRevision ||\n (action === \"add-password\" &&\n (input.credential !== undefined || challenge.targetCredentialId !== undefined))\n )\n return undefined;\n if (!passwordMutationEvidenceMatches(input)) return undefined;\n if (input.credential !== undefined && !current.identifierCurrent) return undefined;\n\n const expected = new Map(\n [...input.expectedRevision.credentials, ...evidence.revision.credentials].map((item) => [\n item.credentialId,\n item,\n ]),\n );\n\n if (current.credentials.length !== expected.size) return undefined;\n for (const credential of current.credentials) {\n const wanted = expected.get(credential.credentialId);\n\n if (wanted === undefined || credential.revision !== wanted.revision || !credential.active)\n return undefined;\n expected.delete(wanted.credentialId);\n }\n if (expected.size !== 0) return undefined;\n if (input.credential !== undefined) {\n const snapshot = yield* current.snapshot;\n\n if (snapshot === undefined || !samePasswordCredential(snapshot, input.credential))\n return undefined;\n }\n const requirement = yield* current.requirement;\n\n if (\n !(yield* passwordEvidenceSatisfiedAt(evidence, input.authorization.requirement, now)) ||\n !(yield* passwordEvidenceSatisfiedAt(evidence, requirement, now))\n )\n return undefined;\n if (\n input.invalidation.existingSessions === \"immediate\" &&\n policy.sessionInvalidation !== \"same-authority-immediate\"\n )\n return undefined;\n\n return now;\n});\n\n/** Decoded subject-first snapshot used only for this action's policy decision. */\nexport interface PasswordMutationRead {\n readonly subject:\n | { readonly active: boolean; readonly securityRevision: SecurityRevision }\n | undefined;\n readonly identifierCurrent: boolean;\n readonly credentials: ReadonlyArray<{\n readonly credentialId: string;\n readonly revision: SecurityRevision;\n readonly active: boolean;\n }>;\n readonly snapshot: Effect.Effect<\n PasswordCredentialSnapshot | undefined,\n PasswordUnavailable | PersistenceMappingError\n >;\n readonly requirement: Effect.Effect<\n AuthenticationRequirement,\n PasswordUnavailable | PersistenceMappingError\n >;\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,9 @@ import { requireStandalone } from "./standalone.mjs";
|
|
|
4
4
|
import { SqlBatchCommit } from "./sql-commit.mjs";
|
|
5
5
|
import { makeNativeProofServices } from "./proof-native.mjs";
|
|
6
6
|
import { makeNativeEmailAddressServices } from "./email-native.mjs";
|
|
7
|
+
import { validateStorageBatch, withStorageValidation } from "./storage-validation.mjs";
|
|
7
8
|
import { storageTables, tableDefinition } from "./storage-tables.mjs";
|
|
9
|
+
import { makeManagedOAuth } from "./oauth-managed.mjs";
|
|
8
10
|
import { makeManagedPasskeys } from "./passkey-managed.mjs";
|
|
9
11
|
import { makeNativePasswordServices } from "./password-native.mjs";
|
|
10
12
|
import { makeComposedPhoneTargets } from "./phone-composed.mjs";
|
|
@@ -15,14 +17,14 @@ import { makeNativePendingAuthenticationServices } from "./session-native-login.
|
|
|
15
17
|
import { makeNativeStatefulSessionServices } from "./session-native-stateful.mjs";
|
|
16
18
|
import { makeNativeSessionStepUpServices } from "./session-native-step-up.mjs";
|
|
17
19
|
import { makeMappings } from "./storage-mapping.mjs";
|
|
18
|
-
import { validateStorageBatch, withStorageValidation } from "./storage-validation.mjs";
|
|
19
20
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
20
21
|
import { SqlClient } from "effect/sql";
|
|
21
22
|
import { EmailAddressPersistence, EmailUnavailable } from "@yielded/auth/Email";
|
|
22
23
|
import { hasCommitScope } from "@yielded/auth/Hooks";
|
|
24
|
+
import { OAuthConnectedPersistence, OAuthConnectedRevocations, OAuthSignInPersistence, OAuthUnavailable } from "@yielded/auth/OAuth";
|
|
23
25
|
import { PasskeyCredentials, PasskeyManagementPersistence, PasskeyPersistence, PasskeyUnavailable } from "@yielded/auth/Passkey";
|
|
24
26
|
import { PasswordPersistence, PasswordUnavailable } from "@yielded/auth/Password";
|
|
25
|
-
import { hooksLayer } from "@yielded/auth/Persistence";
|
|
27
|
+
import { hooksLayer, reportAuthDiagnostic } from "@yielded/auth/Persistence";
|
|
26
28
|
import { PhoneOtpUnavailable, PhoneSignInTargets } from "@yielded/auth/PhoneOtp";
|
|
27
29
|
import { ProofPersistence, ProofUnavailable } from "@yielded/auth/Proofs";
|
|
28
30
|
import { AuthenticationAuthority, SessionUnavailable } from "@yielded/auth/Sessions";
|
|
@@ -45,27 +47,37 @@ const timestampKeys = /* @__PURE__ */ new Set([
|
|
|
45
47
|
"admittedAt",
|
|
46
48
|
"deadline",
|
|
47
49
|
"occurredAt",
|
|
48
|
-
"dedupUntil"
|
|
50
|
+
"dedupUntil",
|
|
51
|
+
"binderExpiresAt",
|
|
52
|
+
"refreshClaimedAt",
|
|
53
|
+
"refreshClaimExpiresAt",
|
|
54
|
+
"claimedAt",
|
|
55
|
+
"claimExpiresAt"
|
|
49
56
|
]);
|
|
50
57
|
const createPersistence = (backend) => {
|
|
51
58
|
const make = (auth) => {
|
|
52
|
-
const features = Object.values(auth.strategies).
|
|
53
|
-
const phone = features.some((feature) => feature
|
|
54
|
-
const password = features.some((feature) => feature
|
|
55
|
-
const passkeys = features.filter((feature) => feature
|
|
56
|
-
const
|
|
57
|
-
const
|
|
59
|
+
const features = Object.values(auth.strategies).flatMap((strategy) => strategy.persistence === void 0 ? [] : [strategy.persistence]);
|
|
60
|
+
const phone = features.some((feature) => feature.kind === "phone");
|
|
61
|
+
const password = features.some((feature) => feature.kind === "password");
|
|
62
|
+
const passkeys = features.filter((feature) => feature.kind === "passkey");
|
|
63
|
+
const oauth = features.filter((feature) => feature?.kind === "oauth");
|
|
64
|
+
const oauthSignIn = oauth.some((feature) => feature.signIn);
|
|
65
|
+
const oauthConnected = oauth.some((feature) => feature.connected !== void 0);
|
|
66
|
+
const stateful = auth.sessionMode === "stateful";
|
|
67
|
+
const stateless = auth.sessionMode === "stateless" && features.length > 0 && features.every((feature) => feature?.kind === "oauth");
|
|
68
|
+
const management = features.some((feature) => feature.kind === "password" && feature.management);
|
|
69
|
+
const email = features.some((feature) => feature.kind === "email" && feature.addresses);
|
|
58
70
|
const proofs = phone || management || email;
|
|
59
|
-
const roles = [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"sessions",
|
|
63
|
-
"pending"
|
|
64
|
-
];
|
|
71
|
+
const roles = ["identifiers", "credentials"];
|
|
72
|
+
if (stateful) roles.push("sessions");
|
|
73
|
+
roles.push("pending");
|
|
65
74
|
if (password) roles.push("passwords");
|
|
66
75
|
if (email) roles.push("emailCredentials");
|
|
67
76
|
if (passkeys.length > 0) roles.push("passkeyCredentials", "passkeyFlows");
|
|
68
77
|
if (proofs) roles.push("proofs");
|
|
78
|
+
if (oauth.length > 0) roles.push("oauthIdentities", "oauthCredentials");
|
|
79
|
+
if (oauthSignIn) roles.push("oauthSignInFlows");
|
|
80
|
+
if (oauthConnected) roles.push("oauthConnectedFlows", "oauthConnectedGrants", "oauthConnectedRevocations");
|
|
69
81
|
const ConfigKey = Context.Service()(`@yielded/auth-persistence/${auth.namespace}/Config`);
|
|
70
82
|
const Config = Object.assign(ConfigKey, { layer: (value) => Layer.succeed(ConfigKey, value) });
|
|
71
83
|
const ProvisioningKey = Context.Service()(`@yielded/auth-persistence/${auth.namespace}/Provisioning`);
|
|
@@ -130,8 +142,7 @@ const createPersistence = (backend) => {
|
|
|
130
142
|
orElse: () => void 0
|
|
131
143
|
});
|
|
132
144
|
if (dialect === void 0) return yield* configError("Use an explicit adapter for this SQL dialect");
|
|
133
|
-
if (
|
|
134
|
-
if (features.some((feature) => feature === void 0 || "lifecycle" in feature && feature.lifecycle || feature.kind === "email" && !feature.addresses)) return yield* configError("Use explicit services for strategies without composed persistence support");
|
|
145
|
+
if (features.some((feature) => "lifecycle" in feature && feature.lifecycle || feature.kind === "email" && !feature.addresses)) return yield* configError("Use explicit services for strategies without composed persistence support");
|
|
135
146
|
if (storage.namespace !== auth.namespace) return yield* configError("Persistence configuration belongs to a different Auth definition");
|
|
136
147
|
const validations = [];
|
|
137
148
|
for (const role of roles) {
|
|
@@ -152,14 +163,19 @@ const createPersistence = (backend) => {
|
|
|
152
163
|
yield* validateStorageBatch(dialect, validations);
|
|
153
164
|
const nativeTables = backend.nativeTables(database);
|
|
154
165
|
const mappings = yield* makeMappings(storage, nativeTables);
|
|
155
|
-
const sessionMapping = mappings.sessions(auth.claims, auth.sessions.moduleId);
|
|
166
|
+
const sessionMapping = stateful ? mappings.sessions(auth.claims, auth.sessions.moduleId) : void 0;
|
|
156
167
|
const pendingMapping = mappings.pending(auth.claims, auth.sessions.moduleId);
|
|
157
|
-
const
|
|
158
|
-
|
|
168
|
+
const { authenticationAuthority } = yield* makeNativeAuthenticationAuthorityServices(nativeTables, sessionMapping ?? {
|
|
169
|
+
...pendingMapping,
|
|
170
|
+
pending: {
|
|
171
|
+
pending: pendingMapping.pending,
|
|
172
|
+
login: pendingMapping.login
|
|
173
|
+
}
|
|
174
|
+
});
|
|
159
175
|
const { pendingAuthentication } = yield* makeNativePendingAuthenticationServices(nativeTables, pendingMapping);
|
|
160
176
|
const { sessionStepUpPersistence } = yield* makeNativeSessionStepUpServices(nativeTables, {
|
|
161
177
|
...pendingMapping,
|
|
162
|
-
source: {
|
|
178
|
+
source: sessionMapping === void 0 ? { kind: "StatelessSigned" } : {
|
|
163
179
|
kind: "Stateful",
|
|
164
180
|
session: sessionMapping.session,
|
|
165
181
|
sessionId: sessionMapping.sessionId,
|
|
@@ -167,7 +183,11 @@ const createPersistence = (backend) => {
|
|
|
167
183
|
}
|
|
168
184
|
});
|
|
169
185
|
const { sessionCleanup } = yield* makeNativeSessionCleanupServices(nativeTables, pendingMapping);
|
|
170
|
-
let context = Context.make(AuthenticationAuthority, authenticationAuthority).pipe(Context.add(auth.sessions.
|
|
186
|
+
let context = Context.make(AuthenticationAuthority, authenticationAuthority).pipe(Context.add(auth.sessions.PendingAuthentication, pendingAuthentication), Context.add(auth.sessions.SessionStepUpPersistence, sessionStepUpPersistence), Context.add(auth.sessions.SessionCleanup, sessionCleanup));
|
|
187
|
+
if (sessionMapping !== void 0) {
|
|
188
|
+
const sessionServices = yield* makeNativeStatefulSessionServices(nativeTables, sessionMapping);
|
|
189
|
+
context = context.pipe(Context.add(auth.sessions.StatefulSessionPersistence, sessionServices.statefulSessionPersistence), Context.add(auth.sessions.SessionRepository, sessionServices.sessionRepository));
|
|
190
|
+
}
|
|
171
191
|
const proofMapping = proofs ? mappings.proofs() : void 0;
|
|
172
192
|
if (proofMapping !== void 0) {
|
|
173
193
|
const services = yield* makeNativeProofServices(backend.nativeTables(database), proofMapping);
|
|
@@ -186,23 +206,28 @@ const createPersistence = (backend) => {
|
|
|
186
206
|
context = Context.add(context, key, registration);
|
|
187
207
|
}
|
|
188
208
|
if (email) context = Context.add(context, EmailAddressPersistence, (yield* makeNativeEmailAddressServices(backend.nativeTables(database), mappings.emails(), proofMapping)).emailAddressPersistence);
|
|
189
|
-
if (phone) context = Context.add(context, PhoneSignInTargets, yield* makeComposedPhoneTargets(backend.nativeTables(database), storage, mappings.proofs(), features.flatMap((feature) => feature
|
|
209
|
+
if (phone) context = Context.add(context, PhoneSignInTargets, yield* makeComposedPhoneTargets(backend.nativeTables(database), storage, mappings.proofs(), features.flatMap((feature) => feature.kind === "phone" ? [feature.moduleId] : [])));
|
|
190
210
|
if (passkeys.length > 0) {
|
|
191
211
|
const services = makeManagedPasskeys({
|
|
192
212
|
storage,
|
|
193
213
|
namespace: auth.namespace,
|
|
194
214
|
dialect,
|
|
195
215
|
features: passkeys,
|
|
196
|
-
passwordModules: features.flatMap((feature) => feature
|
|
216
|
+
passwordModules: features.flatMap((feature) => feature.kind === "password" ? [feature.moduleId] : [])
|
|
197
217
|
}, backend.nativeTables(database));
|
|
198
218
|
context = Context.merge(context, yield* services);
|
|
199
219
|
}
|
|
220
|
+
if (oauth.length > 0) context = Context.merge(context, yield* makeManagedOAuth(storage, nativeTables, oauth));
|
|
200
221
|
return context;
|
|
201
|
-
}).pipe(Effect.provideService(SqlBatchCommit, backend.batch?.(database)))).pipe(withStorageValidation
|
|
222
|
+
}).pipe(Effect.provideService(SqlBatchCommit, backend.batch?.(database)))).pipe(withStorageValidation)).pipe(Layer.provide(hooksLayer));
|
|
202
223
|
return {
|
|
203
224
|
Config,
|
|
204
225
|
Provisioning: ProvisioningKey,
|
|
205
226
|
layer: Layer.effectContext(Effect.gen(function* () {
|
|
227
|
+
if (!stateful && !stateless) {
|
|
228
|
+
if (auth.sessionMode === "stateless") return yield* configError("Composed stateless sessions support only OAuth strategies; use explicit persistence for other strategies");
|
|
229
|
+
return yield* configError("Composed persistence supports stateful or OAuth-only stateless sessions; state-assisted validity and other session modes require explicit persistence");
|
|
230
|
+
}
|
|
206
231
|
const scope = yield* Effect.scope;
|
|
207
232
|
const captured = yield* Effect.context();
|
|
208
233
|
const client = Context.get(captured, SqlClient.SqlClient);
|
|
@@ -212,16 +237,13 @@ const createPersistence = (backend) => {
|
|
|
212
237
|
if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
|
|
213
238
|
yield* requireStandalone(unavailable, client.transactionService);
|
|
214
239
|
}
|
|
215
|
-
const acquired = yield* initialize.pipe(Effect.mapError(unavailable));
|
|
240
|
+
const acquired = yield* initialize.pipe(Effect.tapError((error) => reportAuthDiagnostic("persistence-initialization", Schema.is(PersistenceConfigurationError)(error) ? "configuration" : Schema.is(PersistenceMappingError)(error) ? "mapping" : "unavailable")), Effect.mapError(unavailable));
|
|
216
241
|
const value = Context.getOrUndefined(acquired, key);
|
|
217
242
|
return value === void 0 ? yield* Effect.fail(unavailable()) : value;
|
|
218
243
|
});
|
|
219
244
|
const sessionUnavailable = () => SessionUnavailable.make({});
|
|
220
245
|
const authority = service(AuthenticationAuthority, sessionUnavailable, false);
|
|
221
246
|
const authorityMutation = service(AuthenticationAuthority, sessionUnavailable);
|
|
222
|
-
const sessions = service(auth.sessions.StatefulSessionPersistence, sessionUnavailable, false);
|
|
223
|
-
const sessionMutations = service(auth.sessions.StatefulSessionPersistence, sessionUnavailable);
|
|
224
|
-
const repository = service(auth.sessions.SessionRepository, sessionUnavailable, false);
|
|
225
247
|
const pendingRead = service(auth.sessions.PendingAuthentication, sessionUnavailable, false);
|
|
226
248
|
const pendingMutation = service(auth.sessions.PendingAuthentication, sessionUnavailable);
|
|
227
249
|
const stepUpRead = service(auth.sessions.SessionStepUpPersistence, sessionUnavailable, false);
|
|
@@ -231,14 +253,7 @@ const createPersistence = (backend) => {
|
|
|
231
253
|
capture: (subjectId, credentialIds) => Effect.flatMap(authority, (s) => s.capture(subjectId, credentialIds)),
|
|
232
254
|
requirements: (evidence) => Effect.flatMap(authority, (s) => s.requirements(evidence)),
|
|
233
255
|
approve: (input, prepare) => Effect.flatMap(authorityMutation, (s) => s.approve(input, prepare))
|
|
234
|
-
}).pipe(Context.add(auth.sessions.
|
|
235
|
-
establish: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.establish(input, prepare)),
|
|
236
|
-
verify: (input) => Effect.flatMap(sessions, (s) => s.verify(input)),
|
|
237
|
-
rotate: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.rotate(input, prepare)),
|
|
238
|
-
revokeDigest: (digest, prepare) => Effect.flatMap(sessionMutations, (s) => s.revokeDigest(digest, prepare)),
|
|
239
|
-
revoke: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.revoke(input, prepare)),
|
|
240
|
-
revokeAll: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.revokeAll(input, prepare))
|
|
241
|
-
}), Context.add(auth.sessions.SessionRepository, { list: (input) => Effect.flatMap(repository, (s) => s.list(input)) }), Context.add(auth.sessions.PendingAuthentication, {
|
|
256
|
+
}).pipe(Context.add(auth.sessions.PendingAuthentication, {
|
|
242
257
|
create: (input, now, prepare) => Effect.flatMap(pendingMutation, (s) => s.create(input, now, prepare)),
|
|
243
258
|
read: (input) => Effect.flatMap(pendingRead, (s) => s.read(input)),
|
|
244
259
|
reject: (input, prepare) => Effect.flatMap(pendingMutation, (s) => s.reject(input, prepare))
|
|
@@ -248,6 +263,19 @@ const createPersistence = (backend) => {
|
|
|
248
263
|
reject: (input, prepare) => Effect.flatMap(stepUpMutation, (s) => s.reject(input, prepare)),
|
|
249
264
|
complete: (plan, prepare) => Effect.flatMap(stepUpMutation, (s) => s.complete(plan, prepare))
|
|
250
265
|
}), Context.add(auth.sessions.SessionCleanup, { cleanup: (input) => Effect.flatMap(cleanup, (s) => s.cleanup(input)) }));
|
|
266
|
+
if (stateful) {
|
|
267
|
+
const sessions = service(auth.sessions.StatefulSessionPersistence, sessionUnavailable, false);
|
|
268
|
+
const sessionMutations = service(auth.sessions.StatefulSessionPersistence, sessionUnavailable);
|
|
269
|
+
const repository = service(auth.sessions.SessionRepository, sessionUnavailable, false);
|
|
270
|
+
context = context.pipe(Context.add(auth.sessions.StatefulSessionPersistence, {
|
|
271
|
+
establish: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.establish(input, prepare)),
|
|
272
|
+
verify: (input) => Effect.flatMap(sessions, (s) => s.verify(input)),
|
|
273
|
+
rotate: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.rotate(input, prepare)),
|
|
274
|
+
revokeDigest: (digest, prepare) => Effect.flatMap(sessionMutations, (s) => s.revokeDigest(digest, prepare)),
|
|
275
|
+
revoke: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.revoke(input, prepare)),
|
|
276
|
+
revokeAll: (input, prepare) => Effect.flatMap(sessionMutations, (s) => s.revokeAll(input, prepare))
|
|
277
|
+
}), Context.add(auth.sessions.SessionRepository, { list: (input) => Effect.flatMap(repository, (s) => s.list(input)) }));
|
|
278
|
+
}
|
|
251
279
|
if (proofs) {
|
|
252
280
|
const persistence = service(ProofPersistence, () => ProofUnavailable.make({}));
|
|
253
281
|
context = Context.add(context, ProofPersistence, {
|
|
@@ -313,6 +341,37 @@ const createPersistence = (backend) => {
|
|
|
313
341
|
}));
|
|
314
342
|
}
|
|
315
343
|
}
|
|
344
|
+
const oauthUnavailable = () => OAuthUnavailable.make({});
|
|
345
|
+
function oauthService(key, moduleId, capability) {
|
|
346
|
+
return oauth.some((feature) => feature.moduleId === moduleId && (capability === "signIn" ? feature.signIn : feature.connected !== void 0)) ? service(key, oauthUnavailable) : Effect.fail(oauthUnavailable());
|
|
347
|
+
}
|
|
348
|
+
if (oauthSignIn) {
|
|
349
|
+
const persistence = (moduleId) => oauthService(OAuthSignInPersistence, moduleId, "signIn");
|
|
350
|
+
context = Context.add(context, OAuthSignInPersistence, {
|
|
351
|
+
issue: (input, prepare) => Effect.flatMap(persistence(input.context.moduleId), (s) => s.issue(input, prepare)),
|
|
352
|
+
consume: (input, prepare) => Effect.flatMap(persistence(input.moduleId), (s) => s.consume(input, prepare)),
|
|
353
|
+
resolve: (input) => Effect.flatMap(persistence(input.moduleId), (s) => s.resolve(input)),
|
|
354
|
+
cleanup: (input, prepare) => Effect.flatMap(persistence(input.moduleId), (s) => s.cleanup(input, prepare))
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (oauthConnected) {
|
|
358
|
+
const persistence = (moduleId) => oauthService(OAuthConnectedPersistence, moduleId, "connected");
|
|
359
|
+
const revocations = (moduleId) => oauthService(OAuthConnectedRevocations, moduleId, "connected");
|
|
360
|
+
context = context.pipe(Context.add(OAuthConnectedPersistence, {
|
|
361
|
+
read: (input) => Effect.flatMap(persistence(input.moduleId), (s) => s.read(input)),
|
|
362
|
+
issue: (input, prepare) => Effect.flatMap(persistence(input.context.moduleId), (s) => s.issue(input, prepare)),
|
|
363
|
+
consume: (input, prepare) => Effect.flatMap(persistence(input.moduleId), (s) => s.consume(input, prepare)),
|
|
364
|
+
settle: (input, prepare) => Effect.flatMap(persistence(input.grant.context.moduleId), (s) => s.settle(input, prepare)),
|
|
365
|
+
list: (input) => Effect.flatMap(persistence(input.authorization.moduleId), (s) => s.list(input)),
|
|
366
|
+
disconnect: (input, prepare) => Effect.flatMap(persistence(input.key.moduleId), (s) => s.disconnect(input, prepare)),
|
|
367
|
+
claimRefresh: (input, prepare) => Effect.flatMap(persistence(input.grant.context.moduleId), (s) => s.claimRefresh(input, prepare)),
|
|
368
|
+
settleRefresh: (input, prepare) => Effect.flatMap(persistence(input.claim.grant.context.moduleId), (s) => s.settleRefresh(input, prepare)),
|
|
369
|
+
cleanup: (input, prepare) => Effect.flatMap(persistence(input.moduleId), (s) => s.cleanup(input, prepare))
|
|
370
|
+
}), Context.add(OAuthConnectedRevocations, {
|
|
371
|
+
claim: (input, prepare) => Effect.flatMap(revocations(input.moduleId), (s) => s.claim(input, prepare)),
|
|
372
|
+
settle: (input, prepare) => Effect.flatMap(revocations(input.claim.job.grant.context.moduleId), (s) => s.settle(input, prepare))
|
|
373
|
+
}));
|
|
374
|
+
}
|
|
316
375
|
return context;
|
|
317
376
|
})),
|
|
318
377
|
managed: (options) => layout(options, true),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.mjs","names":[],"sources":["../../src/internal/persistence.ts"],"sourcesContent":["import { EmailAddressPersistence, EmailUnavailable } from \"@yielded/auth/Email\";\nimport type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { hasCommitScope } from \"@yielded/auth/Hooks\";\nimport {\n PasskeyCredentials,\n PasskeyManagementPersistence,\n PasskeyPersistence,\n PasskeyUnavailable,\n} from \"@yielded/auth/Passkey\";\nimport { PasswordPersistence, PasswordUnavailable } from \"@yielded/auth/Password\";\nimport { hooksLayer } from \"@yielded/auth/Persistence\";\nimport { PhoneSignInTargets, PhoneOtpUnavailable } from \"@yielded/auth/PhoneOtp\";\nimport { ProofPersistence, ProofUnavailable } from \"@yielded/auth/Proofs\";\nimport { AuthenticationAuthority, SessionUnavailable } from \"@yielded/auth/Sessions\";\nimport { Context, type Crypto, Effect, Layer, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport {\n PersistenceConfigurationError,\n type ClaimsCodec,\n type ConfigId,\n type Definition,\n type PersistenceApi,\n type Ports,\n type Roles,\n type Provisioning,\n type ProvisioningRequirement,\n type StorageLayout,\n type SubjectMapping,\n type SubjectOptions,\n type PasskeyFeature,\n type PasskeyRequirement,\n} from \"./configuration\";\nimport { makeNativeEmailAddressServices } from \"./email-native\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeManagedPasskeys } from \"./passkey-managed\";\nimport { makeNativePasswordServices } from \"./password-native\";\nimport { makeComposedPhoneTargets } from \"./phone-composed\";\nimport { makeNativeProofServices } from \"./proof-native\";\nimport { makeRegistrationAuthority } from \"./registration\";\nimport type { PasswordRegistrationAuthority } from \"./registration-contract\";\nimport { makeNativeAuthenticationAuthorityServices } from \"./session-native-authority\";\nimport { makeNativeSessionCleanupServices } from \"./session-native-cleanup\";\nimport { makeNativePendingAuthenticationServices } from \"./session-native-login\";\nimport { makeNativeStatefulSessionServices } from \"./session-native-stateful\";\nimport { makeNativeSessionStepUpServices } from \"./session-native-step-up\";\nimport { SqlBatchCommit } from \"./sql-commit\";\nimport { requireStandalone } from \"./standalone\";\nimport { makeMappings } from \"./storage-mapping\";\nimport {\n tableDefinition,\n storageTables,\n type StorageRole,\n type StorageTable,\n} from \"./storage-tables\";\nimport {\n validateStorageBatch,\n withStorageValidation,\n type StorageValidation,\n} from \"./storage-validation\";\n\nexport interface Backend<T extends object, R, Database extends object = object> {\n readonly makeTable: (definition: StorageTable) => T;\n readonly describe: (table: T) => StorageTable;\n readonly acquire: Effect.Effect<Database, PersistenceConfigurationError, R | SqlClient.SqlClient>;\n readonly nativeTables: (database: Database) => NativeSqlTables;\n /** Fixed atomic batches replace interactive transactions on backends such as D1. */\n readonly batch?: (database: Database) => NonNullable<SqlBatchCommit[\"Service\"]>;\n}\n\nconst configError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappingError = (cause: unknown) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause });\n\nconst timestampKeys = new Set([\n \"verifiedAt\",\n \"createdAt\",\n \"lastIssueAt\",\n \"issuedAt\",\n \"expiresAt\",\n \"absoluteExpiresAt\",\n \"retentionUntil\",\n \"claimDeadline\",\n \"retryAt\",\n \"admittedAt\",\n \"deadline\",\n \"occurredAt\",\n \"dedupUntil\",\n]);\n\nexport const createPersistence = <T extends object, R, Database extends object = object>(\n backend: Backend<T, R, Database>,\n): PersistenceApi<T, R> => {\n const make = <C extends ClaimsCodec, const Id extends string, const A extends Definition<C, Id>>(\n auth: A & Definition<C, Id>,\n ) => {\n const features = Object.values(auth.strategies).map((strategy) => strategy.persistence);\n const phone = features.some((feature) => feature?.kind === \"phone\");\n const password = features.some((feature) => feature?.kind === \"password\");\n\n const passkeys = features.filter(\n (feature): feature is PasskeyFeature => feature?.kind === \"passkey\",\n );\n\n const management = features.some(\n (feature) => feature?.kind === \"password\" && feature.management,\n );\n\n const email = features.some((feature) => feature?.kind === \"email\" && feature.addresses);\n const proofs = phone || management || email;\n\n const roles: StorageRole[] = [\"identifiers\", \"credentials\", \"sessions\", \"pending\"];\n\n if (password) roles.push(\"passwords\");\n if (email) roles.push(\"emailCredentials\");\n if (passkeys.length > 0) roles.push(\"passkeyCredentials\", \"passkeyFlows\");\n if (proofs) roles.push(\"proofs\");\n\n const ConfigKey = Context.Service<\n ConfigId<A[\"namespace\"]>,\n StorageLayout<T, Roles<C, Id, A>>\n >()(`@yielded/auth-persistence/${auth.namespace}/Config`);\n\n const Config = Object.assign(ConfigKey, {\n layer: (value: StorageLayout<T, Roles<C, Id, A>>) => Layer.succeed(ConfigKey, value),\n });\n\n const ProvisioningKey = Context.Service<ProvisioningRequirement<A>, Provisioning<A>>()(\n `@yielded/auth-persistence/${auth.namespace}/Provisioning`,\n );\n\n const layout = <N, Instant>(\n options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<StorageRole, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n },\n managed: boolean,\n ): StorageLayout<T, Roles<C, Id, A>> => {\n const subject = options.subjects;\n\n if (passkeys.length > 0 && options.timestamps !== undefined)\n throw configError(\n \"Composed passkey storage uses integer milliseconds; use an explicit passkey adapter for a custom timestamp representation\",\n );\n const description = backend.describe(subject.table);\n const idColumn = description.columns[subject.id];\n\n for (const key of [subject.id, subject.status, subject.securityRevision]) {\n if (description.columns[key] === undefined)\n throw configError(`Missing subject column ${key}`);\n }\n if (subject.activeValue === undefined)\n throw configError(\"An explicit active subject value is required\");\n\n if (idColumn === undefined || idColumn.type === \"boolean\")\n throw configError(\"Subject ID must map a text or integer column\");\n\n const prefix = options.prefix;\n\n if (managed && (prefix === undefined || !/^[A-Za-z_][A-Za-z0-9_]{0,100}$/.test(prefix)))\n throw configError(\"Managed persistence requires an explicit SQL table prefix\");\n\n const schema: Partial<Record<StorageRole, T>> = { ...options.tables };\n\n for (const role of roles) {\n if (schema[role] === undefined && managed) {\n const definition = tableDefinition(role, `${prefix}_${role}`, idColumn.type);\n\n const adjusted =\n options.timestamps === undefined\n ? definition\n : {\n ...definition,\n columns: Object.fromEntries(\n Object.entries(definition.columns).map(([key, column]) => [\n key,\n timestampKeys.has(key)\n ? { ...column, type: options.timestamps?.type ?? column.type }\n : column,\n ]),\n ),\n };\n\n schema[role] = backend.makeTable(adjusted);\n }\n }\n for (const role of roles) {\n if (schema[role] === undefined) throw configError(`Missing ${role} table`);\n }\n const instantCodec = options.timestamps?.codec;\n\n const decodeInstant =\n instantCodec === undefined\n ? Schema.decodeUnknownEffect(Schema.Int)\n : Schema.decodeUnknownEffect(instantCodec);\n\n const decodeInstantSync =\n instantCodec === undefined\n ? Schema.decodeUnknownSync(Schema.Int)\n : Schema.decodeUnknownSync(instantCodec);\n\n const subjects: SubjectMapping = {\n table: subject.table,\n id: subject.id,\n status: subject.status,\n securityRevision: subject.securityRevision,\n activeValue: subject.activeValue,\n requirements: subject.requirements,\n actionRequirements: subject.actionRequirements ?? subject.requirements,\n toSubject: (id) =>\n Schema.decodeUnknownEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toNative: (id) =>\n Schema.encodeEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toSubjectSync: Schema.decodeUnknownSync(subject.idCodec),\n toNativeSync: Schema.encodeSync(subject.idCodec),\n };\n\n return Object.freeze({\n namespace: auth.namespace,\n schema: Object.freeze(schema) as Readonly<Record<Roles<C, Id, A>, T>>,\n tables: schema,\n subjects,\n encodeInstant:\n instantCodec === undefined ? (millis: number) => millis : Schema.encodeSync(instantCodec),\n decodeInstant: (value: unknown) => decodeInstant(value).pipe(Effect.mapError(mappingError)),\n decodeInstantSync,\n });\n };\n\n const services = Layer.effectContext(\n Effect.flatMap(backend.acquire, (database) =>\n Effect.gen(function* () {\n const storage = yield* ConfigKey;\n const client = yield* SqlClient.SqlClient;\n\n const dialect = client.onDialectOrElse({\n pg: () => \"pg\" as const,\n sqlite: () => \"sqlite\" as const,\n orElse: () => undefined,\n });\n\n if (dialect === undefined)\n return yield* configError(\"Use an explicit adapter for this SQL dialect\");\n if (auth.sessionMode !== \"stateful\")\n return yield* configError(\"The composed layer currently requires stateful sessions\");\n if (\n features.some(\n (feature) =>\n feature === undefined ||\n (\"lifecycle\" in feature && feature.lifecycle) ||\n (feature.kind === \"email\" && !feature.addresses),\n )\n )\n return yield* configError(\n \"Use explicit services for strategies without composed persistence support\",\n );\n if (storage.namespace !== auth.namespace)\n return yield* configError(\n \"Persistence configuration belongs to a different Auth definition\",\n );\n const validations: StorageValidation[] = [];\n\n for (const role of roles) {\n const table = (storage.schema as Partial<Record<StorageRole, T>>)[role];\n\n if (table === undefined) return yield* configError(`Missing ${role} table`);\n const description = backend.describe(table);\n const columns = description.columns;\n\n for (const name of Object.keys(storageTables[role].columns)) {\n if (columns[name] === undefined)\n return yield* configError(`Missing ${role}.${name} column`);\n }\n validations.push({ table: description, required: storageTables[role].unique });\n }\n validations.push({\n table: backend.describe(storage.subjects.table as T),\n required: [[storage.subjects.id]],\n });\n yield* validateStorageBatch(dialect, validations);\n const nativeTables = backend.nativeTables(database);\n const mappings = yield* makeMappings(storage, nativeTables);\n const sessionMapping = mappings.sessions(auth.claims, auth.sessions.moduleId);\n const pendingMapping = mappings.pending(auth.claims, auth.sessions.moduleId);\n\n const sessionServices = yield* makeNativeStatefulSessionServices(\n nativeTables,\n sessionMapping,\n );\n\n const { authenticationAuthority } = yield* makeNativeAuthenticationAuthorityServices(\n nativeTables,\n sessionMapping,\n );\n\n const { pendingAuthentication } = yield* makeNativePendingAuthenticationServices(\n nativeTables,\n pendingMapping,\n );\n\n const { sessionStepUpPersistence } = yield* makeNativeSessionStepUpServices(\n nativeTables,\n {\n ...pendingMapping,\n source: {\n kind: \"Stateful\",\n session: sessionMapping.session,\n sessionId: sessionMapping.sessionId,\n constraints: { sessionDigest: \"unique(session.digest)\" },\n },\n },\n );\n\n const { sessionCleanup } = yield* makeNativeSessionCleanupServices(\n nativeTables,\n pendingMapping,\n );\n\n let context: Context.Context<never> = Context.make(\n AuthenticationAuthority,\n authenticationAuthority,\n ).pipe(\n Context.add(\n auth.sessions.StatefulSessionPersistence,\n sessionServices.statefulSessionPersistence,\n ),\n Context.add(auth.sessions.SessionRepository, sessionServices.sessionRepository),\n Context.add(auth.sessions.PendingAuthentication, pendingAuthentication),\n Context.add(auth.sessions.SessionStepUpPersistence, sessionStepUpPersistence),\n Context.add(auth.sessions.SessionCleanup, sessionCleanup),\n );\n\n const proofMapping = proofs ? mappings.proofs() : undefined;\n\n if (proofMapping !== undefined) {\n const services = yield* makeNativeProofServices(\n backend.nativeTables(database),\n proofMapping,\n );\n\n context = Context.add(context, ProofPersistence, services.proofPersistence);\n }\n\n if (password) {\n const mapping = mappings.passwords();\n\n const services = yield* makeNativePasswordServices(\n backend.nativeTables(database),\n mapping,\n proofMapping,\n );\n\n context = Context.add(context, PasswordPersistence, services.passwordPersistence);\n }\n for (const [name, strategy] of Object.entries(auth.strategies)) {\n if (strategy.persistence?.kind !== \"password\" || !strategy.persistence.management)\n continue;\n const key = strategy.RegistrationAuthority;\n\n if (key === undefined)\n return yield* configError(`Missing subject provisioning for ${name}`);\n\n const registration = yield* makeRegistrationAuthority(\n backend.nativeTables(database),\n mappings.passwords(),\n ProvisioningKey,\n name,\n );\n\n context = Context.add(context, key, registration);\n }\n if (email)\n context = Context.add(\n context,\n EmailAddressPersistence,\n (yield* makeNativeEmailAddressServices(\n backend.nativeTables(database),\n mappings.emails(),\n proofMapping,\n )).emailAddressPersistence,\n );\n\n if (phone)\n context = Context.add(\n context,\n PhoneSignInTargets,\n yield* makeComposedPhoneTargets(\n backend.nativeTables(database),\n storage,\n mappings.proofs(),\n features.flatMap((feature) =>\n feature?.kind === \"phone\" ? [feature.moduleId] : [],\n ),\n ),\n );\n\n if (passkeys.length > 0) {\n // Auth capability metadata determines whether PasskeyConfig is required.\n const services = makeManagedPasskeys(\n {\n storage,\n namespace: auth.namespace,\n dialect,\n features: passkeys,\n passwordModules: features.flatMap((feature) =>\n feature?.kind === \"password\" ? [feature.moduleId] : [],\n ),\n },\n backend.nativeTables(database),\n ) as Effect.Effect<\n Context.Context<never>,\n PersistenceConfigurationError,\n | PasskeyRequirement<A>\n | Crypto.Crypto\n | LifecycleHooks\n | SqlClient.SqlClient\n | SqlBatchCommit\n >;\n\n context = Context.merge(context, yield* services);\n }\n\n // The checked capability metadata above determines exactly these service keys.\n return context as Context.Context<Ports<C, Id, A>>;\n }).pipe(Effect.provideService(SqlBatchCommit, backend.batch?.(database))),\n ).pipe(\n withStorageValidation,\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : configError(\"Cannot acquire SQL persistence\"),\n ),\n ),\n ).pipe(Layer.provide(hooksLayer));\n\n const layer = Layer.effectContext(\n Effect.gen(function* () {\n const scope = yield* Effect.scope;\n const captured = yield* Effect.context<Layer.Services<typeof services>>();\n const client = Context.get(captured, SqlClient.SqlClient);\n\n // Construction uses the layer's exact environment and lifetime, never the\n // first operation's SQL transaction, service overrides, or request scope.\n // Cache the full exit: concurrent callers share failures/interruption too,\n // and cannot retry partially completed setup within this acquired layer.\n const initialize = yield* Effect.cached(\n Layer.buildWithScope(services, scope).pipe(Effect.setContext(captured)),\n );\n\n const service = Effect.fnUntraced(function* <I, S, Failure>(\n key: Context.Key<I, S>,\n unavailable: () => Failure,\n standalone = true,\n ): Effect.fn.Return<S, Failure> {\n // Reject unsupported ownership before setup can perform any writes.\n // Session/authority reads retain their existing ambient-read semantics.\n if (standalone) {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* requireStandalone(unavailable, client.transactionService);\n }\n const acquired = yield* initialize.pipe(Effect.mapError(unavailable));\n const value = Context.getOrUndefined(acquired, key);\n\n return value === undefined ? yield* Effect.fail(unavailable()) : value;\n });\n\n const sessionUnavailable = () => SessionUnavailable.make({});\n const authority = service(AuthenticationAuthority, sessionUnavailable, false);\n const authorityMutation = service(AuthenticationAuthority, sessionUnavailable);\n\n const sessions = service(\n auth.sessions.StatefulSessionPersistence,\n sessionUnavailable,\n false,\n );\n\n const sessionMutations = service(\n auth.sessions.StatefulSessionPersistence,\n sessionUnavailable,\n );\n\n const repository = service(auth.sessions.SessionRepository, sessionUnavailable, false);\n const pendingRead = service(auth.sessions.PendingAuthentication, sessionUnavailable, false);\n const pendingMutation = service(auth.sessions.PendingAuthentication, sessionUnavailable);\n\n const stepUpRead = service(\n auth.sessions.SessionStepUpPersistence,\n sessionUnavailable,\n false,\n );\n\n const stepUpMutation = service(auth.sessions.SessionStepUpPersistence, sessionUnavailable);\n const cleanup = service(auth.sessions.SessionCleanup, sessionUnavailable);\n\n let context: Context.Context<never> = Context.make(AuthenticationAuthority, {\n capture: (subjectId, credentialIds) =>\n Effect.flatMap(authority, (s) => s.capture(subjectId, credentialIds)),\n requirements: (evidence) => Effect.flatMap(authority, (s) => s.requirements(evidence)),\n approve: (input, prepare) =>\n Effect.flatMap(authorityMutation, (s) => s.approve(input, prepare)),\n }).pipe(\n Context.add(auth.sessions.StatefulSessionPersistence, {\n establish: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.establish(input, prepare)),\n verify: (input) => Effect.flatMap(sessions, (s) => s.verify(input)),\n rotate: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.rotate(input, prepare)),\n revokeDigest: (digest, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.revokeDigest(digest, prepare)),\n revoke: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.revoke(input, prepare)),\n revokeAll: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.revokeAll(input, prepare)),\n }),\n Context.add(auth.sessions.SessionRepository, {\n list: (input) => Effect.flatMap(repository, (s) => s.list(input)),\n }),\n Context.add(auth.sessions.PendingAuthentication, {\n create: (input, now, prepare) =>\n Effect.flatMap(pendingMutation, (s) => s.create(input, now, prepare)),\n read: (input) => Effect.flatMap(pendingRead, (s) => s.read(input)),\n reject: (input, prepare) =>\n Effect.flatMap(pendingMutation, (s) => s.reject(input, prepare)),\n }),\n Context.add(auth.sessions.SessionStepUpPersistence, {\n create: (input, now, prepare) =>\n Effect.flatMap(stepUpMutation, (s) => s.create(input, now, prepare)),\n read: (input) => Effect.flatMap(stepUpRead, (s) => s.read(input)),\n reject: (input, prepare) =>\n Effect.flatMap(stepUpMutation, (s) => s.reject(input, prepare)),\n complete: (plan, prepare) =>\n Effect.flatMap(stepUpMutation, (s) => s.complete(plan, prepare)),\n }),\n Context.add(auth.sessions.SessionCleanup, {\n cleanup: (input) => Effect.flatMap(cleanup, (s) => s.cleanup(input)),\n }),\n );\n\n if (proofs) {\n const persistence = service(ProofPersistence, () => ProofUnavailable.make({}));\n\n context = Context.add(context, ProofPersistence, {\n issue: (input, prepare) => Effect.flatMap(persistence, (s) => s.issue(input, prepare)),\n redeem: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.redeem(input, prepare)),\n cancel: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.cancel(input, prepare)),\n cleanup: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.cleanup(input, prepare)),\n });\n }\n if (password) {\n const persistence = service(PasswordPersistence, () => PasswordUnavailable.make({}));\n\n context = Context.add(context, PasswordPersistence, {\n findCredential: (input) => Effect.flatMap(persistence, (s) => s.findCredential(input)),\n rehashIfCurrent: (input) =>\n Effect.flatMap(persistence, (s) => s.rehashIfCurrent(input)),\n readForSubject: (input) => Effect.flatMap(persistence, (s) => s.readForSubject(input)),\n recoveryTarget: (input) => Effect.flatMap(persistence, (s) => s.recoveryTarget(input)),\n addIfAbsent: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.addIfAbsent(input, prepare)),\n replaceIfCurrent: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.replaceIfCurrent(input, prepare)),\n resetWithProof: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.resetWithProof(input, prepare)),\n });\n }\n for (const [name, strategy] of Object.entries(auth.strategies)) {\n if (strategy.persistence?.kind !== \"password\" || !strategy.persistence.management)\n continue;\n const key = strategy.RegistrationAuthority;\n\n if (key === undefined)\n return yield* configError(`Missing subject provisioning for ${name}`);\n\n // The heterogeneous strategy table erases the registration input type;\n // its decoded value is forwarded unchanged to the same authority key.\n const registration = service(\n key as Context.Key<unknown, PasswordRegistrationAuthority<unknown>>,\n () => PasswordUnavailable.make({}),\n );\n\n context = Context.add(context, key, {\n register: (input, prepare) =>\n Effect.flatMap(registration, (s) => s.register(input, prepare)),\n } satisfies PasswordRegistrationAuthority<unknown>);\n }\n if (email) {\n const persistence = service(EmailAddressPersistence, () => EmailUnavailable.make({}));\n\n context = Context.add(context, EmailAddressPersistence, {\n target: (input) => Effect.flatMap(persistence, (s) => s.target(input)),\n verifyWithProof: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.verifyWithProof(input, prepare)),\n changeWithProof: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.changeWithProof(input, prepare)),\n });\n }\n if (phone) {\n const unavailable = () => PhoneOtpUnavailable.make({});\n const targets = service(PhoneSignInTargets, unavailable);\n\n context = context.pipe(\n Context.add(PhoneSignInTargets, {\n lookup: (input) => Effect.flatMap(targets, (s) => s.lookup(input)),\n }),\n );\n }\n if (passkeys.length > 0) {\n const unavailable = () => PasskeyUnavailable.make({});\n const credentials = service(PasskeyCredentials, unavailable);\n const persistence = service(PasskeyPersistence, unavailable);\n\n context = context.pipe(\n Context.add(PasskeyCredentials, {\n lookup: (input) => Effect.flatMap(credentials, (s) => s.lookup(input)),\n listForSubject: (input) =>\n Effect.flatMap(credentials, (s) => s.listForSubject(input)),\n }),\n Context.add(PasskeyPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.issue(input, prepare)),\n context: (input) => Effect.flatMap(persistence, (s) => s.context(input)),\n consume: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.consume(input, prepare)),\n cleanup: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.cleanup(input, prepare)),\n }),\n );\n if (passkeys.some((feature) => feature.management)) {\n const manager = service(PasskeyManagementPersistence, unavailable);\n\n context = context.pipe(\n Context.add(PasskeyManagementPersistence, {\n list: (input) => Effect.flatMap(manager, (s) => s.list(input)),\n completeEnrollment: (input, prepare) =>\n Effect.flatMap(manager, (s) => s.completeEnrollment(input, prepare)),\n inspectRemove: (input) => Effect.flatMap(manager, (s) => s.inspectRemove(input)),\n remove: (input, prepare) =>\n Effect.flatMap(manager, (s) => s.remove(input, prepare)),\n rename: (input, prepare) =>\n Effect.flatMap(manager, (s) => s.rename(input, prepare)),\n }),\n );\n }\n }\n\n // Match the same capability-selected service keys as the acquired graph.\n return context as Context.Context<Ports<C, Id, A>>;\n }),\n );\n\n return {\n Config,\n Provisioning: ProvisioningKey,\n layer,\n managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, true),\n map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Partial<Record<StorageRole, T>>;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, false),\n };\n };\n\n return { make };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persistence.mjs","names":[],"sources":["../../src/internal/persistence.ts"],"sourcesContent":["import { EmailAddressPersistence, EmailUnavailable } from \"@yielded/auth/Email\";\nimport type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport { hasCommitScope } from \"@yielded/auth/Hooks\";\nimport {\n OAuthConnectedPersistence,\n OAuthConnectedRevocations,\n OAuthSignInPersistence,\n OAuthUnavailable,\n} from \"@yielded/auth/OAuth\";\nimport {\n PasskeyCredentials,\n PasskeyManagementPersistence,\n PasskeyPersistence,\n PasskeyUnavailable,\n} from \"@yielded/auth/Passkey\";\nimport { PasswordPersistence, PasswordUnavailable } from \"@yielded/auth/Password\";\nimport { hooksLayer, reportAuthDiagnostic } from \"@yielded/auth/Persistence\";\nimport { PhoneSignInTargets, PhoneOtpUnavailable } from \"@yielded/auth/PhoneOtp\";\nimport { ProofPersistence, ProofUnavailable } from \"@yielded/auth/Proofs\";\nimport { AuthenticationAuthority, SessionUnavailable } from \"@yielded/auth/Sessions\";\nimport { Context, type Crypto, Effect, Layer, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport {\n PersistenceConfigurationError,\n type ClaimsCodec,\n type ConfigId,\n type Definition,\n type PersistenceApi,\n type Ports,\n type Roles,\n type Provisioning,\n type ProvisioningRequirement,\n type StorageLayout,\n type SubjectMapping,\n type SubjectOptions,\n type PasskeyFeature,\n type PasskeyRequirement,\n type OAuthFeature,\n} from \"./configuration\";\nimport { makeNativeEmailAddressServices } from \"./email-native\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeManagedOAuth } from \"./oauth-managed\";\nimport { makeManagedPasskeys } from \"./passkey-managed\";\nimport { makeNativePasswordServices } from \"./password-native\";\nimport { makeComposedPhoneTargets } from \"./phone-composed\";\nimport { makeNativeProofServices } from \"./proof-native\";\nimport { makeRegistrationAuthority } from \"./registration\";\nimport type { PasswordRegistrationAuthority } from \"./registration-contract\";\nimport { makeNativeAuthenticationAuthorityServices } from \"./session-native-authority\";\nimport { makeNativeSessionCleanupServices } from \"./session-native-cleanup\";\nimport { makeNativePendingAuthenticationServices } from \"./session-native-login\";\nimport { makeNativeStatefulSessionServices } from \"./session-native-stateful\";\nimport { makeNativeSessionStepUpServices } from \"./session-native-step-up\";\nimport { SqlBatchCommit } from \"./sql-commit\";\nimport { requireStandalone } from \"./standalone\";\nimport { makeMappings } from \"./storage-mapping\";\nimport {\n tableDefinition,\n storageTables,\n type StorageRole,\n type StorageTable,\n} from \"./storage-tables\";\nimport {\n validateStorageBatch,\n withStorageValidation,\n type StorageValidation,\n} from \"./storage-validation\";\n\nexport interface Backend<T extends object, R, Database extends object = object> {\n readonly makeTable: (definition: StorageTable) => T;\n readonly describe: (table: T) => StorageTable;\n readonly acquire: Effect.Effect<Database, PersistenceConfigurationError, R | SqlClient.SqlClient>;\n readonly nativeTables: (database: Database) => NativeSqlTables;\n /** Fixed atomic batches replace interactive transactions on backends such as D1. */\n readonly batch?: (database: Database) => NonNullable<SqlBatchCommit[\"Service\"]>;\n}\n\nconst configError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappingError = (cause: unknown) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause });\n\nconst timestampKeys = new Set([\n \"verifiedAt\",\n \"createdAt\",\n \"lastIssueAt\",\n \"issuedAt\",\n \"expiresAt\",\n \"absoluteExpiresAt\",\n \"retentionUntil\",\n \"claimDeadline\",\n \"retryAt\",\n \"admittedAt\",\n \"deadline\",\n \"occurredAt\",\n \"dedupUntil\",\n \"binderExpiresAt\",\n \"refreshClaimedAt\",\n \"refreshClaimExpiresAt\",\n \"claimedAt\",\n \"claimExpiresAt\",\n]);\n\nexport const createPersistence = <T extends object, R, Database extends object = object>(\n backend: Backend<T, R, Database>,\n): PersistenceApi<T, R> => {\n const make = <C extends ClaimsCodec, const Id extends string, const A extends Definition<C, Id>>(\n auth: A & Definition<C, Id>,\n ) => {\n const features = Object.values(auth.strategies).flatMap((strategy) =>\n strategy.persistence === undefined ? [] : [strategy.persistence],\n );\n\n const phone = features.some((feature) => feature.kind === \"phone\");\n const password = features.some((feature) => feature.kind === \"password\");\n\n const passkeys = features.filter(\n (feature): feature is PasskeyFeature => feature.kind === \"passkey\",\n );\n\n const oauth = features.filter((feature): feature is OAuthFeature => feature?.kind === \"oauth\");\n const oauthSignIn = oauth.some((feature) => feature.signIn);\n const oauthConnected = oauth.some((feature) => feature.connected !== undefined);\n const stateful = auth.sessionMode === \"stateful\";\n\n const stateless =\n auth.sessionMode === \"stateless\" &&\n features.length > 0 &&\n features.every((feature) => feature?.kind === \"oauth\");\n\n const management = features.some(\n (feature) => feature.kind === \"password\" && feature.management,\n );\n\n const email = features.some((feature) => feature.kind === \"email\" && feature.addresses);\n const proofs = phone || management || email;\n\n const roles: StorageRole[] = [\"identifiers\", \"credentials\"];\n\n if (stateful) roles.push(\"sessions\");\n roles.push(\"pending\");\n if (password) roles.push(\"passwords\");\n if (email) roles.push(\"emailCredentials\");\n if (passkeys.length > 0) roles.push(\"passkeyCredentials\", \"passkeyFlows\");\n if (proofs) roles.push(\"proofs\");\n if (oauth.length > 0) roles.push(\"oauthIdentities\", \"oauthCredentials\");\n if (oauthSignIn) roles.push(\"oauthSignInFlows\");\n if (oauthConnected)\n roles.push(\"oauthConnectedFlows\", \"oauthConnectedGrants\", \"oauthConnectedRevocations\");\n\n const ConfigKey = Context.Service<\n ConfigId<A[\"namespace\"]>,\n StorageLayout<T, Roles<C, Id, A>>\n >()(`@yielded/auth-persistence/${auth.namespace}/Config`);\n\n const Config = Object.assign(ConfigKey, {\n layer: (value: StorageLayout<T, Roles<C, Id, A>>) => Layer.succeed(ConfigKey, value),\n });\n\n const ProvisioningKey = Context.Service<ProvisioningRequirement<A>, Provisioning<A>>()(\n `@yielded/auth-persistence/${auth.namespace}/Provisioning`,\n );\n\n const layout = <N, Instant>(\n options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<StorageRole, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n },\n managed: boolean,\n ): StorageLayout<T, Roles<C, Id, A>> => {\n const subject = options.subjects;\n\n if (passkeys.length > 0 && options.timestamps !== undefined)\n throw configError(\n \"Composed passkey storage uses integer milliseconds; use an explicit passkey adapter for a custom timestamp representation\",\n );\n const description = backend.describe(subject.table);\n const idColumn = description.columns[subject.id];\n\n for (const key of [subject.id, subject.status, subject.securityRevision]) {\n if (description.columns[key] === undefined)\n throw configError(`Missing subject column ${key}`);\n }\n if (subject.activeValue === undefined)\n throw configError(\"An explicit active subject value is required\");\n\n if (idColumn === undefined || idColumn.type === \"boolean\")\n throw configError(\"Subject ID must map a text or integer column\");\n\n const prefix = options.prefix;\n\n if (managed && (prefix === undefined || !/^[A-Za-z_][A-Za-z0-9_]{0,100}$/.test(prefix)))\n throw configError(\"Managed persistence requires an explicit SQL table prefix\");\n\n const schema: Partial<Record<StorageRole, T>> = { ...options.tables };\n\n for (const role of roles) {\n if (schema[role] === undefined && managed) {\n const definition = tableDefinition(role, `${prefix}_${role}`, idColumn.type);\n\n const adjusted =\n options.timestamps === undefined\n ? definition\n : {\n ...definition,\n columns: Object.fromEntries(\n Object.entries(definition.columns).map(([key, column]) => [\n key,\n timestampKeys.has(key)\n ? { ...column, type: options.timestamps?.type ?? column.type }\n : column,\n ]),\n ),\n };\n\n schema[role] = backend.makeTable(adjusted);\n }\n }\n for (const role of roles) {\n if (schema[role] === undefined) throw configError(`Missing ${role} table`);\n }\n const instantCodec = options.timestamps?.codec;\n\n const decodeInstant =\n instantCodec === undefined\n ? Schema.decodeUnknownEffect(Schema.Int)\n : Schema.decodeUnknownEffect(instantCodec);\n\n const decodeInstantSync =\n instantCodec === undefined\n ? Schema.decodeUnknownSync(Schema.Int)\n : Schema.decodeUnknownSync(instantCodec);\n\n const subjects: SubjectMapping = {\n table: subject.table,\n id: subject.id,\n status: subject.status,\n securityRevision: subject.securityRevision,\n activeValue: subject.activeValue,\n requirements: subject.requirements,\n actionRequirements: subject.actionRequirements ?? subject.requirements,\n toSubject: (id) =>\n Schema.decodeUnknownEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toNative: (id) =>\n Schema.encodeEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toSubjectSync: Schema.decodeUnknownSync(subject.idCodec),\n toNativeSync: Schema.encodeSync(subject.idCodec),\n };\n\n return Object.freeze({\n namespace: auth.namespace,\n schema: Object.freeze(schema) as Readonly<Record<Roles<C, Id, A>, T>>,\n tables: schema,\n subjects,\n encodeInstant:\n instantCodec === undefined ? (millis: number) => millis : Schema.encodeSync(instantCodec),\n decodeInstant: (value: unknown) => decodeInstant(value).pipe(Effect.mapError(mappingError)),\n decodeInstantSync,\n });\n };\n\n const services = Layer.effectContext(\n Effect.flatMap(backend.acquire, (database) =>\n Effect.gen(function* () {\n const storage = yield* ConfigKey;\n const client = yield* SqlClient.SqlClient;\n\n const dialect = client.onDialectOrElse({\n pg: () => \"pg\" as const,\n sqlite: () => \"sqlite\" as const,\n orElse: () => undefined,\n });\n\n if (dialect === undefined)\n return yield* configError(\"Use an explicit adapter for this SQL dialect\");\n if (\n features.some(\n (feature) =>\n (\"lifecycle\" in feature && feature.lifecycle) ||\n (feature.kind === \"email\" && !feature.addresses),\n )\n )\n return yield* configError(\n \"Use explicit services for strategies without composed persistence support\",\n );\n if (storage.namespace !== auth.namespace)\n return yield* configError(\n \"Persistence configuration belongs to a different Auth definition\",\n );\n const validations: StorageValidation[] = [];\n\n for (const role of roles) {\n const table = (storage.schema as Partial<Record<StorageRole, T>>)[role];\n\n if (table === undefined) return yield* configError(`Missing ${role} table`);\n const description = backend.describe(table);\n const columns = description.columns;\n\n for (const name of Object.keys(storageTables[role].columns)) {\n if (columns[name] === undefined)\n return yield* configError(`Missing ${role}.${name} column`);\n }\n validations.push({ table: description, required: storageTables[role].unique });\n }\n validations.push({\n table: backend.describe(storage.subjects.table as T),\n required: [[storage.subjects.id]],\n });\n yield* validateStorageBatch(dialect, validations);\n const nativeTables = backend.nativeTables(database);\n const mappings = yield* makeMappings(storage, nativeTables);\n\n const sessionMapping = stateful\n ? mappings.sessions(auth.claims, auth.sessions.moduleId)\n : undefined;\n\n const pendingMapping = mappings.pending(auth.claims, auth.sessions.moduleId);\n\n const { authenticationAuthority } = yield* makeNativeAuthenticationAuthorityServices(\n nativeTables,\n sessionMapping ?? {\n ...pendingMapping,\n pending: { pending: pendingMapping.pending, login: pendingMapping.login },\n },\n );\n\n const { pendingAuthentication } = yield* makeNativePendingAuthenticationServices(\n nativeTables,\n pendingMapping,\n );\n\n const { sessionStepUpPersistence } = yield* makeNativeSessionStepUpServices(\n nativeTables,\n {\n ...pendingMapping,\n source:\n sessionMapping === undefined\n ? { kind: \"StatelessSigned\" }\n : {\n kind: \"Stateful\",\n session: sessionMapping.session,\n sessionId: sessionMapping.sessionId,\n constraints: { sessionDigest: \"unique(session.digest)\" },\n },\n },\n );\n\n const { sessionCleanup } = yield* makeNativeSessionCleanupServices(\n nativeTables,\n pendingMapping,\n );\n\n let context: Context.Context<never> = Context.make(\n AuthenticationAuthority,\n authenticationAuthority,\n ).pipe(\n Context.add(auth.sessions.PendingAuthentication, pendingAuthentication),\n Context.add(auth.sessions.SessionStepUpPersistence, sessionStepUpPersistence),\n Context.add(auth.sessions.SessionCleanup, sessionCleanup),\n );\n\n if (sessionMapping !== undefined) {\n const sessionServices = yield* makeNativeStatefulSessionServices(\n nativeTables,\n sessionMapping,\n );\n\n context = context.pipe(\n Context.add(\n auth.sessions.StatefulSessionPersistence,\n sessionServices.statefulSessionPersistence,\n ),\n Context.add(auth.sessions.SessionRepository, sessionServices.sessionRepository),\n );\n }\n\n const proofMapping = proofs ? mappings.proofs() : undefined;\n\n if (proofMapping !== undefined) {\n const services = yield* makeNativeProofServices(\n backend.nativeTables(database),\n proofMapping,\n );\n\n context = Context.add(context, ProofPersistence, services.proofPersistence);\n }\n\n if (password) {\n const mapping = mappings.passwords();\n\n const services = yield* makeNativePasswordServices(\n backend.nativeTables(database),\n mapping,\n proofMapping,\n );\n\n context = Context.add(context, PasswordPersistence, services.passwordPersistence);\n }\n for (const [name, strategy] of Object.entries(auth.strategies)) {\n if (strategy.persistence?.kind !== \"password\" || !strategy.persistence.management)\n continue;\n const key = strategy.RegistrationAuthority;\n\n if (key === undefined)\n return yield* configError(`Missing subject provisioning for ${name}`);\n\n const registration = yield* makeRegistrationAuthority(\n backend.nativeTables(database),\n mappings.passwords(),\n ProvisioningKey,\n name,\n );\n\n context = Context.add(context, key, registration);\n }\n if (email)\n context = Context.add(\n context,\n EmailAddressPersistence,\n (yield* makeNativeEmailAddressServices(\n backend.nativeTables(database),\n mappings.emails(),\n proofMapping,\n )).emailAddressPersistence,\n );\n\n if (phone)\n context = Context.add(\n context,\n PhoneSignInTargets,\n yield* makeComposedPhoneTargets(\n backend.nativeTables(database),\n storage,\n mappings.proofs(),\n features.flatMap((feature) => (feature.kind === \"phone\" ? [feature.moduleId] : [])),\n ),\n );\n\n if (passkeys.length > 0) {\n // Auth capability metadata determines whether PasskeyConfig is required.\n const services = makeManagedPasskeys(\n {\n storage,\n namespace: auth.namespace,\n dialect,\n features: passkeys,\n passwordModules: features.flatMap((feature) =>\n feature.kind === \"password\" ? [feature.moduleId] : [],\n ),\n },\n backend.nativeTables(database),\n ) as Effect.Effect<\n Context.Context<never>,\n PersistenceConfigurationError,\n | PasskeyRequirement<A>\n | Crypto.Crypto\n | LifecycleHooks\n | SqlClient.SqlClient\n | SqlBatchCommit\n >;\n\n context = Context.merge(context, yield* services);\n }\n\n if (oauth.length > 0)\n context = Context.merge(context, yield* makeManagedOAuth(storage, nativeTables, oauth));\n\n // The checked capability metadata above determines exactly these service keys.\n return context as Context.Context<Ports<C, Id, A>>;\n }).pipe(Effect.provideService(SqlBatchCommit, backend.batch?.(database))),\n ).pipe(withStorageValidation),\n ).pipe(Layer.provide(hooksLayer));\n\n const layer = Layer.effectContext(\n Effect.gen(function* () {\n if (!stateful && !stateless) {\n if (auth.sessionMode === \"stateless\")\n return yield* configError(\n \"Composed stateless sessions support only OAuth strategies; use explicit persistence for other strategies\",\n );\n\n return yield* configError(\n \"Composed persistence supports stateful or OAuth-only stateless sessions; state-assisted validity and other session modes require explicit persistence\",\n );\n }\n const scope = yield* Effect.scope;\n const captured = yield* Effect.context<Layer.Services<typeof services>>();\n const client = Context.get(captured, SqlClient.SqlClient);\n\n // Construction uses the layer's exact environment and lifetime, never the\n // first operation's SQL transaction, service overrides, or request scope.\n // Cache the full exit: concurrent callers share failures/interruption too,\n // and cannot retry partially completed setup within this acquired layer.\n const initialize = yield* Effect.cached(\n Layer.buildWithScope(services, scope).pipe(Effect.setContext(captured)),\n );\n\n const service = Effect.fnUntraced(function* <I, S, Failure>(\n key: Context.Key<I, S>,\n unavailable: () => Failure,\n standalone = true,\n ): Effect.fn.Return<S, Failure> {\n // Reject unsupported ownership before setup can perform any writes.\n // Session/authority reads retain their existing ambient-read semantics.\n if (standalone) {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* requireStandalone(unavailable, client.transactionService);\n }\n\n // Observe the cached failure in this invocation, not the captured setup context.\n const acquired = yield* initialize.pipe(\n Effect.tapError((error) =>\n reportAuthDiagnostic(\n \"persistence-initialization\",\n Schema.is(PersistenceConfigurationError)(error)\n ? \"configuration\"\n : Schema.is(PersistenceMappingError)(error)\n ? \"mapping\"\n : \"unavailable\",\n ),\n ),\n Effect.mapError(unavailable),\n );\n\n const value = Context.getOrUndefined(acquired, key);\n\n return value === undefined ? yield* Effect.fail(unavailable()) : value;\n });\n\n const sessionUnavailable = () => SessionUnavailable.make({});\n const authority = service(AuthenticationAuthority, sessionUnavailable, false);\n const authorityMutation = service(AuthenticationAuthority, sessionUnavailable);\n\n const pendingRead = service(auth.sessions.PendingAuthentication, sessionUnavailable, false);\n const pendingMutation = service(auth.sessions.PendingAuthentication, sessionUnavailable);\n\n const stepUpRead = service(\n auth.sessions.SessionStepUpPersistence,\n sessionUnavailable,\n false,\n );\n\n const stepUpMutation = service(auth.sessions.SessionStepUpPersistence, sessionUnavailable);\n const cleanup = service(auth.sessions.SessionCleanup, sessionUnavailable);\n\n let context: Context.Context<never> = Context.make(AuthenticationAuthority, {\n capture: (subjectId, credentialIds) =>\n Effect.flatMap(authority, (s) => s.capture(subjectId, credentialIds)),\n requirements: (evidence) => Effect.flatMap(authority, (s) => s.requirements(evidence)),\n approve: (input, prepare) =>\n Effect.flatMap(authorityMutation, (s) => s.approve(input, prepare)),\n }).pipe(\n Context.add(auth.sessions.PendingAuthentication, {\n create: (input, now, prepare) =>\n Effect.flatMap(pendingMutation, (s) => s.create(input, now, prepare)),\n read: (input) => Effect.flatMap(pendingRead, (s) => s.read(input)),\n reject: (input, prepare) =>\n Effect.flatMap(pendingMutation, (s) => s.reject(input, prepare)),\n }),\n Context.add(auth.sessions.SessionStepUpPersistence, {\n create: (input, now, prepare) =>\n Effect.flatMap(stepUpMutation, (s) => s.create(input, now, prepare)),\n read: (input) => Effect.flatMap(stepUpRead, (s) => s.read(input)),\n reject: (input, prepare) =>\n Effect.flatMap(stepUpMutation, (s) => s.reject(input, prepare)),\n complete: (plan, prepare) =>\n Effect.flatMap(stepUpMutation, (s) => s.complete(plan, prepare)),\n }),\n Context.add(auth.sessions.SessionCleanup, {\n cleanup: (input) => Effect.flatMap(cleanup, (s) => s.cleanup(input)),\n }),\n );\n\n if (stateful) {\n const sessions = service(\n auth.sessions.StatefulSessionPersistence,\n sessionUnavailable,\n false,\n );\n\n const sessionMutations = service(\n auth.sessions.StatefulSessionPersistence,\n sessionUnavailable,\n );\n\n const repository = service(auth.sessions.SessionRepository, sessionUnavailable, false);\n\n context = context.pipe(\n Context.add(auth.sessions.StatefulSessionPersistence, {\n establish: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.establish(input, prepare)),\n verify: (input) => Effect.flatMap(sessions, (s) => s.verify(input)),\n rotate: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.rotate(input, prepare)),\n revokeDigest: (digest, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.revokeDigest(digest, prepare)),\n revoke: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.revoke(input, prepare)),\n revokeAll: (input, prepare) =>\n Effect.flatMap(sessionMutations, (s) => s.revokeAll(input, prepare)),\n }),\n Context.add(auth.sessions.SessionRepository, {\n list: (input) => Effect.flatMap(repository, (s) => s.list(input)),\n }),\n );\n }\n\n if (proofs) {\n const persistence = service(ProofPersistence, () => ProofUnavailable.make({}));\n\n context = Context.add(context, ProofPersistence, {\n issue: (input, prepare) => Effect.flatMap(persistence, (s) => s.issue(input, prepare)),\n redeem: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.redeem(input, prepare)),\n cancel: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.cancel(input, prepare)),\n cleanup: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.cleanup(input, prepare)),\n });\n }\n if (password) {\n const persistence = service(PasswordPersistence, () => PasswordUnavailable.make({}));\n\n context = Context.add(context, PasswordPersistence, {\n findCredential: (input) => Effect.flatMap(persistence, (s) => s.findCredential(input)),\n rehashIfCurrent: (input) =>\n Effect.flatMap(persistence, (s) => s.rehashIfCurrent(input)),\n readForSubject: (input) => Effect.flatMap(persistence, (s) => s.readForSubject(input)),\n recoveryTarget: (input) => Effect.flatMap(persistence, (s) => s.recoveryTarget(input)),\n addIfAbsent: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.addIfAbsent(input, prepare)),\n replaceIfCurrent: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.replaceIfCurrent(input, prepare)),\n resetWithProof: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.resetWithProof(input, prepare)),\n });\n }\n for (const [name, strategy] of Object.entries(auth.strategies)) {\n if (strategy.persistence?.kind !== \"password\" || !strategy.persistence.management)\n continue;\n const key = strategy.RegistrationAuthority;\n\n if (key === undefined)\n return yield* configError(`Missing subject provisioning for ${name}`);\n\n // The heterogeneous strategy table erases the registration input type;\n // its decoded value is forwarded unchanged to the same authority key.\n const registration = service(\n key as Context.Key<unknown, PasswordRegistrationAuthority<unknown>>,\n () => PasswordUnavailable.make({}),\n );\n\n context = Context.add(context, key, {\n register: (input, prepare) =>\n Effect.flatMap(registration, (s) => s.register(input, prepare)),\n } satisfies PasswordRegistrationAuthority<unknown>);\n }\n if (email) {\n const persistence = service(EmailAddressPersistence, () => EmailUnavailable.make({}));\n\n context = Context.add(context, EmailAddressPersistence, {\n target: (input) => Effect.flatMap(persistence, (s) => s.target(input)),\n verifyWithProof: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.verifyWithProof(input, prepare)),\n changeWithProof: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.changeWithProof(input, prepare)),\n });\n }\n if (phone) {\n const unavailable = () => PhoneOtpUnavailable.make({});\n const targets = service(PhoneSignInTargets, unavailable);\n\n context = context.pipe(\n Context.add(PhoneSignInTargets, {\n lookup: (input) => Effect.flatMap(targets, (s) => s.lookup(input)),\n }),\n );\n }\n if (passkeys.length > 0) {\n const unavailable = () => PasskeyUnavailable.make({});\n const credentials = service(PasskeyCredentials, unavailable);\n const persistence = service(PasskeyPersistence, unavailable);\n\n context = context.pipe(\n Context.add(PasskeyCredentials, {\n lookup: (input) => Effect.flatMap(credentials, (s) => s.lookup(input)),\n listForSubject: (input) =>\n Effect.flatMap(credentials, (s) => s.listForSubject(input)),\n }),\n Context.add(PasskeyPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.issue(input, prepare)),\n context: (input) => Effect.flatMap(persistence, (s) => s.context(input)),\n consume: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.consume(input, prepare)),\n cleanup: (input, prepare) =>\n Effect.flatMap(persistence, (s) => s.cleanup(input, prepare)),\n }),\n );\n if (passkeys.some((feature) => feature.management)) {\n const manager = service(PasskeyManagementPersistence, unavailable);\n\n context = context.pipe(\n Context.add(PasskeyManagementPersistence, {\n list: (input) => Effect.flatMap(manager, (s) => s.list(input)),\n completeEnrollment: (input, prepare) =>\n Effect.flatMap(manager, (s) => s.completeEnrollment(input, prepare)),\n inspectRemove: (input) => Effect.flatMap(manager, (s) => s.inspectRemove(input)),\n remove: (input, prepare) =>\n Effect.flatMap(manager, (s) => s.remove(input, prepare)),\n rename: (input, prepare) =>\n Effect.flatMap(manager, (s) => s.rename(input, prepare)),\n }),\n );\n }\n }\n\n const oauthUnavailable = () => OAuthUnavailable.make({});\n\n function oauthService<I, S>(\n key: Context.Key<I, S>,\n moduleId: string,\n capability: \"signIn\" | \"connected\",\n ): Effect.Effect<S, OAuthUnavailable> {\n const enabled = oauth.some(\n (feature) =>\n feature.moduleId === moduleId &&\n (capability === \"signIn\" ? feature.signIn : feature.connected !== undefined),\n );\n\n return enabled ? service(key, oauthUnavailable) : Effect.fail(oauthUnavailable());\n }\n\n if (oauthSignIn) {\n const persistence = (moduleId: string) =>\n oauthService(OAuthSignInPersistence, moduleId, \"signIn\");\n\n context = Context.add(context, OAuthSignInPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(persistence(input.context.moduleId), (s) => s.issue(input, prepare)),\n consume: (input, prepare) =>\n Effect.flatMap(persistence(input.moduleId), (s) => s.consume(input, prepare)),\n resolve: (input) =>\n Effect.flatMap(persistence(input.moduleId), (s) => s.resolve(input)),\n cleanup: (input, prepare) =>\n Effect.flatMap(persistence(input.moduleId), (s) => s.cleanup(input, prepare)),\n });\n }\n if (oauthConnected) {\n const persistence = (moduleId: string) =>\n oauthService(OAuthConnectedPersistence, moduleId, \"connected\");\n\n const revocations = (moduleId: string) =>\n oauthService(OAuthConnectedRevocations, moduleId, \"connected\");\n\n context = context.pipe(\n Context.add(OAuthConnectedPersistence, {\n read: (input) => Effect.flatMap(persistence(input.moduleId), (s) => s.read(input)),\n issue: (input, prepare) =>\n Effect.flatMap(persistence(input.context.moduleId), (s) => s.issue(input, prepare)),\n consume: (input, prepare) =>\n Effect.flatMap(persistence(input.moduleId), (s) => s.consume(input, prepare)),\n settle: (input, prepare) =>\n Effect.flatMap(persistence(input.grant.context.moduleId), (s) =>\n s.settle(input, prepare),\n ),\n list: (input) =>\n Effect.flatMap(persistence(input.authorization.moduleId), (s) => s.list(input)),\n disconnect: (input, prepare) =>\n Effect.flatMap(persistence(input.key.moduleId), (s) =>\n s.disconnect(input, prepare),\n ),\n claimRefresh: (input, prepare) =>\n Effect.flatMap(persistence(input.grant.context.moduleId), (s) =>\n s.claimRefresh(input, prepare),\n ),\n settleRefresh: (input, prepare) =>\n Effect.flatMap(persistence(input.claim.grant.context.moduleId), (s) =>\n s.settleRefresh(input, prepare),\n ),\n cleanup: (input, prepare) =>\n Effect.flatMap(persistence(input.moduleId), (s) => s.cleanup(input, prepare)),\n }),\n Context.add(OAuthConnectedRevocations, {\n claim: (input, prepare) =>\n Effect.flatMap(revocations(input.moduleId), (s) => s.claim(input, prepare)),\n settle: (input, prepare) =>\n Effect.flatMap(revocations(input.claim.job.grant.context.moduleId), (s) =>\n s.settle(input, prepare),\n ),\n }),\n );\n }\n\n // Match the same capability-selected service keys as the acquired graph.\n return context as Context.Context<Ports<C, Id, A>>;\n }),\n );\n\n return {\n Config,\n Provisioning: ProvisioningKey,\n layer,\n managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, true),\n map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Partial<Record<StorageRole, T>>;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, false),\n };\n };\n\n return { make };\n};\n"],"mappings":"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"}
|
|
@@ -187,7 +187,7 @@ const makeNativePhoneServices = Effect.fnUntraced(function* (tables, mapping) {
|
|
|
187
187
|
];
|
|
188
188
|
for (const originalRequirement of requirements) {
|
|
189
189
|
const requirement = yield* Schema.decodeEffect(AuthenticationRequirement)(originalRequirement);
|
|
190
|
-
if (!passwordEvidenceSatisfiedAt(evidence, requirement, captured.now)) return yield* rejected();
|
|
190
|
+
if (!(yield* passwordEvidenceSatisfiedAt(evidence, requirement, captured.now))) return yield* rejected();
|
|
191
191
|
}
|
|
192
192
|
if (evidence.proofs.length === 0) return yield* rejected();
|
|
193
193
|
for (const proof of evidence.proofs) {
|