@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +11 -0
  2. package/dist/Adapter.d.mts +13 -5
  3. package/dist/Adapter.mjs +9 -2
  4. package/dist/OAuthPersistence.d.mts +87 -0
  5. package/dist/OAuthPersistence.mjs +125 -0
  6. package/dist/OAuthPersistence.mjs.map +1 -0
  7. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  8. package/dist/index.d.mts +3 -1
  9. package/dist/index.mjs +3 -1
  10. package/dist/internal/authority-snapshot.mjs +53 -0
  11. package/dist/internal/authority-snapshot.mjs.map +1 -0
  12. package/dist/internal/configuration.d.mts +1 -1
  13. package/dist/internal/configuration.mjs.map +1 -1
  14. package/dist/internal/models/oauth-connected-model.d.mts +294 -0
  15. package/dist/internal/models/oauth-connected-model.mjs +22 -0
  16. package/dist/internal/models/oauth-connected-model.mjs.map +1 -0
  17. package/dist/internal/models/oauth-model.d.mts +467 -0
  18. package/dist/internal/models/oauth-model.mjs +49 -0
  19. package/dist/internal/models/oauth-model.mjs.map +1 -0
  20. package/dist/internal/models/password-model.d.mts +4 -97
  21. package/dist/internal/models/password-model.mjs +0 -3
  22. package/dist/internal/models/password-model.mjs.map +1 -1
  23. package/dist/internal/native-sql-table.d.mts +34 -0
  24. package/dist/internal/native-sql-table.mjs +111 -0
  25. package/dist/internal/native-sql-table.mjs.map +1 -0
  26. package/dist/internal/oauth/accounts.d.mts +8 -0
  27. package/dist/internal/oauth/accounts.mjs +379 -0
  28. package/dist/internal/oauth/accounts.mjs.map +1 -0
  29. package/dist/internal/oauth/connected-access.d.mts +9 -0
  30. package/dist/internal/oauth/connected-access.mjs +229 -0
  31. package/dist/internal/oauth/connected-access.mjs.map +1 -0
  32. package/dist/internal/oauth/connected-collection.d.mts +7 -0
  33. package/dist/internal/oauth/connected-collection.mjs +214 -0
  34. package/dist/internal/oauth/connected-collection.mjs.map +1 -0
  35. package/dist/internal/oauth/connected-custody.d.mts +6 -0
  36. package/dist/internal/oauth/connected-custody.mjs +165 -0
  37. package/dist/internal/oauth/connected-custody.mjs.map +1 -0
  38. package/dist/internal/oauth/connected-flow.d.mts +7 -0
  39. package/dist/internal/oauth/connected-flow.mjs +324 -0
  40. package/dist/internal/oauth/connected-flow.mjs.map +1 -0
  41. package/dist/internal/oauth/connected-input.mjs +122 -0
  42. package/dist/internal/oauth/connected-input.mjs.map +1 -0
  43. package/dist/internal/oauth/connected-keys.d.mts +2 -0
  44. package/dist/internal/oauth/connected-keys.mjs +22 -0
  45. package/dist/internal/oauth/connected-keys.mjs.map +1 -0
  46. package/dist/internal/oauth/connected-maintenance.d.mts +9 -0
  47. package/dist/internal/oauth/connected-maintenance.mjs +226 -0
  48. package/dist/internal/oauth/connected-maintenance.mjs.map +1 -0
  49. package/dist/internal/oauth/connected-management.d.mts +9 -0
  50. package/dist/internal/oauth/connected-management.mjs +243 -0
  51. package/dist/internal/oauth/connected-management.mjs.map +1 -0
  52. package/dist/internal/oauth/connected-reference.mjs +63 -0
  53. package/dist/internal/oauth/connected-reference.mjs.map +1 -0
  54. package/dist/internal/oauth/connected-settlement.d.mts +8 -0
  55. package/dist/internal/oauth/connected-settlement.mjs +141 -0
  56. package/dist/internal/oauth/connected-settlement.mjs.map +1 -0
  57. package/dist/internal/oauth/connected-sign-in.d.mts +10 -0
  58. package/dist/internal/oauth/connected-sign-in.mjs +281 -0
  59. package/dist/internal/oauth/connected-sign-in.mjs.map +1 -0
  60. package/dist/internal/oauth/connected-state.d.mts +14 -0
  61. package/dist/internal/oauth/connected-state.mjs +469 -0
  62. package/dist/internal/oauth/connected-state.mjs.map +1 -0
  63. package/dist/internal/oauth/connected-target.d.mts +21 -0
  64. package/dist/internal/oauth/connected-target.mjs +117 -0
  65. package/dist/internal/oauth/connected-target.mjs.map +1 -0
  66. package/dist/internal/oauth/flow.d.mts +5 -0
  67. package/dist/internal/oauth/flow.mjs +279 -0
  68. package/dist/internal/oauth/flow.mjs.map +1 -0
  69. package/dist/internal/oauth/input.mjs +127 -0
  70. package/dist/internal/oauth/input.mjs.map +1 -0
  71. package/dist/internal/oauth/owner.d.mts +10 -0
  72. package/dist/internal/oauth/owner.mjs +10 -0
  73. package/dist/internal/oauth/owner.mjs.map +1 -0
  74. package/dist/internal/oauth/query-compiler.d.mts +7 -0
  75. package/dist/internal/oauth/query-compiler.mjs +9 -0
  76. package/dist/internal/oauth/query-compiler.mjs.map +1 -0
  77. package/dist/internal/oauth/registration.d.mts +7 -0
  78. package/dist/internal/oauth/registration.mjs +393 -0
  79. package/dist/internal/oauth/registration.mjs.map +1 -0
  80. package/dist/internal/oauth/state.mjs +107 -0
  81. package/dist/internal/oauth/state.mjs.map +1 -0
  82. package/dist/internal/oauth/target.d.mts +31 -0
  83. package/dist/internal/oauth/target.mjs +273 -0
  84. package/dist/internal/oauth/target.mjs.map +1 -0
  85. package/dist/internal/oauth-kernel.d.mts +1423 -0
  86. package/dist/internal/oauth-kernel.mjs +187 -0
  87. package/dist/internal/oauth-kernel.mjs.map +1 -0
  88. package/dist/internal/oauth-proxy.d.mts +31 -0
  89. package/dist/internal/oauth-proxy.mjs +129 -0
  90. package/dist/internal/oauth-proxy.mjs.map +1 -0
  91. package/dist/internal/passkey/target.d.mts +1 -1
  92. package/dist/internal/passkey-kernel.d.mts +5 -5
  93. package/dist/internal/passkeys.mjs +1 -1
  94. package/dist/internal/password-credentials.d.mts +11 -0
  95. package/dist/internal/password-credentials.mjs +121 -0
  96. package/dist/internal/password-credentials.mjs.map +1 -0
  97. package/dist/internal/password-kernel.d.mts +15 -28
  98. package/dist/internal/password-kernel.mjs +35 -236
  99. package/dist/internal/password-kernel.mjs.map +1 -1
  100. package/dist/internal/persistence.d.mts +2 -0
  101. package/dist/internal/persistence.mjs +5 -5
  102. package/dist/internal/persistence.mjs.map +1 -1
  103. package/dist/internal/query-operations.d.mts +2 -0
  104. package/dist/internal/session-kernel.d.mts +5 -3
  105. package/dist/internal/session-kernel.mjs +95 -29
  106. package/dist/internal/session-kernel.mjs.map +1 -1
  107. package/dist/internal/sql-oauth-connected-model.d.mts +29 -0
  108. package/dist/internal/sql-oauth-connected-model.mjs +7 -0
  109. package/dist/internal/sql-oauth-connected-model.mjs.map +1 -0
  110. package/dist/internal/sql-oauth-model.d.mts +60 -0
  111. package/dist/internal/sql-oauth-model.mjs +10 -0
  112. package/dist/internal/sql-oauth-model.mjs.map +1 -0
  113. package/dist/internal/sql-persistence.mjs +3 -1
  114. package/dist/internal/sql-persistence.mjs.map +1 -1
  115. package/dist/internal/sql-query.d.mts +12 -0
  116. package/dist/internal/sql-query.mjs +15 -18
  117. package/dist/internal/sql-query.mjs.map +1 -1
  118. package/dist/internal/sql-storage-validation.mjs +54 -0
  119. package/dist/internal/sql-storage-validation.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +2 -67
  121. package/dist/internal/storage-mapping.mjs.map +1 -1
  122. package/dist/internal/storage-plans.d.mts +7 -0
  123. package/dist/internal/storage-plans.mjs +189 -0
  124. package/dist/internal/storage-plans.mjs.map +1 -0
  125. package/dist/internal/storage-tables.d.mts +0 -99
  126. package/dist/internal/storage-tables.mjs +0 -43
  127. package/dist/internal/storage-tables.mjs.map +1 -1
  128. package/dist/internal/storage-validation.d.mts +6 -1
  129. package/dist/internal/storage-validation.mjs +42 -1
  130. package/dist/internal/storage-validation.mjs.map +1 -1
  131. package/dist/internal/transaction-execution-kernel.d.mts +1 -1
  132. package/dist/internal/transaction-kernel.d.mts +1 -1
  133. package/dist/internal/transaction-kernel.mjs +3 -2
  134. package/dist/internal/transaction-kernel.mjs.map +1 -1
  135. package/package.json +1 -1
  136. package/src/Adapter.ts +97 -8
  137. package/src/OAuthPersistence.ts +427 -0
  138. package/src/index.ts +3 -0
  139. package/src/internal/authority-snapshot.ts +91 -0
  140. package/src/internal/configuration.ts +1 -8
  141. package/src/internal/models/oauth-connected-model.ts +396 -0
  142. package/src/internal/models/oauth-model.ts +708 -0
  143. package/src/internal/models/password-model.ts +0 -120
  144. package/src/internal/native-sql-table.ts +178 -0
  145. package/src/internal/oauth/accounts.ts +838 -0
  146. package/src/internal/oauth/connected-access.ts +388 -0
  147. package/src/internal/oauth/connected-collection.ts +345 -0
  148. package/src/internal/oauth/connected-custody.ts +308 -0
  149. package/src/internal/oauth/connected-flow.ts +562 -0
  150. package/src/internal/oauth/connected-input.ts +147 -0
  151. package/src/internal/oauth/connected-keys.ts +31 -0
  152. package/src/internal/oauth/connected-maintenance.ts +379 -0
  153. package/src/internal/oauth/connected-management.ts +391 -0
  154. package/src/internal/oauth/connected-reference.ts +135 -0
  155. package/src/internal/oauth/connected-settlement.ts +278 -0
  156. package/src/internal/oauth/connected-sign-in.ts +472 -0
  157. package/src/internal/oauth/connected-state.ts +924 -0
  158. package/src/internal/oauth/connected-target.ts +307 -0
  159. package/src/internal/oauth/flow.ts +505 -0
  160. package/src/internal/oauth/input.ts +201 -0
  161. package/src/internal/oauth/owner.ts +22 -0
  162. package/src/internal/oauth/query-compiler.ts +15 -0
  163. package/src/internal/oauth/registration.ts +676 -0
  164. package/src/internal/oauth/state.ts +218 -0
  165. package/src/internal/oauth/target.ts +803 -0
  166. package/src/internal/oauth-kernel.ts +294 -0
  167. package/src/internal/oauth-proxy.ts +176 -0
  168. package/src/internal/password-credentials.ts +245 -0
  169. package/src/internal/password-kernel.ts +98 -712
  170. package/src/internal/persistence.ts +5 -10
  171. package/src/internal/query-operations.ts +2 -0
  172. package/src/internal/session-kernel.ts +231 -81
  173. package/src/internal/sql-oauth-connected-model.ts +145 -0
  174. package/src/internal/sql-oauth-model.ts +263 -0
  175. package/src/internal/sql-persistence.ts +2 -0
  176. package/src/internal/sql-query.ts +16 -13
  177. package/src/internal/sql-storage-validation.ts +64 -0
  178. package/src/internal/storage-mapping.ts +0 -66
  179. package/src/internal/storage-plans.ts +218 -0
  180. package/src/internal/storage-tables.ts +0 -35
  181. package/src/internal/storage-validation.ts +58 -0
  182. package/src/internal/transaction-kernel.ts +6 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-credentials.mjs","names":[],"sources":["../../src/internal/password-credentials.ts"],"sourcesContent":["import { CurrentCommitJournal, hasCommitScope } from \"@yielded/auth/Hooks\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n EncodedPasswordHash,\n type PasswordPersistence,\n PasswordUnavailable,\n snapshotPasswordCredential,\n type PasswordCredentialSnapshot,\n} from \"@yielded/auth/Password\";\nimport { reportPersistenceFailure } from \"@yielded/auth/Persistence\";\nimport { Effect, Option, Redacted, Schema } from \"effect\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyPasswordPersistenceMapping } from \"./models/password-model\";\nimport { sqlMapping, type SqlTable } from \"./native-sql-table\";\nimport type { PasswordSqlConfiguration } from \"./password-kernel\";\nimport { NativeDatabase } from \"./transaction-kernel\";\n\ntype Row = Readonly<Record<string, unknown>>;\nconst unavailable = () => PasswordUnavailable.make({});\n\nconst failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n reportPersistenceFailure(effect, Schema.is(PasswordUnavailable)).pipe(\n Effect.mapError(unavailable),\n );\n\nexport const samePasswordCredentialSnapshot = (\n a: PasswordCredentialSnapshot,\n b: PasswordCredentialSnapshot,\n) =>\n a.moduleId === b.moduleId &&\n a.revision.subjectId === b.revision.subjectId &&\n a.revision.securityRevision === b.revision.securityRevision &&\n a.revision.credentials.length === b.revision.credentials.length &&\n new Set(a.revision.credentials.map((entry) => entry.credentialId)).size ===\n a.revision.credentials.length &&\n new Set(b.revision.credentials.map((entry) => entry.credentialId)).size ===\n b.revision.credentials.length &&\n a.revision.credentials.every((entry) =>\n b.revision.credentials.some(\n (other) => entry.credentialId === other.credentialId && entry.revision === other.revision,\n ),\n ) &&\n a.credentialId === b.credentialId &&\n a.credentialRevision === b.credentialRevision &&\n a.verifierVersion === b.verifierVersion &&\n Redacted.value(a.verifier) === Redacted.value(b.verifier) &&\n a.normalization === b.normalization &&\n a.identifier.namespace === b.identifier.namespace &&\n a.identifier.value === b.identifier.value &&\n a.identifierBindingRevision === b.identifierBindingRevision &&\n a.identifierVerifiedAtMillis === b.identifierVerifiedAtMillis;\n\n/** Lookup is read-only; rehash is one conditional maintenance write, never authentication. */\nexport const makePasswordCredentials = Effect.fnUntraced(function* (\n table: (table: object) => SqlTable,\n mapping: AnyPasswordPersistenceMapping,\n options: PasswordSqlConfiguration,\n): Effect.fn.Return<\n Pick<PasswordPersistence[\"Service\"], \"findCredential\" | \"rehashIfCurrent\">,\n PasswordUnavailable,\n NativeDatabase\n> {\n const sql = (yield* NativeDatabase).$client.withoutTransforms();\n const parent = yield* Effect.serviceOption(CurrentCommitJournal);\n\n const [s, i, c] = yield* sqlMapping(\n () =>\n [\n table(mapping.subject.table),\n table(mapping.identifier.table),\n table(mapping.credential.table),\n ] as const,\n ).pipe(failure);\n\n const standalone = Effect.gen(function* () {\n if (options.coordinated) {\n const current = yield* Effect.serviceOption(CurrentCommitJournal);\n\n if (Option.isNone(parent) || Option.isNone(current) || parent.value !== current.value)\n return yield* unavailable();\n } else {\n if (yield* hasCommitScope) return yield* unavailable();\n yield* options.standaloneGuard;\n }\n });\n\n const allocate = <A>(\n asynchronous: Effect.Effect<A, PersistenceMappingError> | undefined,\n sync: (() => A) | undefined,\n ) => {\n if (options.mode !== \"synchronous\" && asynchronous !== undefined) return asynchronous;\n\n return sync === undefined\n ? Effect.fail(unavailable())\n : Effect.try({ try: sync, catch: unavailable });\n };\n\n // Bind each native ID through its own column codec. Physical encodings of the\n // same application ID need not match across subject, identifier and password.\n const snapshot = Effect.fnUntraced(function* (\n nativeId: unknown,\n moduleId: string,\n identifier: LoginIdentifier,\n ) {\n const subjectTable = s.as(\"password_subject\");\n const identifierTable = i.as(\"password_identifier\");\n const credentialTable = c.as(\"password_credential\");\n\n const rows = yield* sqlMapping(\n () =>\n sql<Row>`select ${subjectTable.fields(\"subject_\")}, ${identifierTable.fields(\"identifier_\")}, ${credentialTable.fields(\"credential_\")}\n from ${subjectTable.name}\n inner join ${identifierTable.name} on ${identifierTable.column(mapping.identifier.namespace)} = ${identifierTable.value(mapping.identifier.namespace, identifier.namespace)}\n and ${identifierTable.column(mapping.identifier.value)} = ${identifierTable.value(mapping.identifier.value, identifier.value)}\n and ${identifierTable.column(mapping.identifier.subjectId)} = ${identifierTable.value(mapping.identifier.subjectId, nativeId)}\n inner join ${credentialTable.name} on ${credentialTable.column(mapping.credential.moduleId)} = ${credentialTable.value(mapping.credential.moduleId, moduleId)}\n and ${credentialTable.column(mapping.credential.subjectId)} = ${credentialTable.value(mapping.credential.subjectId, nativeId)}\n where ${subjectTable.column(mapping.subject.id)} = ${subjectTable.value(mapping.subject.id, nativeId)}\n `,\n ).pipe(Effect.flatten);\n\n if (rows.length !== 1) return undefined;\n\n const [subject, identifierRow, credential] = yield* sqlMapping(\n () =>\n [\n s.decode(rows[0]!, \"subject_\"),\n i.decode(rows[0]!, \"identifier_\"),\n c.decode(rows[0]!, \"credential_\"),\n ] as const,\n );\n\n if (\n yield* sqlMapping(\n () =>\n !mapping.subject.isActiveStatus(subject[mapping.subject.status]) ||\n !mapping.identifier.isCurrent(identifierRow) ||\n !mapping.subjectId.equals(nativeId, identifierRow[mapping.identifier.subjectId]) ||\n !mapping.subjectId.equals(nativeId, subject[mapping.subject.id]) ||\n !mapping.subjectId.equals(nativeId, credential[mapping.credential.subjectId]),\n )\n )\n return undefined;\n\n const captured = yield* sqlMapping(() =>\n mapping.credential.decode({ moduleId, subject, identifier: identifierRow, credential }),\n ).pipe(Effect.flatten, Effect.flatMap(snapshotPasswordCredential));\n\n return captured;\n });\n\n return {\n findCredential: (uncaptured) =>\n Effect.gen(function* () {\n const input = { ...uncaptured, identifier: { ...uncaptured.identifier } };\n\n yield* standalone;\n\n const identifiers = yield* sqlMapping(\n () => sql<Row>`select ${i.fields(\"i_\")} from ${i.name}\n where ${i.column(mapping.identifier.namespace)} = ${i.value(mapping.identifier.namespace, input.identifier.namespace)}\n and ${i.column(mapping.identifier.value)} = ${i.value(mapping.identifier.value, input.identifier.value)}`,\n ).pipe(Effect.flatten);\n\n const identifier =\n identifiers.length === 1\n ? yield* sqlMapping(() => i.decode(identifiers[0]!, \"i_\"))\n : undefined;\n\n if (\n identifier === undefined ||\n !(yield* sqlMapping(() => mapping.identifier.isCurrent(identifier)))\n )\n return Option.none();\n\n const credential = yield* snapshot(\n identifier[mapping.identifier.subjectId],\n input.moduleId,\n input.identifier,\n );\n\n return credential === undefined ||\n (input.subjectId !== undefined && credential.revision.subjectId !== input.subjectId)\n ? Option.none()\n : Option.some(credential);\n }).pipe(failure),\n rehashIfCurrent: (input) =>\n Effect.gen(function* () {\n const nextVerifier = Redacted.make(\n yield* Schema.decodeEffect(EncodedPasswordHash)(Redacted.value(input.nextVerifier)),\n );\n\n const credential = yield* snapshotPasswordCredential(input.credential);\n\n yield* standalone;\n const nativeId = yield* mapping.subjectId.toNative(credential.revision.subjectId);\n const version = yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync);\n\n if (version === credential.verifierVersion) return yield* unavailable();\n yield* sqlMapping(() => {\n const encoded = mapping.credential.encodeVerifier(nextVerifier, version);\n\n // A custom encoder cannot turn maintenance into a semantic credential change.\n const values = {\n [mapping.credential.verifier]: encoded[mapping.credential.verifier],\n [mapping.credential.verifierVersion]: encoded[mapping.credential.verifierVersion],\n };\n\n if (Object.values(values).some((value) => value === undefined)) throw unavailable();\n\n // Ordinary identity predicates retain index use; the full CAS ignores text collation.\n const exact = sql.and(\n Object.entries({\n [mapping.credential.moduleId]: credential.moduleId,\n [mapping.credential.subjectId]: nativeId,\n [mapping.credential.credentialId]: credential.credentialId,\n [mapping.credential.credentialRevision]: credential.credentialRevision,\n [mapping.credential.verifierVersion]: credential.verifierVersion,\n [mapping.credential.verifier]: Redacted.value(credential.verifier),\n [mapping.credential.normalization]: credential.normalization,\n }).map(([key, value]) => {\n const column = c.column(key),\n bound = c.value(key, value);\n\n if (typeof value !== \"string\") return sql`${column} = ${bound}`;\n\n return sql.onDialectOrElse({\n mysql: () => sql`binary ${column} = binary ${bound}`,\n pg: () =>\n sql`convert_to(cast(${column} as text), 'UTF8') = convert_to(cast(${bound} as text), 'UTF8')`,\n orElse: () => sql`cast(${column} as blob) = cast(${bound} as blob)`,\n });\n }),\n );\n\n return sql`${c.update(values)}\n where ${c.column(mapping.credential.moduleId)} = ${c.value(mapping.credential.moduleId, credential.moduleId)}\n and ${c.column(mapping.credential.subjectId)} = ${c.value(mapping.credential.subjectId, nativeId)}\n and ${c.column(mapping.credential.credentialId)} = ${c.value(mapping.credential.credentialId, credential.credentialId)}\n and ${exact}`;\n }).pipe(Effect.flatten);\n }).pipe(failure),\n } satisfies Pick<PasswordPersistence[\"Service\"], \"findCredential\" | \"rehashIfCurrent\">;\n});\n"],"mappings":"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"}
@@ -3,12 +3,15 @@ import { NativeDatabase } from "./transaction-kernel.mjs";
3
3
  import { QueryFailure, QueryOperations, SqlColumn, SqlFragment } from "./query-operations.mjs";
4
4
  import { AnyPasswordPersistenceMapping } from "./models/password-model.mjs";
5
5
  import { CurrentProofSql, ProofSqlConfiguration, makeProofKernel } from "./proof-kernel.mjs";
6
+ import { NativeSqlTables } from "./native-sql-table.mjs";
7
+ import { samePasswordCredentialSnapshot } from "./password-credentials.mjs";
6
8
  import { Context, DateTime, Effect, Option, Redacted } from "effect";
7
9
  import { CurrentCommitJournal, HookConfigurationError, LifecycleHooks, PreparedCommit } from "@yielded/auth/Hooks";
8
10
  import { AuthenticationEvidence, AuthenticationRequirement, SecurityRevision } from "@yielded/auth/Sessions";
9
11
  import { ProofCompletionInput, ProofCompletionPlan } from "@yielded/auth/Proofs";
10
- import { PasswordAttemptDecision, PasswordAttemptPolicy, PasswordCredentialSnapshot, PasswordMutationDecision, PasswordMutationInput, PasswordUnavailable } from "@yielded/auth/Password";
12
+ import { PasswordMutationDecision, PasswordMutationInput, PasswordUnavailable } from "@yielded/auth/Password";
11
13
  import { LoginIdentifier } from "@yielded/auth/Identity";
14
+ import { SqlClient } from "effect/sql/SqlClient";
12
15
  import * as SqlError$1 from "effect/sql/SqlError";
13
16
  //#region src/internal/password-kernel.d.ts
14
17
  type AdapterFailure = QueryFailure | PersistenceMappingError | SqlError$1.SqlError;
@@ -47,47 +50,31 @@ export interface PasswordSqlConfiguration {
47
50
  };
48
51
  }
49
52
  type Database = PasswordSqlDatabase;
50
- export declare const makePasswordKernel: <Fragment extends SqlFragment = SqlFragment, Column extends SqlColumn = SqlColumn>(operations: QueryOperations<Fragment, Column>, proofs: Pick<ReturnType<typeof makeProofKernel>, "completeProofPlanIn">) => {
53
+ export declare const makePasswordKernel: <Fragment extends SqlFragment = SqlFragment, Column extends SqlColumn = SqlColumn>(operations: QueryOperations<Fragment, Column>, proofs: Pick<ReturnType<typeof makeProofKernel>, "completeProofPlanIn">, nativeTables?: (client: SqlClient, database: object) => NativeSqlTables) => {
51
54
  makeSqlPasswordPersistence: (mapping: AnyPasswordPersistenceMapping<Fragment>, configuration: PasswordSqlConfiguration) => Effect.Effect<{
52
- readonly admitAttempt: <A>(input: {
55
+ readonly findCredential: (input: {
53
56
  readonly moduleId: string;
54
- readonly action: "sign-in" | "change";
55
57
  readonly identifier: LoginIdentifier;
56
58
  readonly subjectId?: import("@yielded/auth/Schema").SubjectId;
57
- readonly policy: PasswordAttemptPolicy;
58
- }, prepare: import("@yielded/auth/Password").PreparePasswordCommit<import("@yielded/auth/Password").PasswordAttemptAdmission, A>) => Effect.Effect<PreparedCommit<A>, PasswordUnavailable>;
59
- readonly settleAttempt: <A>(input: {
60
- readonly moduleId: string;
61
- readonly attemptId: import("@yielded/auth/Password").PasswordAttemptId;
62
- readonly captured?: PasswordCredentialSnapshot;
63
- readonly outcome: PasswordAttemptDecision;
64
- readonly rehash?: {
65
- readonly expectedVersion: SecurityRevision;
66
- readonly expectedVerifier: Redacted.Redacted<import("@yielded/auth/Password").EncodedPasswordHash>;
67
- readonly nextVerifier: Redacted.Redacted<import("@yielded/auth/Password").EncodedPasswordHash>;
68
- };
69
- }, prepare: import("@yielded/auth/Password").PreparePasswordCommit<PasswordAttemptDecision, A>) => Effect.Effect<PreparedCommit<A>, PasswordUnavailable>;
59
+ }) => Effect.Effect<Option.Option<import("@yielded/auth/Password").PasswordCredentialSnapshot>, PasswordUnavailable>;
60
+ readonly rehashIfCurrent: (input: {
61
+ readonly credential: import("@yielded/auth/Password").PasswordCredentialSnapshot;
62
+ readonly nextVerifier: Redacted.Redacted<import("@yielded/auth/Password").EncodedPasswordHash>;
63
+ }) => Effect.Effect<void, PasswordUnavailable>;
70
64
  readonly readForSubject: (input: {
71
65
  readonly moduleId: string;
72
66
  readonly subjectId: import("@yielded/auth/Schema").SubjectId;
73
- }) => Effect.Effect<Option.Option<PasswordCredentialSnapshot>, PasswordUnavailable>;
67
+ }) => Effect.Effect<Option.Option<import("@yielded/auth/Password").PasswordCredentialSnapshot>, PasswordUnavailable>;
74
68
  readonly recoveryTarget: (input: {
75
69
  readonly moduleId: string;
76
70
  readonly identifier: LoginIdentifier;
77
- }) => Effect.Effect<Option.Option<PasswordCredentialSnapshot>, PasswordUnavailable>;
71
+ }) => Effect.Effect<Option.Option<import("@yielded/auth/Password").PasswordCredentialSnapshot>, PasswordUnavailable>;
78
72
  readonly addIfAbsent: <A>(input: PasswordMutationInput, prepare: import("@yielded/auth/Password").PreparePasswordCommit<PasswordMutationDecision, A>) => Effect.Effect<PreparedCommit<A>, PasswordUnavailable>;
79
73
  readonly replaceIfCurrent: <A>(input: PasswordMutationInput, prepare: import("@yielded/auth/Password").PreparePasswordCommit<PasswordMutationDecision, A>) => Effect.Effect<PreparedCommit<A>, PasswordUnavailable>;
80
74
  readonly checkReset: (input: ProofCompletionInput) => Effect.Effect<boolean, PasswordUnavailable>;
81
75
  readonly resetWithProof: <A>(input: PasswordMutationInput & {
82
76
  readonly completion: ProofCompletionPlan;
83
77
  }, prepare: import("@yielded/auth/Password").PreparePasswordCommit<PasswordMutationDecision, A>) => Effect.Effect<PreparedCommit<A>, PasswordUnavailable>;
84
- readonly cleanupAttempts: <A>(input: {
85
- readonly moduleId: string;
86
- readonly limit: number;
87
- }, prepare: import("@yielded/auth/Password").PreparePasswordCommit<{
88
- readonly removed: number;
89
- readonly hasMore: boolean;
90
- }, A>) => Effect.Effect<PreparedCommit<A>, PasswordUnavailable>;
91
78
  }, PasswordUnavailable, CurrentPasswordSql | LifecycleHooks | NativeDatabase>;
92
79
  passwordSqlKernel: {
93
80
  allocate: <A>(mode: PasswordSqlConfiguration["mode"], asyncValue: Effect.Effect<A, PersistenceMappingError> | undefined, syncValue: (() => A) | undefined) => Effect.Effect<A, PasswordUnavailable | PersistenceMappingError>;
@@ -203,8 +190,8 @@ export declare const makePasswordKernel: <Fragment extends SqlFragment = SqlFrag
203
190
  nativeSubjectId: any;
204
191
  subject: any;
205
192
  identifier: any;
193
+ credential?: never;
206
194
  snapshot?: never;
207
- readonly credential?: never;
208
195
  } | {
209
196
  nativeSubjectId: any;
210
197
  subject: any;
@@ -255,7 +242,7 @@ export declare const makePasswordKernel: <Fragment extends SqlFragment = SqlFrag
255
242
  readonly nextSecurityRevision: SecurityRevision;
256
243
  }, now: number) => Effect.Effect<boolean, AdapterFailure, CurrentPasswordSql>;
257
244
  evidenceSatisfiedAt: (evidence: AuthenticationEvidence, requirement: AuthenticationRequirement, now: number) => boolean;
258
- sameCredentialSnapshot: (left: PasswordCredentialSnapshot, right: PasswordCredentialSnapshot) => boolean;
245
+ sameCredentialSnapshot: typeof samePasswordCredentialSnapshot;
259
246
  subjectColumns: (mapping: AnyPasswordPersistenceMapping<Fragment>) => {
260
247
  id: Column;
261
248
  status: Column;
@@ -1,6 +1,10 @@
1
1
  import { PersistenceMappingError } from "./mapping-error.mjs";
2
+ import { makeNativeSqlTables, sqlMapping } from "./native-sql-table.mjs";
3
+ import { NativeDatabase } from "./transaction-kernel.mjs";
4
+ import { CurrentPasswordPreparedTransaction, PasswordPreparedPostconditions } from "./PasswordPreparedPostconditions.mjs";
2
5
  import { CurrentProofSql } from "./proof-kernel.mjs";
3
6
  import { requiredPasswordConstraints } from "./models/password-model.mjs";
7
+ import { makePasswordCredentials, samePasswordCredentialSnapshot } from "./password-credentials.mjs";
4
8
  import { Cause, Context, DateTime, Effect, Option, Redacted, Schema } from "effect";
5
9
  import { CurrentCommitJournal, HookConfigurationError, LifecycleHooks, coordinateCommit, hasCommitScope } from "@yielded/auth/Hooks";
6
10
  import { snapshotAuthenticationEvidence } from "@yielded/auth/Sessions";
@@ -9,8 +13,8 @@ import { ProofUnavailable } from "@yielded/auth/Proofs";
9
13
  import { PasswordPersistence, PasswordUnavailable, snapshotPasswordCredential, snapshotPasswordRequirement, snapshotPasswordRevision } from "@yielded/auth/Password";
10
14
  //#region src/internal/password-kernel.ts
11
15
  var CurrentPasswordSql = class extends Context.Service()("effect-auth/CurrentPasswordSql") {};
12
- const makePasswordKernel = (operations, proofs) => {
13
- const { and, eq, gt, gte, inArray, lte, notExists, sql, column, updateValues } = operations;
16
+ const makePasswordKernel = (operations, proofs, nativeTables = (client) => makeNativeSqlTables(client)) => {
17
+ const { and, eq, inArray, column, updateValues } = operations;
14
18
  const { completeProofPlanIn } = proofs;
15
19
  const unavailable = () => PasswordUnavailable.make({});
16
20
  const translateFailure = (effect) => reportPersistenceFailure(effect, (error) => Schema.is(PasswordUnavailable)(error) || Schema.is(ProofUnavailable)(error) || Schema.is(HookConfigurationError)(error)).pipe(Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, () => unavailable()))));
@@ -43,38 +47,6 @@ const makePasswordKernel = (operations, proofs) => {
43
47
  credentialId: column(mapping.authorityCredential.table, mapping.authorityCredential.credentialId),
44
48
  revision: column(mapping.authorityCredential.table, mapping.authorityCredential.revision)
45
49
  });
46
- const attemptColumns = (mapping) => ({
47
- moduleId: column(mapping.attempt.table, mapping.attempt.moduleId),
48
- action: column(mapping.attempt.table, mapping.attempt.action),
49
- attemptId: column(mapping.attempt.table, mapping.attempt.attemptId),
50
- identifierNamespace: column(mapping.attempt.table, mapping.attempt.identifierNamespace),
51
- identifierValue: column(mapping.attempt.table, mapping.attempt.identifierValue),
52
- subjectId: column(mapping.attempt.table, mapping.attempt.subjectId),
53
- credentialId: column(mapping.attempt.table, mapping.attempt.credentialId),
54
- securityRevision: column(mapping.attempt.table, mapping.attempt.securityRevision),
55
- credentialRevision: column(mapping.attempt.table, mapping.attempt.credentialRevision),
56
- verifierVersion: column(mapping.attempt.table, mapping.attempt.verifierVersion),
57
- identifierBindingRevision: column(mapping.attempt.table, mapping.attempt.identifierBindingRevision),
58
- admittedAt: column(mapping.attempt.table, mapping.attempt.admittedAt),
59
- deadline: column(mapping.attempt.table, mapping.attempt.deadline),
60
- retentionUntil: column(mapping.attempt.table, mapping.attempt.retentionUntil),
61
- state: column(mapping.attempt.table, mapping.attempt.state)
62
- });
63
- const scopeColumns = (mapping) => ({
64
- moduleId: column(mapping.rateScope.table, mapping.rateScope.moduleId),
65
- action: column(mapping.rateScope.table, mapping.rateScope.action),
66
- scopeKind: column(mapping.rateScope.table, mapping.rateScope.scopeKind),
67
- scopeKey: column(mapping.rateScope.table, mapping.rateScope.scopeKey)
68
- });
69
- const chargeColumns = (mapping) => ({
70
- moduleId: column(mapping.charge.table, mapping.charge.moduleId),
71
- action: column(mapping.charge.table, mapping.charge.action),
72
- scopeKind: column(mapping.charge.table, mapping.charge.scopeKind),
73
- scopeKey: column(mapping.charge.table, mapping.charge.scopeKey),
74
- attemptId: column(mapping.charge.table, mapping.charge.attemptId),
75
- occurredAt: column(mapping.charge.table, mapping.charge.occurredAt),
76
- retentionUntil: column(mapping.charge.table, mapping.charge.retentionUntil)
77
- });
78
50
  const commandColumns = (mapping) => ({
79
51
  moduleId: column(mapping.command.table, mapping.command.moduleId),
80
52
  commandId: column(mapping.command.table, mapping.command.commandId),
@@ -106,7 +78,6 @@ const makePasswordKernel = (operations, proofs) => {
106
78
  return allocate(mode, asyncValue ?? mapping.allocateRevision, syncValue ?? mapping.allocateRevisionSync);
107
79
  };
108
80
  const sameIdentifier = (left, right) => left.namespace === right.namespace && left.value === right.value;
109
- const sameCredentialSnapshot = (left, right) => left.moduleId === right.moduleId && left.revision.subjectId === right.revision.subjectId && left.revision.securityRevision === right.revision.securityRevision && left.credentialId === right.credentialId && left.credentialRevision === right.credentialRevision && left.verifierVersion === right.verifierVersion && left.normalization === right.normalization && left.identifierVerifiedAtMillis === right.identifierVerifiedAtMillis && left.revision.credentials.length === right.revision.credentials.length && left.revision.credentials.every((entry) => right.revision.credentials.some((other) => other.credentialId === entry.credentialId && other.revision === entry.revision)) && left.identifierBindingRevision === right.identifierBindingRevision && sameIdentifier(left.identifier, right.identifier) && Redacted.value(left.verifier) === Redacted.value(right.verifier);
110
81
  const evidenceSatisfiedAt = (evidence, requirement, now) => {
111
82
  if (evidence.proofs.length > 64 || evidence.revision.credentials.length > 64) return false;
112
83
  const revisions = /* @__PURE__ */ new Map();
@@ -197,54 +168,6 @@ const makePasswordKernel = (operations, proofs) => {
197
168
  }).pipe(Effect.flatMap(snapshotPasswordCredential))
198
169
  };
199
170
  });
200
- const scopeEntries = (keys, policy) => {
201
- const values = [{
202
- kind: "action",
203
- key: keys.action,
204
- ...policy.action
205
- }, {
206
- kind: "identifier",
207
- key: keys.identifier,
208
- ...policy.identifier
209
- }];
210
- if (keys.subject !== void 0) values.push({
211
- kind: "subject",
212
- key: keys.subject,
213
- ...policy.subject
214
- });
215
- return values;
216
- };
217
- const lockScope = Effect.fn("DrizzlePassword.lockScope")(function* (mapping, configuration, moduleId, action, entry) {
218
- const database = yield* CurrentPasswordSql;
219
- const query = database.insert(mapping.rateScope.table).values(mapping.rateScope.encodeInsert({
220
- moduleId,
221
- action,
222
- scopeKind: entry.kind,
223
- scopeKey: entry.key
224
- }));
225
- yield* configuration.insertIfAbsent(query, mapping.rateScope.scopeKey, entry.key);
226
- const c = scopeColumns(mapping);
227
- yield* selectRows(database.select().from(mapping.rateScope.table).where(and(eq(c.moduleId, moduleId), eq(c.action, action), eq(c.scopeKind, entry.kind), eq(c.scopeKey, entry.key))).limit(1), configuration.locking);
228
- });
229
- const scopeAdmits = Effect.fn("DrizzlePassword.scopeAdmits")(function* (mapping, moduleId, action, entry, policy, identifier, nativeSubjectId, now) {
230
- const database = yield* CurrentPasswordSql;
231
- const c = chargeColumns(mapping);
232
- if ((yield* selectRows(database.select({ attemptId: c.attemptId }).from(mapping.charge.table).where(and(eq(c.moduleId, moduleId), eq(c.action, action), eq(c.scopeKind, entry.kind), eq(c.scopeKey, entry.key), gte(c.occurredAt, mapping.encodeInstant(now - entry.windowMillis)))).limit(entry.limit), true)).length >= entry.limit) return false;
233
- const a = attemptColumns(mapping);
234
- const pendingWhere = and(entry.kind === "action" ? and(eq(a.moduleId, moduleId), eq(a.action, action)) : entry.kind === "identifier" ? and(eq(a.moduleId, moduleId), eq(a.action, action), eq(a.identifierNamespace, identifier.namespace), eq(a.identifierValue, identifier.value)) : and(eq(a.moduleId, moduleId), eq(a.action, action), eq(a.subjectId, nativeSubjectId)), eq(a.state, "pending"), gt(a.retentionUntil, mapping.encodeInstant(now)));
235
- return (yield* selectRows(database.select({ attemptId: a.attemptId }).from(mapping.attempt.table).where(pendingWhere).limit(policy.maximumPending), true)).length < policy.maximumPending;
236
- });
237
- const insertCharge = Effect.fn("Drizzle.insertCharge")(function* (mapping, moduleId, action, entry, attemptId, now) {
238
- return yield* (yield* CurrentPasswordSql).insert(mapping.charge.table).values(mapping.charge.encodeInsert({
239
- moduleId,
240
- action,
241
- scopeKind: entry.kind,
242
- scopeKey: entry.key,
243
- attemptId,
244
- occurredAtMillis: now,
245
- retentionUntilMillis: now + entry.windowMillis
246
- }));
247
- });
248
171
  const owned = (database, mapping, configuration, body) => {
249
172
  if (!validConstraints(mapping)) return Effect.fail(unavailable());
250
173
  const run = coordinateCommit(() => database.transaction((transaction) => body.pipe(Effect.provideService(CurrentPasswordSql, transaction), Effect.provideService(CurrentProofSql, transaction))), { mode: configuration.mode }).pipe(Effect.map((result) => result.value));
@@ -254,6 +177,15 @@ const makePasswordKernel = (operations, proofs) => {
254
177
  return yield* run;
255
178
  }) : run;
256
179
  };
180
+ const registerFinal = Effect.fnUntraced(function* (check) {
181
+ const postconditions = yield* Effect.serviceOption(PasswordPreparedPostconditions);
182
+ if (Option.isNone(postconditions)) return;
183
+ const postcondition = Effect.gen(function* () {
184
+ const transaction = yield* CurrentPasswordPreparedTransaction;
185
+ if (!(yield* check.pipe(Effect.provideService(CurrentPasswordSql, transaction)))) return yield* unavailable();
186
+ }).pipe(translateFailure);
187
+ if (!postconditions.value.register(postcondition)) return yield* unavailable();
188
+ });
257
189
  const safeRead = (database, configuration, body) => Effect.gen(function* () {
258
190
  if (configuration.coordinated !== true) {
259
191
  if (yield* hasCommitScope) return yield* unavailable();
@@ -317,7 +249,7 @@ const makePasswordKernel = (operations, proofs) => {
317
249
  identifier,
318
250
  credential
319
251
  });
320
- if (!sameCredentialSnapshot(snapshot, input.credential)) return void 0;
252
+ if (!samePasswordCredentialSnapshot(snapshot, input.credential)) return void 0;
321
253
  }
322
254
  const currentRequirement = yield* mapping.subject.decodeActionRequirement(row, action);
323
255
  const now = yield* nowMillis;
@@ -383,10 +315,9 @@ const makePasswordKernel = (operations, proofs) => {
383
315
  }));
384
316
  yield* database.update(mapping.subject.table).set(updateValues([[mapping.subject.securityRevision, nextSecurityRevision]])).where(and(eq(subjectColumns(mapping).id, authority.nativeSubjectId), eq(subjectColumns(mapping).securityRevision, input.expectedRevision.securityRevision)));
385
317
  yield* insertCommand(mapping, input, "add-password", commandNow ?? authority.now);
386
- const inserted = (yield* readPasswordCredential(mapping, input.moduleId, authority.nativeSubjectId, false))[0];
387
- const authorityInserted = (yield* database.select().from(mapping.authorityCredential.table).where(and(eq(authorityCredentialColumns(mapping).subjectId, authority.nativeSubjectId), eq(authorityCredentialColumns(mapping).credentialId, credentialId), eq(authorityCredentialColumns(mapping).revision, credentialRevision))).limit(1))[0];
388
- const subjectUpdated = (yield* database.select().from(mapping.subject.table).where(and(eq(subjectColumns(mapping).id, authority.nativeSubjectId), eq(subjectColumns(mapping).securityRevision, nextSecurityRevision))).limit(1))[0];
389
- if (inserted === void 0 || inserted[mapping.credential.credentialId] !== credentialId || inserted[mapping.credential.credentialRevision] !== credentialRevision || inserted[mapping.credential.verifierVersion] !== verifierVersion || inserted[mapping.credential.verifier] !== Redacted.value(input.replacement.verifier) || inserted[mapping.credential.normalization] !== input.replacement.normalization || authorityInserted === void 0 || subjectUpdated === void 0 || !(yield* mutationPostconditions(mapping, input, authority.nativeSubjectId, subjectUpdated, authorityInserted, commandNow ?? authority.now))) return yield* unavailable();
318
+ const applied = checkMutationApplied(mapping, input, authority.nativeSubjectId, revisions, commandNow ?? authority.now);
319
+ if (!(yield* applied)) return yield* unavailable();
320
+ yield* registerFinal(applied);
390
321
  return true;
391
322
  });
392
323
  const makeSqlPasswordPersistence = Effect.fn("makeSqlPasswordPersistence")(function* (mapping, configuration) {
@@ -396,96 +327,11 @@ const makePasswordKernel = (operations, proofs) => {
396
327
  proof: configuration.proof?.mapping
397
328
  }) ?? Effect.void).pipe(Effect.mapError(unavailable));
398
329
  const hooks = yield* LifecycleHooks;
330
+ if (!validConstraints(mapping)) return yield* unavailable();
331
+ const root = yield* NativeDatabase;
332
+ const credentials = yield* makePasswordCredentials(yield* sqlMapping(() => nativeTables(root.$client.withoutTransforms(), root)).pipe(translateFailure), mapping, configuration);
399
333
  return PasswordPersistence.of({
400
- admitAttempt: (input, prepare) => Effect.gen(function* () {
401
- const attemptId = yield* allocate(configuration.mode, mapping.allocateAttemptId, mapping.allocateAttemptIdSync);
402
- return yield* owned(database, mapping, configuration, Effect.gen(function* () {
403
- const transaction = yield* CurrentPasswordSql;
404
- const journal = yield* CurrentCommitJournal;
405
- const resolved = yield* resolveCredential(mapping, input.moduleId, input.identifier, input.subjectId, configuration.locking);
406
- const keys = mapping.scopeKeys({
407
- moduleId: input.moduleId,
408
- action: input.action,
409
- identifier: input.identifier,
410
- ...resolved?.snapshot === void 0 ? {} : { subjectId: resolved.snapshot.revision.subjectId }
411
- });
412
- const scopes = scopeEntries(keys, input.policy);
413
- const admitted = [];
414
- yield* lockScope(mapping, configuration, input.moduleId, input.action, scopes[0]);
415
- let currentNow = yield* nowMillis;
416
- if (yield* scopeAdmits(mapping, input.moduleId, input.action, scopes[0], input.policy, input.identifier, resolved?.nativeSubjectId, currentNow)) admitted.push(scopes[0]);
417
- if (admitted.length === 1) for (const entry of scopes.slice(1)) {
418
- yield* lockScope(mapping, configuration, input.moduleId, input.action, entry);
419
- currentNow = yield* nowMillis;
420
- if (yield* scopeAdmits(mapping, input.moduleId, input.action, entry, input.policy, input.identifier, resolved?.nativeSubjectId, currentNow)) admitted.push(entry);
421
- }
422
- const now = yield* nowMillis;
423
- for (const entry of admitted) yield* insertCharge(mapping, input.moduleId, input.action, entry, attemptId, now);
424
- if (admitted.length !== scopes.length) return prepare({ _tag: "Denied" }, journal);
425
- const snapshot = resolved?.snapshot;
426
- const deadline = now + input.policy.attemptLifetimeMillis;
427
- const retentionUntil = Math.max(deadline, ...scopes.map((entry) => now + entry.windowMillis));
428
- yield* transaction.insert(mapping.attempt.table).values(mapping.attempt.encodeInsert({
429
- moduleId: input.moduleId,
430
- action: input.action,
431
- attemptId,
432
- identifier: input.identifier,
433
- ...snapshot === void 0 ? {} : {
434
- subjectId: snapshot.revision.subjectId,
435
- credentialId: snapshot.credentialId,
436
- securityRevision: snapshot.revision.securityRevision,
437
- credentialRevision: snapshot.credentialRevision,
438
- verifierVersion: snapshot.verifierVersion,
439
- identifierBindingRevision: snapshot.identifierBindingRevision
440
- },
441
- admittedAtMillis: now,
442
- deadlineMillis: deadline,
443
- retentionUntilMillis: retentionUntil
444
- }, {
445
- ...resolved?.nativeSubjectId === void 0 ? {} : { nativeSubjectId: resolved.nativeSubjectId },
446
- state: "pending"
447
- }));
448
- return prepare({
449
- _tag: "Admitted",
450
- attemptId,
451
- ...snapshot === void 0 ? {} : { credential: snapshot }
452
- }, journal);
453
- }));
454
- }).pipe(Effect.provideService(CurrentPasswordSql, database), Effect.provideService(LifecycleHooks, hooks), translateFailure),
455
- settleAttempt: (uncaptured, prepare) => Effect.gen(function* () {
456
- const captured = uncaptured.captured === void 0 ? void 0 : yield* snapshotPasswordCredential(uncaptured.captured);
457
- const input = {
458
- ...uncaptured,
459
- ...captured === void 0 ? {} : { captured }
460
- };
461
- return yield* owned(database, mapping, configuration, Effect.gen(function* () {
462
- const transaction = yield* CurrentPasswordSql;
463
- const journal = yield* CurrentCommitJournal;
464
- const c = attemptColumns(mapping);
465
- let row = (yield* selectRows(transaction.select().from(mapping.attempt.table).where(and(eq(c.moduleId, input.moduleId), eq(c.attemptId, input.attemptId))).limit(1), false))[0];
466
- if (row === void 0 || row[mapping.attempt.state] !== "pending") return prepare("rejected", journal);
467
- let decision = "rejected";
468
- let current;
469
- let attemptMatches = false;
470
- if (input.outcome === "verified" && input.captured !== void 0) {
471
- current = yield* resolveCredential(mapping, input.moduleId, input.captured.identifier, input.captured.revision.subjectId, configuration.locking);
472
- attemptMatches = input.captured.moduleId === input.moduleId && row[mapping.attempt.identifierNamespace] === input.captured.identifier.namespace && row[mapping.attempt.identifierValue] === input.captured.identifier.value && mapping.subjectId.equals(row[mapping.attempt.subjectId], current?.nativeSubjectId) && row[mapping.attempt.credentialId] === input.captured.credentialId && row[mapping.attempt.securityRevision] === input.captured.revision.securityRevision && row[mapping.attempt.credentialRevision] === input.captured.credentialRevision && row[mapping.attempt.verifierVersion] === input.captured.verifierVersion && row[mapping.attempt.identifierBindingRevision] === input.captured.identifierBindingRevision;
473
- }
474
- row = (yield* selectRows(transaction.select().from(mapping.attempt.table).where(and(eq(c.moduleId, input.moduleId), eq(c.attemptId, input.attemptId))).limit(1), configuration.locking))[0];
475
- if (row === void 0 || row[mapping.attempt.state] !== "pending") return prepare("rejected", journal);
476
- const now = yield* nowMillis;
477
- const deadline = yield* mapping.decodeInstant(row[mapping.attempt.deadline]);
478
- if (attemptMatches && current?.snapshot !== void 0 && input.captured !== void 0 && deadline > now && sameCredentialSnapshot(current.snapshot, input.captured)) decision = "verified";
479
- const prepared = prepare(decision, journal);
480
- yield* transaction.update(mapping.attempt.table).set(updateValues([[mapping.attempt.state, decision]])).where(and(eq(c.moduleId, input.moduleId), eq(c.attemptId, input.attemptId), eq(c.state, "pending")));
481
- if (decision === "verified" && input.rehash !== void 0 && current?.snapshot !== void 0) {
482
- const cc = credentialColumns(mapping);
483
- const nextVersion = yield* allocate(configuration.mode, mapping.allocateRevision, mapping.allocateRevisionSync);
484
- yield* transaction.update(mapping.credential.table).set(mapping.credential.encodeVerifier(input.rehash.nextVerifier, nextVersion)).where(and(eq(cc.moduleId, input.moduleId), eq(cc.subjectId, current.nativeSubjectId), eq(cc.credentialId, current.snapshot.credentialId), eq(cc.credentialRevision, current.snapshot.credentialRevision), eq(cc.verifierVersion, input.rehash.expectedVersion), eq(cc.verifier, Redacted.value(input.rehash.expectedVerifier))));
485
- }
486
- return prepared;
487
- }));
488
- }).pipe(Effect.provideService(CurrentPasswordSql, database), Effect.provideService(LifecycleHooks, hooks), translateFailure),
334
+ ...credentials,
489
335
  readForSubject: (input) => safeRead(database, configuration, Effect.gen(function* () {
490
336
  const transaction = yield* CurrentPasswordSql;
491
337
  const locked = yield* lockSubject(mapping, input.subjectId, false);
@@ -565,72 +411,20 @@ const makePasswordKernel = (operations, proofs) => {
565
411
  if (!(yield* currentExpectedCredential(mapping, input, authority.nativeSubjectId))) return prepare("rejected", journal);
566
412
  let passwordReceipt;
567
413
  yield* completeProofPlanIn(configuration.proof.mapping, configuration.proof.configuration, input.completion, Effect.gen(function* () {
568
- return yield* writeReplacement(mapping, input, authority.nativeSubjectId, {
414
+ const revisions = {
569
415
  credentialRevision,
570
416
  verifierVersion,
571
417
  nextSecurityRevision
572
- }, authority.now);
418
+ };
419
+ const applied = yield* writeReplacement(mapping, input, authority.nativeSubjectId, revisions, authority.now);
420
+ if (applied) yield* registerReplacement(mapping, input, authority.nativeSubjectId, revisions, authority.now);
421
+ return applied;
573
422
  }), (decision) => {
574
423
  passwordReceipt = prepare(decision === "completed" ? "changed" : "rejected", journal);
575
424
  return decision;
576
425
  });
577
426
  return passwordReceipt ?? prepare("rejected", journal);
578
427
  }));
579
- })).pipe(Effect.provideService(CurrentPasswordSql, database), Effect.provideService(LifecycleHooks, hooks), translateFailure),
580
- cleanupAttempts: (input, prepare) => owned(database, mapping, configuration, Effect.gen(function* () {
581
- const transaction = yield* CurrentPasswordSql;
582
- const journal = yield* CurrentCommitJournal;
583
- const now = yield* nowMillis;
584
- const nativeNow = mapping.encodeInstant(now);
585
- let remaining = input.limit;
586
- let hasMore = false;
587
- const a = attemptColumns(mapping);
588
- const c = chargeColumns(mapping);
589
- const attempts = yield* selectRows(transaction.select({ attemptId: a.attemptId }).from(mapping.attempt.table).where(and(eq(a.moduleId, input.moduleId), lte(a.retentionUntil, nativeNow))).limit(remaining + 1), false);
590
- if (attempts.length > remaining) hasMore = true;
591
- const attemptCandidates = attempts.slice(0, remaining);
592
- remaining -= attemptCandidates.length;
593
- const charges = yield* selectRows(transaction.select({
594
- action: c.action,
595
- scopeKind: c.scopeKind,
596
- scopeKey: c.scopeKey,
597
- attemptId: c.attemptId
598
- }).from(mapping.charge.table).where(and(eq(c.moduleId, input.moduleId), lte(c.retentionUntil, nativeNow))).limit(remaining + 1), false);
599
- if (charges.length > remaining) hasMore = true;
600
- const chargeCandidates = charges.slice(0, remaining);
601
- remaining -= chargeCandidates.length;
602
- const sc = scopeColumns(mapping);
603
- const scopes = yield* selectRows(transaction.select({
604
- action: sc.action,
605
- scopeKind: sc.scopeKind,
606
- scopeKey: sc.scopeKey
607
- }).from(mapping.rateScope.table).where(and(eq(sc.moduleId, input.moduleId), notExists(transaction.select({ one: sql`1` }).from(mapping.charge.table).where(and(eq(c.moduleId, sc.moduleId), eq(c.action, sc.action), eq(c.scopeKind, sc.scopeKind), eq(c.scopeKey, sc.scopeKey)))))).limit(remaining + 1), remaining === 0 ? false : configuration.locking);
608
- if (scopes.length > remaining) hasMore = true;
609
- const selectedScopes = scopes.slice(0, remaining);
610
- remaining -= selectedScopes.length;
611
- const selectedCharges = [];
612
- for (const candidate of chargeCandidates) {
613
- const locked = yield* selectRows(transaction.select({
614
- action: c.action,
615
- scopeKind: c.scopeKind,
616
- scopeKey: c.scopeKey,
617
- attemptId: c.attemptId
618
- }).from(mapping.charge.table).where(and(eq(c.moduleId, input.moduleId), eq(c.action, candidate.action), eq(c.scopeKind, candidate.scopeKind), eq(c.scopeKey, candidate.scopeKey), eq(c.attemptId, candidate.attemptId), lte(c.retentionUntil, nativeNow))).limit(1), configuration.locking);
619
- if (locked[0] !== void 0) selectedCharges.push(locked[0]);
620
- }
621
- const selectedAttempts = [];
622
- for (const candidate of attemptCandidates) {
623
- const locked = yield* selectRows(transaction.select({ attemptId: a.attemptId }).from(mapping.attempt.table).where(and(eq(a.moduleId, input.moduleId), eq(a.attemptId, candidate.attemptId), lte(a.retentionUntil, nativeNow))).limit(1), configuration.locking);
624
- if (locked[0] !== void 0) selectedAttempts.push(locked[0]);
625
- }
626
- const prepared = prepare({
627
- removed: selectedAttempts.length + selectedCharges.length + selectedScopes.length,
628
- hasMore
629
- }, journal);
630
- for (const row of selectedCharges) yield* transaction.delete(mapping.charge.table).where(and(eq(c.moduleId, input.moduleId), eq(c.action, row.action), eq(c.scopeKind, row.scopeKind), eq(c.scopeKey, row.scopeKey), eq(c.attemptId, row.attemptId), lte(c.retentionUntil, nativeNow)));
631
- if (selectedAttempts.length > 0) yield* transaction.delete(mapping.attempt.table).where(and(eq(a.moduleId, input.moduleId), inArray(a.attemptId, selectedAttempts.map((row) => row.attemptId)), lte(a.retentionUntil, nativeNow)));
632
- for (const row of selectedScopes) yield* transaction.delete(mapping.rateScope.table).where(and(eq(sc.moduleId, input.moduleId), eq(sc.action, row.action), eq(sc.scopeKind, row.scopeKind), eq(sc.scopeKey, row.scopeKey), notExists(transaction.select({ one: sql`1` }).from(mapping.charge.table).where(and(eq(c.moduleId, sc.moduleId), eq(c.action, sc.action), eq(c.scopeKind, sc.scopeKind), eq(c.scopeKey, sc.scopeKey))))));
633
- return prepared;
634
428
  })).pipe(Effect.provideService(CurrentPasswordSql, database), Effect.provideService(LifecycleHooks, hooks), translateFailure)
635
429
  });
636
430
  });
@@ -646,7 +440,7 @@ const makePasswordKernel = (operations, proofs) => {
646
440
  const credential = (yield* database.select().from(mapping.credential.table).where(and(eq(c.moduleId, input.moduleId), eq(c.subjectId, nativeSubjectId), eq(c.credentialId, revisions.credentialId), eq(c.credentialRevision, revisions.credentialRevision), eq(c.verifierVersion, revisions.verifierVersion), eq(c.verifier, Redacted.value(input.replacement.verifier)), eq(c.normalization, input.replacement.normalization))).limit(1))[0];
647
441
  const subject = (yield* database.select().from(mapping.subject.table).where(and(eq(s.id, nativeSubjectId), eq(s.securityRevision, revisions.nextSecurityRevision))).limit(1))[0];
648
442
  const authorityCredential = (yield* database.select().from(mapping.authorityCredential.table).where(and(eq(authorityCredentialColumns(mapping).subjectId, nativeSubjectId), eq(authorityCredentialColumns(mapping).credentialId, revisions.credentialId), eq(authorityCredentialColumns(mapping).revision, revisions.credentialRevision))).limit(1))[0];
649
- return credential !== void 0 && (yield* mutationPostconditions(mapping, input, nativeSubjectId, subject, authorityCredential, now));
443
+ return credential !== void 0 && credential[mapping.credential.credentialId] === revisions.credentialId && credential[mapping.credential.credentialRevision] === revisions.credentialRevision && credential[mapping.credential.verifierVersion] === revisions.verifierVersion && credential[mapping.credential.verifier] === Redacted.value(input.replacement.verifier) && credential[mapping.credential.normalization] === input.replacement.normalization && (yield* mutationPostconditions(mapping, input, nativeSubjectId, subject, authorityCredential, now));
650
444
  });
651
445
  const writeReplacement = Effect.fn("DrizzlePassword.writeReplacement")(function* (mapping, input, nativeSubjectId, revisions, now) {
652
446
  const database = yield* CurrentPasswordSql;
@@ -665,6 +459,10 @@ const makePasswordKernel = (operations, proofs) => {
665
459
  credentialId: input.credential.credentialId
666
460
  }, now);
667
461
  });
462
+ const registerReplacement = (mapping, input, nativeSubjectId, revisions, now) => registerFinal(checkMutationApplied(mapping, input, nativeSubjectId, {
463
+ ...revisions,
464
+ credentialId: input.credential.credentialId
465
+ }, now));
668
466
  const replaceIn = Effect.fn("Drizzle.replaceIn")(function* (mapping, configuration, input, action, revisions, prepare) {
669
467
  const journal = yield* CurrentCommitJournal;
670
468
  const authority = yield* validateMutationAuthority(mapping, configuration, input, action);
@@ -673,6 +471,7 @@ const makePasswordKernel = (operations, proofs) => {
673
471
  if (!(yield* currentExpectedCredential(mapping, input, authority.nativeSubjectId))) return prepare("rejected", journal);
674
472
  const prepared = prepare("changed", journal);
675
473
  if (!(yield* writeReplacement(mapping, input, authority.nativeSubjectId, revisions, authority.now))) return yield* unavailable();
474
+ yield* registerReplacement(mapping, input, authority.nativeSubjectId, revisions, authority.now);
676
475
  return prepared;
677
476
  });
678
477
  return {
@@ -694,7 +493,7 @@ const makePasswordKernel = (operations, proofs) => {
694
493
  checkMutationApplied,
695
494
  writeReplacement,
696
495
  evidenceSatisfiedAt,
697
- sameCredentialSnapshot,
496
+ sameCredentialSnapshot: samePasswordCredentialSnapshot,
698
497
  subjectColumns,
699
498
  identifierColumns,
700
499
  credentialColumns,