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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +11 -7
  2. package/dist/Adapter.d.mts +1 -1
  3. package/dist/Adapter.mjs +8 -8
  4. package/dist/OAuthPersistence.mjs +10 -5
  5. package/dist/OAuthPersistence.mjs.map +1 -1
  6. package/dist/internal/configuration.d.mts +49 -13
  7. package/dist/internal/configuration.mjs.map +1 -1
  8. package/dist/internal/email-native.mjs +10 -2
  9. package/dist/internal/email-native.mjs.map +1 -1
  10. package/dist/internal/email-policy.mjs +3 -2
  11. package/dist/internal/email-policy.mjs.map +1 -1
  12. package/dist/internal/models/oauth-model.d.mts +4 -2
  13. package/dist/internal/models/oauth-model.mjs.map +1 -1
  14. package/dist/internal/models/password-model.d.mts +2 -1
  15. package/dist/internal/models/password-model.mjs.map +1 -1
  16. package/dist/internal/oauth/native-accounts.d.mts +1 -1
  17. package/dist/internal/oauth/native-accounts.mjs +6 -6
  18. package/dist/internal/oauth/native-accounts.mjs.map +1 -1
  19. package/dist/internal/oauth/native-connected.d.mts +1 -1
  20. package/dist/internal/oauth/native-connected.mjs +1 -1
  21. package/dist/internal/oauth/native-registration.mjs +19 -8
  22. package/dist/internal/oauth/native-registration.mjs.map +1 -1
  23. package/dist/internal/oauth/native-sign-in.d.mts +1 -1
  24. package/dist/internal/oauth/native-sign-in.mjs +1 -1
  25. package/dist/internal/oauth/native-state.mjs +21 -11
  26. package/dist/internal/oauth/native-state.mjs.map +1 -1
  27. package/dist/internal/oauth/state.d.mts +1 -1
  28. package/dist/internal/oauth/state.mjs +1 -1
  29. package/dist/internal/oauth-managed.mjs +182 -0
  30. package/dist/internal/oauth-managed.mjs.map +1 -0
  31. package/dist/internal/passkey-managed.mjs +5 -3
  32. package/dist/internal/passkey-managed.mjs.map +1 -1
  33. package/dist/internal/passkey-native-registration.mjs +1 -1
  34. package/dist/internal/passkey-native-state.mjs +33 -23
  35. package/dist/internal/passkey-native-state.mjs.map +1 -1
  36. package/dist/internal/passkey-native-write.mjs +3 -3
  37. package/dist/internal/passkey-native-write.mjs.map +1 -1
  38. package/dist/internal/passkey-native.d.mts +15 -7
  39. package/dist/internal/passkey-native.mjs +2 -1
  40. package/dist/internal/passkey-native.mjs.map +1 -1
  41. package/dist/internal/passkey-policy.mjs +9 -7
  42. package/dist/internal/passkey-policy.mjs.map +1 -1
  43. package/dist/internal/password-native.mjs +22 -5
  44. package/dist/internal/password-native.mjs.map +1 -1
  45. package/dist/internal/password-policy.mjs +7 -6
  46. package/dist/internal/password-policy.mjs.map +1 -1
  47. package/dist/internal/password-registration-native.d.mts +1 -1
  48. package/dist/internal/persistence.d.mts +3 -0
  49. package/dist/internal/persistence.mjs +99 -42
  50. package/dist/internal/persistence.mjs.map +1 -1
  51. package/dist/internal/phone-native.mjs +1 -1
  52. package/dist/internal/phone-native.mjs.map +1 -1
  53. package/dist/internal/registration.mjs +17 -8
  54. package/dist/internal/registration.mjs.map +1 -1
  55. package/dist/internal/session-native-authority.mjs +8 -6
  56. package/dist/internal/session-native-authority.mjs.map +1 -1
  57. package/dist/internal/session-native-login.d.mts +2 -2
  58. package/dist/internal/session-native-login.mjs +4 -2
  59. package/dist/internal/session-native-login.mjs.map +1 -1
  60. package/dist/internal/session-native-pending-read.mjs +26 -20
  61. package/dist/internal/session-native-pending-read.mjs.map +1 -1
  62. package/dist/internal/session-native-record.mjs +6 -4
  63. package/dist/internal/session-native-record.mjs.map +1 -1
  64. package/dist/internal/session-native-state.mjs +24 -8
  65. package/dist/internal/session-native-state.mjs.map +1 -1
  66. package/dist/internal/session-native-stateful.d.mts +2 -2
  67. package/dist/internal/session-native-stateful.mjs +67 -38
  68. package/dist/internal/session-native-stateful.mjs.map +1 -1
  69. package/dist/internal/session-native-step-up.d.mts +2 -2
  70. package/dist/internal/session-native-step-up.mjs +17 -19
  71. package/dist/internal/session-native-step-up.mjs.map +1 -1
  72. package/dist/internal/session-native-validity.mjs +3 -1
  73. package/dist/internal/session-native-validity.mjs.map +1 -1
  74. package/dist/internal/sql-authority.mjs +5 -1
  75. package/dist/internal/sql-authority.mjs.map +1 -1
  76. package/dist/internal/sql-storage-validation.mjs +2 -1
  77. package/dist/internal/sql-storage-validation.mjs.map +1 -1
  78. package/dist/internal/sql-table.d.mts +1 -0
  79. package/dist/internal/sql-table.mjs.map +1 -1
  80. package/dist/internal/sqlite-policy-guard.mjs +23 -0
  81. package/dist/internal/sqlite-policy-guard.mjs.map +1 -0
  82. package/dist/internal/step-up-state.mjs +19 -6
  83. package/dist/internal/step-up-state.mjs.map +1 -1
  84. package/dist/internal/storage-mapping.d.mts +9 -5
  85. package/dist/internal/storage-mapping.mjs +17 -5
  86. package/dist/internal/storage-mapping.mjs.map +1 -1
  87. package/dist/internal/storage-tables.d.mts +237 -0
  88. package/dist/internal/storage-tables.mjs +68 -0
  89. package/dist/internal/storage-tables.mjs.map +1 -1
  90. package/dist/internal/storage-validation.d.mts +3 -1
  91. package/dist/internal/storage-validation.mjs +70 -17
  92. package/dist/internal/storage-validation.mjs.map +1 -1
  93. package/dist/internal/totp-native.mjs +1 -1
  94. package/package.json +1 -1
  95. package/src/OAuthPersistence.ts +7 -2
  96. package/src/internal/configuration.ts +72 -15
  97. package/src/internal/email-native.ts +16 -6
  98. package/src/internal/email-policy.ts +12 -3
  99. package/src/internal/models/oauth-model.ts +7 -1
  100. package/src/internal/models/password-model.ts +2 -1
  101. package/src/internal/oauth/native-accounts.ts +3 -3
  102. package/src/internal/oauth/native-registration.ts +17 -6
  103. package/src/internal/oauth/native-state.ts +38 -18
  104. package/src/internal/oauth-managed.ts +277 -0
  105. package/src/internal/passkey-managed.ts +8 -2
  106. package/src/internal/passkey-native-state.ts +59 -52
  107. package/src/internal/passkey-native-write.ts +2 -2
  108. package/src/internal/passkey-native.ts +4 -2
  109. package/src/internal/passkey-policy.ts +13 -9
  110. package/src/internal/password-native.ts +45 -11
  111. package/src/internal/password-policy.ts +16 -8
  112. package/src/internal/persistence.ts +360 -204
  113. package/src/internal/phone-native.ts +1 -1
  114. package/src/internal/registration.ts +53 -40
  115. package/src/internal/session-native-authority.ts +26 -8
  116. package/src/internal/session-native-login.ts +9 -4
  117. package/src/internal/session-native-pending-read.ts +46 -33
  118. package/src/internal/session-native-record.ts +6 -4
  119. package/src/internal/session-native-state.ts +32 -10
  120. package/src/internal/session-native-stateful.ts +147 -52
  121. package/src/internal/session-native-step-up.ts +31 -24
  122. package/src/internal/session-native-validity.ts +3 -1
  123. package/src/internal/sql-authority.ts +5 -1
  124. package/src/internal/sql-storage-validation.ts +53 -46
  125. package/src/internal/sql-table.ts +1 -0
  126. package/src/internal/sqlite-policy-guard.ts +34 -0
  127. package/src/internal/step-up-state.ts +43 -6
  128. package/src/internal/storage-mapping.ts +40 -4
  129. package/src/internal/storage-tables.ts +76 -0
  130. package/src/internal/storage-validation.ts +127 -27
@@ -1 +1 @@
1
- {"version":3,"file":"native-state.mjs","names":[],"sources":["../../../src/internal/oauth/native-state.ts"],"sourcesContent":["import {\n OAuthAccountRevision,\n OAuthCredentialSnapshot,\n OAuthExternalIdentity,\n type OAuthCredentialKey,\n} from \"@yielded/auth/OAuth\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport { Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport type { OAuthSignInMapping } from \"../models/oauth-model\";\nimport type { NativeSqlTables } from \"../native-sql-table\";\nimport { exactSqlText } from \"../sql-change\";\nimport type { SqlExpression, TableModel } from \"../table-model\";\nimport { invariant, oauthIdentityKey } from \"./state\";\n\n// Expression handles are checked by the physical compiler, not domain codecs.\nexport type OAuthNativeReadMapping = OAuthSignInMapping<\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n SqlExpression\n>;\n\ntype Row = Readonly<Record<string, unknown>>;\n\n/** Unlocked snapshots and explicit subject-first reads share decoding rules.\n * No observation registry or discovered-identity placeholder is retained. */\nexport const makeOAuthNativeAuthority = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: Pick<OAuthNativeReadMapping, \"subject\" | \"authority\" | \"subjectId\" | \"clock\">,\n) {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n const subject = tables(mapping.subject.table);\n const authority = tables(mapping.authority.table);\n const s = subject.as(\"oauth_subject\");\n const a = authority.as(\"oauth_authority\");\n const now = tables.expression(mapping.clock.engineNowMillis);\n\n const lock = sql.onDialectOrElse({\n pg: () => sql`for update`,\n mysql: () => sql`for update`,\n orElse: () => sql``,\n });\n\n const active = (kind: \"subject\" | \"authority\", alias: string) => {\n const descriptor = mapping[kind];\n const physical = tables(descriptor.table);\n\n return sql`(select * from ${physical.name} where ${tables.expression(descriptor.activeCondition)}) as ${sql(alias)}`;\n };\n\n const revision = Effect.fnUntraced(function* (\n subjectRow: Row,\n factors: ReadonlyArray<Row>,\n nativeId: unknown,\n ) {\n invariant(mapping.subject.isActiveStatus(subjectRow[mapping.subject.status]));\n invariant(mapping.subjectId.equals(subjectRow[mapping.subject.id], nativeId));\n invariant(factors.length > 0 && factors.length <= 64);\n invariant(\n factors.every(\n (row) =>\n mapping.subjectId.equals(row[mapping.authority.subjectId], nativeId) &&\n mapping.authority.isActiveStatus(row[mapping.authority.status]),\n ),\n );\n\n const credentials = factors.map((row) => ({\n credentialId: row[mapping.authority.credentialId],\n revision: row[mapping.authority.revision],\n }));\n\n invariant(new Set(credentials.map((item) => item.credentialId)).size === credentials.length);\n\n const value = yield* Schema.decodeUnknownEffect(Schema.toType(OAuthAccountRevision))({\n subjectId: yield* mapping.subjectId.toSubject(nativeId),\n securityRevision: subjectRow[mapping.subject.securityRevision],\n credentials,\n });\n\n return {\n ...value,\n credentials: [...value.credentials].sort((left, right) =>\n left.credentialId.localeCompare(right.credentialId),\n ),\n };\n });\n\n const captureSubject = Effect.fnUntraced(function* (subjectId: SubjectId, locking: boolean) {\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n const rows =\n yield* sql`select ${subject.fields(\"s_\")}, ${now} as engine_now from ${subject.name} where ${subject.column(mapping.subject.id)} = ${subject.value(mapping.subject.id, nativeId)} and ${tables.expression(mapping.subject.activeCondition)} limit 2 ${locking ? lock : sql``}`;\n\n invariant(rows.length <= 1);\n if (rows[0] === undefined) return undefined;\n const row = subject.decode(rows[0], \"s_\");\n\n invariant(mapping.subjectId.equals(row[mapping.subject.id], nativeId));\n\n return {\n nativeId,\n subject: row,\n securityRevision: yield* Schema.decodeUnknownEffect(\n OAuthAccountRevision.fields.securityRevision,\n )(row[mapping.subject.securityRevision]),\n now: yield* Schema.decodeEffect(Schema.Int)(Number(rows[0].engine_now)),\n };\n });\n\n const capture = Effect.fnUntraced(function* (subjectId: SubjectId, locking: boolean) {\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n if (locking) {\n // Never acquire a credential, identity, flow or policy lock before this.\n const subjects =\n yield* sql`select ${subject.fields(\"s_\")}, ${now} as engine_now from ${subject.name}\n where ${subject.column(mapping.subject.id)} = ${subject.value(mapping.subject.id, nativeId)}\n and ${tables.expression(mapping.subject.activeCondition)} ${lock}`;\n\n if (subjects.length === 0) return undefined;\n invariant(subjects.length === 1);\n const subjectRow = subject.decode(subjects[0]!, \"s_\");\n\n const factors = yield* sql`select ${authority.fields(\"a_\")} from ${authority.name}\n where ${authority.column(mapping.authority.subjectId)} = ${authority.value(mapping.authority.subjectId, nativeId)}\n and ${tables.expression(mapping.authority.activeCondition)}\n order by ${authority.column(mapping.authority.credentialId)} limit 65 ${lock}`;\n\n if (factors.length === 0) return undefined;\n\n return {\n nativeId,\n subject: subjectRow,\n revision: yield* revision(\n subjectRow,\n factors.map((row) => authority.decode(row, \"a_\")),\n nativeId,\n ),\n now: yield* Schema.decodeEffect(Schema.Int)(Number(subjects[0]!.engine_now)),\n };\n }\n\n // Bind each column's own native codec; the columns need not share an encoding.\n const rows = yield* sql`select ${s.fields(\"s_\")}, ${a.fields(\"a_\")}, ${now} as engine_now\n from ${active(\"subject\", \"oauth_subject\")}\n join ${active(\"authority\", \"oauth_authority\")} on\n ${a.column(mapping.authority.subjectId)} = ${a.value(mapping.authority.subjectId, nativeId)}\n where ${s.column(mapping.subject.id)} = ${s.value(mapping.subject.id, nativeId)}\n order by ${a.column(mapping.authority.credentialId)} limit 65`;\n\n if (rows.length === 0) return undefined;\n const subjectRow = s.decode(rows[0]!, \"s_\");\n\n return {\n nativeId,\n subject: subjectRow,\n revision: yield* revision(\n subjectRow,\n rows.map((row) => a.decode(row, \"a_\")),\n nativeId,\n ),\n now: yield* Schema.decodeEffect(Schema.Int)(Number(rows[0]!.engine_now)),\n };\n });\n\n return { sql, now, lock, subject, authority, capture, captureSubject, revision };\n});\n\nexport const makeOAuthNativeState = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: OAuthNativeReadMapping,\n) {\n const state = yield* makeOAuthNativeAuthority(tables, mapping);\n const { sql, subject, authority, revision } = state;\n const ownership = tables(mapping.ownership.table);\n const credential = tables(mapping.credential.table);\n const s = subject.as(\"oauth_subject\");\n const o = ownership.as(\"oauth_identity\");\n const c = credential.as(\"oauth_credential\");\n const a = authority.as(\"oauth_authority\");\n\n const exact = (\n left: Parameters<typeof exactSqlText>[1],\n right: Parameters<typeof exactSqlText>[2],\n ) => exactSqlText(sql, left, right);\n\n const active = (kind: \"subject\" | \"credential\" | \"authority\", alias: string) => {\n const descriptor = mapping[kind];\n const physical = tables(descriptor.table);\n\n return sql`(select * from ${physical.name} where ${tables.expression(descriptor.activeCondition)}) as ${sql(alias)}`;\n };\n\n const joinedIds =\n ownership.unencodedTextColumn?.(mapping.ownership.subjectId) !== undefined &&\n subject.unencodedTextColumn?.(mapping.subject.id) !== undefined &&\n credential.unencodedTextColumn?.(mapping.credential.subjectId) !== undefined &&\n authority.unencodedTextColumn?.(mapping.authority.subjectId) !== undefined;\n\n const decodeSnapshot = Effect.fnUntraced(function* (\n rows: ReadonlyArray<Row>,\n moduleId: string,\n nativeId?: unknown,\n ) {\n if (rows.length === 0) return undefined;\n const owned = o.decode(rows[0]!, \"o_\");\n\n const identity = yield* Schema.decodeUnknownEffect(OAuthExternalIdentity)({\n provider: owned[mapping.ownership.provider],\n issuer: owned[mapping.ownership.issuer],\n subject: owned[mapping.ownership.externalSubject],\n });\n\n const identityKey = yield* oauthIdentityKey(identity);\n\n invariant(owned[mapping.ownership.identityKey] === identityKey);\n const ownerId = mapping.ownership.decodeSubjectId(owned);\n\n if (nativeId !== undefined) invariant(mapping.subjectId.equals(ownerId, nativeId));\n const login = c.decode(rows[0]!, \"c_\");\n\n invariant(mapping.subjectId.equals(login[mapping.credential.subjectId], ownerId));\n invariant(login[mapping.credential.identityKey] === identityKey);\n invariant(mapping.credential.isActiveStatus(login[mapping.credential.status]));\n\n const current = yield* revision(\n s.decode(rows[0]!, \"s_\"),\n rows.map((row) => a.decode(row, \"a_\")),\n ownerId,\n );\n\n const credentialId = login[mapping.credential.credentialId];\n const credentialRevision = login[mapping.credential.credentialRevision];\n\n if (\n !current.credentials.some(\n (item) => item.credentialId === credentialId && item.revision === credentialRevision,\n )\n )\n return undefined;\n\n return yield* Schema.decodeUnknownEffect(Schema.toType(OAuthCredentialSnapshot))({\n moduleId,\n identity,\n credentialId,\n credentialRevision,\n revision: current,\n requirement: mapping.subject.decodeAuthenticationRequirement(s.decode(rows[0]!, \"s_\")),\n });\n });\n\n const resolve = Effect.fnUntraced(function* (\n moduleId: string,\n identity: typeof OAuthExternalIdentity.Type,\n ) {\n const identityKey = yield* oauthIdentityKey(identity);\n let nativeId: unknown;\n\n if (!joinedIds) {\n const found = yield* sql`select ${ownership.fields(\"o_\")} from ${ownership.name}\n where ${exact(ownership.column(mapping.ownership.identityKey), ownership.value(mapping.ownership.identityKey, identityKey))}`;\n\n if (found.length === 0) return undefined;\n invariant(found.length === 1);\n const row = ownership.decode(found[0]!, \"o_\");\n\n invariant(\n row[mapping.ownership.provider] === identity.provider &&\n row[mapping.ownership.issuer] === identity.issuer &&\n row[mapping.ownership.externalSubject] === identity.subject,\n );\n nativeId = mapping.ownership.decodeSubjectId(row);\n }\n\n const bindOwner = (table: typeof s, key: string) =>\n joinedIds\n ? exact(table.column(key), o.column(mapping.ownership.subjectId))\n : sql`${table.column(key)} = ${table.value(key, nativeId)}`;\n\n const rows =\n yield* sql`select ${o.fields(\"o_\")}, ${s.fields(\"s_\")}, ${c.fields(\"c_\")}, ${a.fields(\"a_\")}\n from ${o.name}\n join ${active(\"subject\", \"oauth_subject\")} on ${bindOwner(s, mapping.subject.id)}\n join ${active(\"credential\", \"oauth_credential\")} on ${bindOwner(c, mapping.credential.subjectId)}\n and ${exact(c.column(mapping.credential.moduleId), c.value(mapping.credential.moduleId, moduleId))}\n and ${exact(c.column(mapping.credential.identityKey), c.value(mapping.credential.identityKey, identityKey))}\n join ${active(\"authority\", \"oauth_authority\")} on ${bindOwner(a, mapping.authority.subjectId)}\n where ${exact(o.column(mapping.ownership.identityKey), o.value(mapping.ownership.identityKey, identityKey))}\n order by ${a.column(mapping.authority.credentialId)} limit 65`;\n\n const result = yield* decodeSnapshot(rows, moduleId, joinedIds ? undefined : nativeId);\n\n if (result !== undefined)\n invariant(\n result.identity.provider === identity.provider &&\n result.identity.issuer === identity.issuer &&\n result.identity.subject === identity.subject,\n );\n\n return result;\n });\n\n const readCredential = Effect.fnUntraced(function* (\n input: typeof OAuthCredentialKey.Type,\n metadataAccess: Fragment = sql`true`,\n ) {\n const nativeId = yield* mapping.subjectId.toNative(input.subjectId);\n let identityKey: unknown;\n\n const direct =\n credential.unencodedTextColumn?.(mapping.credential.identityKey) !== undefined &&\n ownership.unencodedTextColumn?.(mapping.ownership.identityKey) !== undefined;\n\n if (!direct) {\n const selected = yield* sql`select ${credential.fields(\"c_\")} from ${credential.name}\n where ${exact(credential.column(mapping.credential.moduleId), credential.value(mapping.credential.moduleId, input.moduleId))}\n and ${exact(credential.column(mapping.credential.credentialId), credential.value(mapping.credential.credentialId, input.credentialId))}\n and ${credential.column(mapping.credential.subjectId)} = ${credential.value(mapping.credential.subjectId, nativeId)}`;\n\n if (selected.length === 0) return undefined;\n invariant(selected.length === 1);\n identityKey = credential.decode(selected[0]!, \"c_\")[mapping.credential.identityKey];\n }\n\n const owner = (table: typeof s, key: string) =>\n sql`${table.column(key)} = ${table.value(key, nativeId)}`;\n\n const rows =\n yield* sql`select ${o.fields(\"o_\")}, ${s.fields(\"s_\")}, ${c.fields(\"c_\")}, ${a.fields(\"a_\")}\n from ${active(\"credential\", \"oauth_credential\")}\n join ${o.name} on ${direct ? exact(o.column(mapping.ownership.identityKey), c.column(mapping.credential.identityKey)) : exact(o.column(mapping.ownership.identityKey), o.value(mapping.ownership.identityKey, identityKey))}\n and ${owner(o, mapping.ownership.subjectId)}\n join ${active(\"subject\", \"oauth_subject\")} on ${owner(s, mapping.subject.id)}\n join ${active(\"authority\", \"oauth_authority\")} on ${owner(a, mapping.authority.subjectId)}\n where ${metadataAccess} and ${owner(c, mapping.credential.subjectId)}\n and ${exact(c.column(mapping.credential.moduleId), c.value(mapping.credential.moduleId, input.moduleId))}\n and ${exact(c.column(mapping.credential.credentialId), c.value(mapping.credential.credentialId, input.credentialId))}\n order by ${a.column(mapping.authority.credentialId)} limit 65`;\n\n return yield* decodeSnapshot(rows, input.moduleId, nativeId);\n });\n\n return { ...state, ownership, credential, resolve, readCredential };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"native-state.mjs","names":[],"sources":["../../../src/internal/oauth/native-state.ts"],"sourcesContent":["import {\n OAuthAccountRevision,\n OAuthCredentialSnapshot,\n OAuthExternalIdentity,\n type OAuthCredentialKey,\n} from \"@yielded/auth/OAuth\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type { AuthenticationRequirement } from \"@yielded/auth/Sessions\";\nimport { Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport type { PersistenceMappingError } from \"../mapping-error\";\nimport type { OAuthSignInMapping, OAuthSubjectReadTable } from \"../models/oauth-model\";\nimport type { NativeSqlTables } from \"../native-sql-table\";\nimport { exactSqlText } from \"../sql-change\";\nimport { canJoinTextColumns, withStorageValidation } from \"../storage-validation\";\nimport type { SqlExpression, TableModel } from \"../table-model\";\nimport { invariant, oauthIdentityKey, unavailable } from \"./state\";\n\n// Expression handles are checked by the physical compiler, not domain codecs.\nexport type OAuthNativeReadMapping = OAuthSignInMapping<\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n SqlExpression\n>;\n\ntype Row = Readonly<Record<string, unknown>>;\n\nexport const decodeOAuthRequirement = Effect.fnUntraced(function* (\n subject: OAuthSubjectReadTable<TableModel>,\n row: Row,\n): Effect.fn.Return<AuthenticationRequirement, PersistenceMappingError> {\n const requirement = subject.decodeAuthenticationRequirement(row);\n\n return yield* Effect.isEffect(requirement) ? requirement : Effect.succeed(requirement);\n});\n\n/** Unlocked snapshots and explicit subject-first reads share decoding rules.\n * No observation registry or discovered-identity placeholder is retained. */\nexport const makeOAuthNativeAuthority = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: Pick<OAuthNativeReadMapping, \"subject\" | \"authority\" | \"subjectId\" | \"clock\">,\n) {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n const subject = tables(mapping.subject.table);\n const authority = tables(mapping.authority.table);\n const s = subject.as(\"oauth_subject\");\n const a = authority.as(\"oauth_authority\");\n const now = tables.expression(mapping.clock.engineNowMillis);\n\n const lock = sql.onDialectOrElse({\n pg: () => sql`for update`,\n mysql: () => sql`for update`,\n orElse: () => sql``,\n });\n\n const active = (kind: \"subject\" | \"authority\", alias: string) => {\n const descriptor = mapping[kind];\n const physical = tables(descriptor.table);\n\n return sql`(select * from ${physical.name} where ${tables.expression(descriptor.activeCondition)}) as ${sql(alias)}`;\n };\n\n const revision = Effect.fnUntraced(function* (\n subjectRow: Row,\n factors: ReadonlyArray<Row>,\n nativeId: unknown,\n ) {\n invariant(mapping.subject.isActiveStatus(subjectRow[mapping.subject.status]));\n invariant(mapping.subjectId.equals(subjectRow[mapping.subject.id], nativeId));\n invariant(factors.length > 0 && factors.length <= 64);\n invariant(\n factors.every(\n (row) =>\n mapping.subjectId.equals(row[mapping.authority.subjectId], nativeId) &&\n mapping.authority.isActiveStatus(row[mapping.authority.status]),\n ),\n );\n\n const credentials = factors.map((row) => ({\n credentialId: row[mapping.authority.credentialId],\n revision: row[mapping.authority.revision],\n }));\n\n invariant(new Set(credentials.map((item) => item.credentialId)).size === credentials.length);\n\n const value = yield* Schema.decodeUnknownEffect(Schema.toType(OAuthAccountRevision))({\n subjectId: yield* mapping.subjectId.toSubject(nativeId),\n securityRevision: subjectRow[mapping.subject.securityRevision],\n credentials,\n });\n\n return {\n ...value,\n credentials: [...value.credentials].sort((left, right) =>\n left.credentialId.localeCompare(right.credentialId),\n ),\n };\n });\n\n const captureSubject = Effect.fnUntraced(function* (subjectId: SubjectId, locking: boolean) {\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n const rows =\n yield* sql`select ${subject.fields(\"s_\")}, ${now} as engine_now from ${subject.name} where ${subject.column(mapping.subject.id)} = ${subject.value(mapping.subject.id, nativeId)} and ${tables.expression(mapping.subject.activeCondition)} limit 2 ${locking ? lock : sql``}`;\n\n invariant(rows.length <= 1);\n if (rows[0] === undefined) return undefined;\n const row = subject.decode(rows[0], \"s_\");\n\n invariant(mapping.subjectId.equals(row[mapping.subject.id], nativeId));\n\n return {\n nativeId,\n subject: row,\n securityRevision: yield* Schema.decodeUnknownEffect(\n OAuthAccountRevision.fields.securityRevision,\n )(row[mapping.subject.securityRevision]),\n now: yield* Schema.decodeEffect(Schema.Int)(Number(rows[0].engine_now)),\n };\n });\n\n const capture = Effect.fnUntraced(function* (subjectId: SubjectId, locking: boolean) {\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n if (locking) {\n // Never acquire a credential, identity, flow or policy lock before this.\n const subjects =\n yield* sql`select ${subject.fields(\"s_\")}, ${now} as engine_now from ${subject.name}\n where ${subject.column(mapping.subject.id)} = ${subject.value(mapping.subject.id, nativeId)}\n and ${tables.expression(mapping.subject.activeCondition)} ${lock}`;\n\n if (subjects.length === 0) return undefined;\n invariant(subjects.length === 1);\n const subjectRow = subject.decode(subjects[0]!, \"s_\");\n\n const factors = yield* sql`select ${authority.fields(\"a_\")} from ${authority.name}\n where ${authority.column(mapping.authority.subjectId)} = ${authority.value(mapping.authority.subjectId, nativeId)}\n and ${tables.expression(mapping.authority.activeCondition)}\n order by ${authority.column(mapping.authority.credentialId)} limit 65 ${lock}`;\n\n if (factors.length === 0) return undefined;\n\n return {\n nativeId,\n subject: subjectRow,\n revision: yield* revision(\n subjectRow,\n factors.map((row) => authority.decode(row, \"a_\")),\n nativeId,\n ),\n now: yield* Schema.decodeEffect(Schema.Int)(Number(subjects[0]!.engine_now)),\n };\n }\n\n // Bind each column's own native codec; the columns need not share an encoding.\n const rows = yield* sql`select ${s.fields(\"s_\")}, ${a.fields(\"a_\")}, ${now} as engine_now\n from ${active(\"subject\", \"oauth_subject\")}\n join ${active(\"authority\", \"oauth_authority\")} on\n ${a.column(mapping.authority.subjectId)} = ${a.value(mapping.authority.subjectId, nativeId)}\n where ${s.column(mapping.subject.id)} = ${s.value(mapping.subject.id, nativeId)}\n order by ${a.column(mapping.authority.credentialId)} limit 65`;\n\n if (rows.length === 0) return undefined;\n const subjectRow = s.decode(rows[0]!, \"s_\");\n\n return {\n nativeId,\n subject: subjectRow,\n revision: yield* revision(\n subjectRow,\n rows.map((row) => a.decode(row, \"a_\")),\n nativeId,\n ),\n now: yield* Schema.decodeEffect(Schema.Int)(Number(rows[0]!.engine_now)),\n };\n });\n\n return { sql, now, lock, subject, authority, capture, captureSubject, revision };\n});\n\nexport const makeOAuthNativeState = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: OAuthNativeReadMapping,\n) {\n const state = yield* makeOAuthNativeAuthority(tables, mapping);\n const { sql, subject, authority, revision } = state;\n const ownership = tables(mapping.ownership.table);\n const credential = tables(mapping.credential.table);\n const s = subject.as(\"oauth_subject\");\n const o = ownership.as(\"oauth_identity\");\n const c = credential.as(\"oauth_credential\");\n const a = authority.as(\"oauth_authority\");\n\n const exact = (\n left: Parameters<typeof exactSqlText>[1],\n right: Parameters<typeof exactSqlText>[2],\n ) => exactSqlText(sql, left, right);\n\n const active = (kind: \"subject\" | \"credential\" | \"authority\", alias: string) => {\n const descriptor = mapping[kind];\n const physical = tables(descriptor.table);\n\n return sql`(select * from ${physical.name} where ${tables.expression(descriptor.activeCondition)}) as ${sql(alias)}`;\n };\n\n const [joinedIds, joinedIdentityKeys] = yield* Effect.forEach(\n [\n [\n ownership.unencodedTextColumn?.(mapping.ownership.subjectId),\n subject.unencodedTextColumn?.(mapping.subject.id),\n credential.unencodedTextColumn?.(mapping.credential.subjectId),\n authority.unencodedTextColumn?.(mapping.authority.subjectId),\n ],\n [\n credential.unencodedTextColumn?.(mapping.credential.identityKey),\n ownership.unencodedTextColumn?.(mapping.ownership.identityKey),\n ],\n ],\n canJoinTextColumns,\n ).pipe(withStorageValidation, Effect.mapError(unavailable));\n\n const decodeSnapshot = Effect.fnUntraced(function* (\n rows: ReadonlyArray<Row>,\n moduleId: string,\n nativeId?: unknown,\n ) {\n const first = rows[0];\n\n if (first === undefined) return undefined;\n const owned = o.decode(first, \"o_\");\n\n const identity = yield* Schema.decodeUnknownEffect(OAuthExternalIdentity)({\n provider: owned[mapping.ownership.provider],\n issuer: owned[mapping.ownership.issuer],\n subject: owned[mapping.ownership.externalSubject],\n });\n\n const identityKey = yield* oauthIdentityKey(identity);\n\n invariant(owned[mapping.ownership.identityKey] === identityKey);\n const ownerId = mapping.ownership.decodeSubjectId(owned);\n\n if (nativeId !== undefined) invariant(mapping.subjectId.equals(ownerId, nativeId));\n const login = c.decode(first, \"c_\");\n\n invariant(mapping.subjectId.equals(login[mapping.credential.subjectId], ownerId));\n invariant(login[mapping.credential.identityKey] === identityKey);\n invariant(mapping.credential.isActiveStatus(login[mapping.credential.status]));\n\n const current = yield* revision(\n s.decode(first, \"s_\"),\n rows.map((row) => a.decode(row, \"a_\")),\n ownerId,\n );\n\n const credentialId = login[mapping.credential.credentialId];\n const credentialRevision = login[mapping.credential.credentialRevision];\n\n if (\n !current.credentials.some(\n (item) => item.credentialId === credentialId && item.revision === credentialRevision,\n )\n )\n return undefined;\n\n return yield* Schema.decodeUnknownEffect(Schema.toType(OAuthCredentialSnapshot))({\n moduleId,\n identity,\n credentialId,\n credentialRevision,\n revision: current,\n requirement: yield* decodeOAuthRequirement(mapping.subject, s.decode(first, \"s_\")),\n });\n });\n\n const resolve = Effect.fnUntraced(function* (\n moduleId: string,\n identity: typeof OAuthExternalIdentity.Type,\n ) {\n const identityKey = yield* oauthIdentityKey(identity);\n let nativeId: unknown;\n\n if (!joinedIds) {\n const found = yield* sql`select ${ownership.fields(\"o_\")} from ${ownership.name}\n where ${exact(ownership.column(mapping.ownership.identityKey), ownership.value(mapping.ownership.identityKey, identityKey))}`;\n\n if (found.length === 0) return undefined;\n invariant(found.length === 1);\n const row = ownership.decode(found[0]!, \"o_\");\n\n invariant(\n row[mapping.ownership.provider] === identity.provider &&\n row[mapping.ownership.issuer] === identity.issuer &&\n row[mapping.ownership.externalSubject] === identity.subject,\n );\n nativeId = mapping.ownership.decodeSubjectId(row);\n }\n\n const bindOwner = (table: typeof s, key: string) =>\n joinedIds\n ? exact(table.column(key), o.column(mapping.ownership.subjectId))\n : sql`${table.column(key)} = ${table.value(key, nativeId)}`;\n\n const rows =\n yield* sql`select ${o.fields(\"o_\")}, ${s.fields(\"s_\")}, ${c.fields(\"c_\")}, ${a.fields(\"a_\")}\n from ${o.name}\n join ${active(\"subject\", \"oauth_subject\")} on ${bindOwner(s, mapping.subject.id)}\n join ${active(\"credential\", \"oauth_credential\")} on ${bindOwner(c, mapping.credential.subjectId)}\n and ${exact(c.column(mapping.credential.moduleId), c.value(mapping.credential.moduleId, moduleId))}\n and ${exact(c.column(mapping.credential.identityKey), c.value(mapping.credential.identityKey, identityKey))}\n join ${active(\"authority\", \"oauth_authority\")} on ${bindOwner(a, mapping.authority.subjectId)}\n where ${exact(o.column(mapping.ownership.identityKey), o.value(mapping.ownership.identityKey, identityKey))}\n order by ${a.column(mapping.authority.credentialId)} limit 65`;\n\n const result = yield* decodeSnapshot(rows, moduleId, joinedIds ? undefined : nativeId);\n\n if (result !== undefined)\n invariant(\n result.identity.provider === identity.provider &&\n result.identity.issuer === identity.issuer &&\n result.identity.subject === identity.subject,\n );\n\n return result;\n });\n\n const readCredential = Effect.fnUntraced(function* (\n input: typeof OAuthCredentialKey.Type,\n metadataAccess: Fragment = sql`true`,\n ) {\n const nativeId = yield* mapping.subjectId.toNative(input.subjectId);\n let identityKey: unknown;\n\n if (!joinedIdentityKeys) {\n const selected = yield* sql`select ${credential.fields(\"c_\")} from ${credential.name}\n where ${exact(credential.column(mapping.credential.moduleId), credential.value(mapping.credential.moduleId, input.moduleId))}\n and ${exact(credential.column(mapping.credential.credentialId), credential.value(mapping.credential.credentialId, input.credentialId))}\n and ${credential.column(mapping.credential.subjectId)} = ${credential.value(mapping.credential.subjectId, nativeId)}`;\n\n if (selected.length === 0) return undefined;\n invariant(selected.length === 1);\n identityKey = credential.decode(selected[0]!, \"c_\")[mapping.credential.identityKey];\n }\n\n const owner = (table: typeof s, key: string) =>\n sql`${table.column(key)} = ${table.value(key, nativeId)}`;\n\n const rows =\n yield* sql`select ${o.fields(\"o_\")}, ${s.fields(\"s_\")}, ${c.fields(\"c_\")}, ${a.fields(\"a_\")}\n from ${active(\"credential\", \"oauth_credential\")}\n join ${o.name} on ${joinedIdentityKeys ? exact(o.column(mapping.ownership.identityKey), c.column(mapping.credential.identityKey)) : exact(o.column(mapping.ownership.identityKey), o.value(mapping.ownership.identityKey, identityKey))}\n and ${owner(o, mapping.ownership.subjectId)}\n join ${active(\"subject\", \"oauth_subject\")} on ${owner(s, mapping.subject.id)}\n join ${active(\"authority\", \"oauth_authority\")} on ${owner(a, mapping.authority.subjectId)}\n where ${metadataAccess} and ${owner(c, mapping.credential.subjectId)}\n and ${exact(c.column(mapping.credential.moduleId), c.value(mapping.credential.moduleId, input.moduleId))}\n and ${exact(c.column(mapping.credential.credentialId), c.value(mapping.credential.credentialId, input.credentialId))}\n order by ${a.column(mapping.authority.credentialId)} limit 65`;\n\n return yield* decodeSnapshot(rows, input.moduleId, nativeId);\n });\n\n return { ...state, ownership, credential, resolve, readCredential };\n});\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import "../crypto.mjs";
2
2
  import { Crypto, Effect, Schema } from "effect";
3
- import { AuthenticationRequirement } from "@yielded/auth/Sessions";
4
3
  import { OAuthAccountRevision, OAuthExternalIdentity, OAuthUnavailable } from "@yielded/auth/OAuth";
4
+ import { AuthenticationRequirement } from "@yielded/auth/Sessions";
5
5
  //#region src/internal/oauth/state.d.ts
6
6
  /** Capture configuration/callback references while preserving adapter table, SQL
7
7
  * and Effect objects. Never freeze or mutate the consumer's original graph. */
@@ -1,7 +1,7 @@
1
1
  import "../crypto.mjs";
2
2
  import { Crypto, Effect, Schema } from "effect";
3
- import { Base64Url } from "effect/encoding";
4
3
  import { OAuthExternalIdentity, OAuthUnavailable, snapshotOAuthSync } from "@yielded/auth/OAuth";
4
+ import { Base64Url } from "effect/encoding";
5
5
  //#region src/internal/oauth/state.ts
6
6
  const encoder = new TextEncoder();
7
7
  const decoder = new TextDecoder("utf-8", { fatal: true });
@@ -0,0 +1,182 @@
1
+ import { PersistenceConfigurationError } from "./configuration.mjs";
2
+ import { exactSqlText } from "./sql-change.mjs";
3
+ import { SqlBatchCommit } from "./sql-commit.mjs";
4
+ import { requiredOAuthConnectedConstraints, requiredOAuthConnectedRevocationConstraints } from "./models/oauth-connected-model.mjs";
5
+ import { requiredOAuthSignInConstraints } from "./models/oauth-model.mjs";
6
+ import { sameRevision, satisfies } from "./oauth/state.mjs";
7
+ import { makeNativeOAuthSignInServices } from "./oauth/native-sign-in.mjs";
8
+ import { makeNativeOAuthConnectedServices } from "./oauth/native-connected.mjs";
9
+ import { makeNativeOAuthRevocationServices } from "./oauth/native-revocations.mjs";
10
+ import { makeStorageClock } from "./storage-clock.mjs";
11
+ import { storageTables } from "./storage-tables.mjs";
12
+ import { Context, DateTime, Effect, Schema } from "effect";
13
+ import { SqlClient } from "effect/sql";
14
+ import * as M from "@yielded/auth/OAuth";
15
+ import { AuthenticationFlowId } from "@yielded/auth/Sessions";
16
+ //#region src/internal/oauth-managed.ts
17
+ const revocationRetentionMillis = 2592e6;
18
+ const encodeProfile = Schema.encodeSync(Schema.fromJsonString(M.OAuthConnectedProfile));
19
+ const encodePolicy = Schema.encodeSync(Schema.fromJsonString(M.OAuthConnectedPolicy));
20
+ const configurationError = (reason) => PersistenceConfigurationError.make({ reason });
21
+ /** Managed tables use the same native OAuth operations as explicit mappings. */
22
+ const makeManagedOAuth = Effect.fnUntraced(function* (storage, tables, features) {
23
+ const sql = (yield* SqlClient.SqlClient).withoutTransforms();
24
+ const batch = yield* SqlBatchCommit;
25
+ const clock = yield* makeStorageClock(storage).pipe(Effect.mapError(() => configurationError("Cannot configure the OAuth storage clock")));
26
+ function mapped(role) {
27
+ const table = storage.tables[role];
28
+ if (table === void 0) throw configurationError(`Missing ${role} mapping`);
29
+ return {
30
+ table,
31
+ ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key]))
32
+ };
33
+ }
34
+ const s = storage.subjects;
35
+ const subject = tables(s.table);
36
+ const credential = mapped("oauthCredentials");
37
+ const credentials = tables(credential.table);
38
+ const authority = mapped("credentials");
39
+ const factors = tables(authority.table);
40
+ const now = tables.expression(clock.engineNowMillis);
41
+ const policies = /* @__PURE__ */ new Map();
42
+ for (const feature of features) {
43
+ if (feature.connected === void 0) continue;
44
+ const policy = yield* M.snapshotOAuth(M.OAuthConnectedPolicy, feature.connected).pipe(Effect.mapError(() => configurationError(`Invalid OAuth policy for ${feature.moduleId}`)));
45
+ const previous = policies.get(feature.moduleId);
46
+ if (previous !== void 0 && encodePolicy(previous) !== encodePolicy(policy)) return yield* configurationError(`Conflicting OAuth policies for ${feature.moduleId}`);
47
+ policies.set(feature.moduleId, policy);
48
+ }
49
+ const shared = {
50
+ subject: {
51
+ table: s.table,
52
+ id: s.id,
53
+ status: s.status,
54
+ securityRevision: s.securityRevision,
55
+ isActiveStatus: (value) => Object.is(value, s.activeValue),
56
+ activeCondition: sql`${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)}`,
57
+ decodeAuthenticationRequirement: s.requirements
58
+ },
59
+ subjectId: {
60
+ toSubject: s.toSubject,
61
+ toNative: s.toNative,
62
+ equals: Object.is
63
+ },
64
+ ownership: {
65
+ ...mapped("oauthIdentities"),
66
+ decodeSubjectId: (row) => row.subjectId,
67
+ encodeInsert: () => ({})
68
+ },
69
+ credential: {
70
+ ...credential,
71
+ status: "active",
72
+ isActiveStatus: (value) => value === true,
73
+ activeCondition: sql`${credentials.column("active")} = ${credentials.value("active", true)}`
74
+ },
75
+ authority: {
76
+ ...authority,
77
+ status: "active",
78
+ isActiveStatus: (value) => value === true,
79
+ activeCondition: sql`${factors.column("active")} = ${factors.value("active", true)}`
80
+ },
81
+ clock
82
+ };
83
+ function policyCondition(input) {
84
+ let moduleId;
85
+ switch (input.kind) {
86
+ case "sign-in":
87
+ moduleId = input.credential.moduleId;
88
+ break;
89
+ case "action":
90
+ moduleId = input.authorization.challenge.moduleId;
91
+ break;
92
+ case "metadata":
93
+ case "use": moduleId = input.authorization.moduleId;
94
+ }
95
+ const policy = policies.get(moduleId);
96
+ if (policy === void 0) return sql`false`;
97
+ const grant = input.grant;
98
+ if (grant !== void 0) {
99
+ if (grant.moduleId !== moduleId || grant.subjectId !== input.revision.subjectId) return sql`false`;
100
+ if (input.kind !== "action" || input.operation !== "disconnect") {
101
+ const profile = policy.profiles.find((value) => value.key === grant.configuration.profile.key && value.issuance === "active");
102
+ if (profile === void 0 || encodeProfile(profile) !== encodeProfile({
103
+ ...grant.configuration.profile,
104
+ issuance: "active"
105
+ })) return sql`false`;
106
+ }
107
+ }
108
+ const current = sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, input.subjectId)} and ${shared.subject.activeCondition} and ${exactSqlText(sql, subject.column(s.securityRevision), subject.value(s.securityRevision, input.revision.securityRevision))})`;
109
+ if (input.kind === "sign-in") return features.some((feature) => feature.moduleId === moduleId && feature.signIn) ? current : sql`false`;
110
+ if (input.kind !== "action") return input.authorization.policyRevision === input.revision.securityRevision ? current : sql`false`;
111
+ const { authorization } = input;
112
+ const { challenge, evidence, requirement } = authorization;
113
+ const action = input.operation === "settle" ? "connected-complete" : "connected-disconnect";
114
+ if (challenge.action !== action || !sameRevision(challenge.revision, input.revision) || !sameRevision(evidence.revision, input.revision) || evidence.flowId !== AuthenticationFlowId.make(challenge.flowId) || evidence.bindingDigest !== challenge.bindingDigest || !satisfies(evidence.proofs, requirement) || evidence.proofs.some((proof) => !input.revision.credentials.some((entry) => entry.credentialId === proof.credentialId))) return sql`false`;
115
+ const maximumAge = Math.min(requirement.maximumAgeMillis, policy.maximumEvidenceAgeMillis);
116
+ const verified = evidence.proofs.map((proof) => DateTime.toEpochMillis(proof.verifiedAt));
117
+ if (authorization.source._tag === "Session") verified.push(DateTime.toEpochMillis(authorization.source.authenticatedAt));
118
+ return sql.and([
119
+ current,
120
+ sql`${now} < ${authorization.validUntilMillis}`,
121
+ ...verified.map((instant) => sql`${now} >= ${instant} and ${now} < ${instant + maximumAge}`)
122
+ ]);
123
+ }
124
+ let context = Context.empty();
125
+ if (features.some((feature) => feature.signIn)) {
126
+ const mapping = {
127
+ ...shared,
128
+ flow: {
129
+ ...mapped("oauthSignInFlows"),
130
+ encodeInsert: () => ({})
131
+ },
132
+ constraints: requiredOAuthSignInConstraints
133
+ };
134
+ const services = yield* makeNativeOAuthSignInServices(tables, mapping, batch).pipe(Effect.mapError(() => configurationError("Cannot configure OAuth sign-in persistence")));
135
+ context = Context.add(context, M.OAuthSignInPersistence, services.oauthSignInPersistence);
136
+ }
137
+ if (policies.size > 0) {
138
+ const job = {
139
+ ...mapped("oauthConnectedRevocations"),
140
+ encodeInsert: () => ({})
141
+ };
142
+ const grant = {
143
+ ...mapped("oauthConnectedGrants"),
144
+ encodeInsert: () => ({})
145
+ };
146
+ const mapping = {
147
+ ...shared,
148
+ flow: {
149
+ ...mapped("oauthConnectedFlows"),
150
+ encodeInsert: () => ({})
151
+ },
152
+ grant,
153
+ policy: { condition: policyCondition },
154
+ constraints: requiredOAuthConnectedConstraints,
155
+ revocation: {
156
+ mode: "provider",
157
+ job,
158
+ retentionMillis: revocationRetentionMillis,
159
+ constraints: requiredOAuthConnectedRevocationConstraints
160
+ },
161
+ otherReferences: ({ identityKey, subjectId }) => sql`exists(select 1 from ${credentials.name} where ${credentials.column("subjectId")} = ${credentials.value("subjectId", subjectId)} and ${exactSqlText(sql, credentials.column("identityKey"), credentials.value("identityKey", identityKey))})`
162
+ };
163
+ const connected = yield* makeNativeOAuthConnectedServices(tables, mapping, batch).pipe(Effect.mapError(() => configurationError("Cannot configure OAuth connected persistence")));
164
+ const revocations = yield* makeNativeOAuthRevocationServices(tables, {
165
+ ...mapping,
166
+ job,
167
+ constraints: requiredOAuthConnectedRevocationConstraints
168
+ }, batch).pipe(Effect.mapError(() => configurationError("Cannot configure OAuth revocation persistence")));
169
+ context = context.pipe(Context.add(M.OAuthConnectedPersistence, {
170
+ ...connected.oauthConnectedPersistence,
171
+ read: (input) => connected.oauthConnectedPersistence.read(input).pipe(Effect.map((snapshot) => snapshot === void 0 ? void 0 : {
172
+ ...snapshot,
173
+ policyRevision: snapshot.revision.securityRevision
174
+ }))
175
+ }), Context.add(M.OAuthConnectedRevocations, revocations.oauthConnectedRevocations));
176
+ }
177
+ return context;
178
+ });
179
+ //#endregion
180
+ export { makeManagedOAuth };
181
+
182
+ //# sourceMappingURL=oauth-managed.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-managed.mjs","names":[],"sources":["../../src/internal/oauth-managed.ts"],"sourcesContent":["import type { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport * as M from \"@yielded/auth/OAuth\";\nimport { AuthenticationFlowId } from \"@yielded/auth/Sessions\";\nimport { Context, type Crypto, DateTime, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport {\n PersistenceConfigurationError,\n type MappingInput,\n type OAuthFeature,\n} from \"./configuration\";\nimport {\n requiredOAuthConnectedConstraints,\n requiredOAuthConnectedRevocationConstraints,\n type OAuthConnectedPolicyInput,\n} from \"./models/oauth-connected-model\";\nimport { requiredOAuthSignInConstraints } from \"./models/oauth-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativeOAuthConnectedServices } from \"./oauth/native-connected\";\nimport type { OAuthNativeConnectedMapping } from \"./oauth/native-connected-state\";\nimport { makeNativeOAuthRevocationServices } from \"./oauth/native-revocations\";\nimport { makeNativeOAuthSignInServices } from \"./oauth/native-sign-in\";\nimport type { OAuthNativeReadMapping } from \"./oauth/native-state\";\nimport { sameRevision, satisfies } from \"./oauth/state\";\nimport { exactSqlText } from \"./sql-change\";\nimport { SqlBatchCommit } from \"./sql-commit\";\nimport { makeStorageClock } from \"./storage-clock\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\n// Removed provider grants retain revocation custody for 30 days; claims may extend it.\nconst revocationRetentionMillis = 30 * 24 * 60 * 60 * 1000;\nconst encodeProfile = Schema.encodeSync(Schema.fromJsonString(M.OAuthConnectedProfile));\nconst encodePolicy = Schema.encodeSync(Schema.fromJsonString(M.OAuthConnectedPolicy));\n\nconst configurationError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\n/** Managed tables use the same native OAuth operations as explicit mappings. */\nexport const makeManagedOAuth = Effect.fnUntraced(function* (\n storage: MappingInput,\n tables: NativeSqlTables,\n features: ReadonlyArray<OAuthFeature>,\n): Effect.fn.Return<\n Context.Context<never>,\n PersistenceConfigurationError,\n Crypto.Crypto | LifecycleHooks | SqlClient.SqlClient | SqlBatchCommit\n> {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n const batch = yield* SqlBatchCommit;\n\n const clock = yield* makeStorageClock(storage).pipe(\n Effect.mapError(() => configurationError(\"Cannot configure the OAuth storage clock\")),\n );\n\n function mapped<Role extends StorageRole>(role: Role) {\n const table = storage.tables[role];\n\n if (table === undefined) throw configurationError(`Missing ${role} mapping`);\n\n return {\n table,\n // Storage validation owns these logical column keys.\n ...(Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])) as {\n readonly [Key in keyof (typeof storageTables)[Role][\"columns\"]]: Key;\n }),\n };\n }\n\n const s = storage.subjects;\n const subject = tables(s.table);\n const credential = mapped(\"oauthCredentials\");\n const credentials = tables(credential.table);\n const authority = mapped(\"credentials\");\n const factors = tables(authority.table);\n const now = tables.expression(clock.engineNowMillis);\n const policies = new Map<string, M.OAuthConnectedPolicy>();\n\n for (const feature of features) {\n if (feature.connected === undefined) continue;\n\n const policy = yield* M.snapshotOAuth(M.OAuthConnectedPolicy, feature.connected).pipe(\n Effect.mapError(() => configurationError(`Invalid OAuth policy for ${feature.moduleId}`)),\n );\n\n const previous = policies.get(feature.moduleId);\n\n if (previous !== undefined && encodePolicy(previous) !== encodePolicy(policy))\n return yield* configurationError(`Conflicting OAuth policies for ${feature.moduleId}`);\n policies.set(feature.moduleId, policy);\n }\n\n const shared = {\n subject: {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n activeCondition: sql`${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)}`,\n decodeAuthenticationRequirement: s.requirements,\n },\n subjectId: { toSubject: s.toSubject, toNative: s.toNative, equals: Object.is },\n ownership: {\n ...mapped(\"oauthIdentities\"),\n decodeSubjectId: (row: Record<string, unknown>) => row.subjectId,\n encodeInsert: () => ({}),\n },\n credential: {\n ...credential,\n status: \"active\",\n isActiveStatus: (value: unknown) => value === true,\n activeCondition: sql`${credentials.column(\"active\")} = ${credentials.value(\"active\", true)}`,\n },\n authority: {\n ...authority,\n status: \"active\",\n isActiveStatus: (value: unknown) => value === true,\n activeCondition: sql`${factors.column(\"active\")} = ${factors.value(\"active\", true)}`,\n },\n clock,\n };\n\n function policyCondition(input: OAuthConnectedPolicyInput<unknown>): Fragment {\n let moduleId: string;\n\n switch (input.kind) {\n case \"sign-in\":\n moduleId = input.credential.moduleId;\n break;\n case \"action\":\n moduleId = input.authorization.challenge.moduleId;\n break;\n case \"metadata\":\n case \"use\":\n moduleId = input.authorization.moduleId;\n }\n const policy = policies.get(moduleId);\n\n if (policy === undefined) return sql`false`;\n const grant = input.grant;\n\n if (grant !== undefined) {\n if (grant.moduleId !== moduleId || grant.subjectId !== input.revision.subjectId)\n return sql`false`;\n // Disconnect must remain possible after a profile is retired or removed.\n if (input.kind !== \"action\" || input.operation !== \"disconnect\") {\n const profile = policy.profiles.find(\n (value) => value.key === grant.configuration.profile.key && value.issuance === \"active\",\n );\n\n if (\n profile === undefined ||\n encodeProfile(profile) !==\n encodeProfile({\n ...grant.configuration.profile,\n issuance: \"active\",\n })\n )\n return sql`false`;\n }\n }\n\n const current = sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, input.subjectId)} and ${shared.subject.activeCondition} and ${exactSqlText(sql, subject.column(s.securityRevision), subject.value(s.securityRevision, input.revision.securityRevision))})`;\n\n if (input.kind === \"sign-in\")\n return features.some((feature) => feature.moduleId === moduleId && feature.signIn)\n ? current\n : sql`false`;\n if (input.kind !== \"action\") {\n // Mutable application policy shares the subject's guarded security revision.\n return input.authorization.policyRevision === input.revision.securityRevision\n ? current\n : sql`false`;\n }\n\n const { authorization } = input;\n const { challenge, evidence, requirement } = authorization;\n const action = input.operation === \"settle\" ? \"connected-complete\" : \"connected-disconnect\";\n\n if (\n challenge.action !== action ||\n !sameRevision(challenge.revision, input.revision) ||\n !sameRevision(evidence.revision, input.revision) ||\n evidence.flowId !== AuthenticationFlowId.make(challenge.flowId) ||\n evidence.bindingDigest !== challenge.bindingDigest ||\n !satisfies(evidence.proofs, requirement) ||\n evidence.proofs.some(\n (proof) =>\n !input.revision.credentials.some((entry) => entry.credentialId === proof.credentialId),\n )\n )\n return sql`false`;\n\n const maximumAge = Math.min(requirement.maximumAgeMillis, policy.maximumEvidenceAgeMillis);\n const verified = evidence.proofs.map((proof) => DateTime.toEpochMillis(proof.verifiedAt));\n\n if (authorization.source._tag === \"Session\")\n verified.push(DateTime.toEpochMillis(authorization.source.authenticatedAt));\n\n return sql.and([\n current,\n sql`${now} < ${authorization.validUntilMillis}`,\n ...verified.map((instant) => sql`${now} >= ${instant} and ${now} < ${instant + maximumAge}`),\n ]);\n }\n\n let context: Context.Context<never> = Context.empty();\n\n if (features.some((feature) => feature.signIn)) {\n const mapping: OAuthNativeReadMapping = {\n ...shared,\n flow: { ...mapped(\"oauthSignInFlows\"), encodeInsert: () => ({}) },\n constraints: requiredOAuthSignInConstraints,\n };\n\n const services = yield* makeNativeOAuthSignInServices(tables, mapping, batch).pipe(\n Effect.mapError(() => configurationError(\"Cannot configure OAuth sign-in persistence\")),\n );\n\n context = Context.add(context, M.OAuthSignInPersistence, services.oauthSignInPersistence);\n }\n if (policies.size > 0) {\n const job = { ...mapped(\"oauthConnectedRevocations\"), encodeInsert: () => ({}) };\n const grant = { ...mapped(\"oauthConnectedGrants\"), encodeInsert: () => ({}) };\n\n const mapping: OAuthNativeConnectedMapping = {\n ...shared,\n flow: { ...mapped(\"oauthConnectedFlows\"), encodeInsert: () => ({}) },\n grant,\n policy: { condition: policyCondition },\n constraints: requiredOAuthConnectedConstraints,\n revocation: {\n mode: \"provider\",\n job,\n retentionMillis: revocationRetentionMillis,\n constraints: requiredOAuthConnectedRevocationConstraints,\n },\n otherReferences: ({ identityKey, subjectId }) =>\n sql`exists(select 1 from ${credentials.name} where ${credentials.column(\"subjectId\")} = ${credentials.value(\"subjectId\", subjectId)} and ${exactSqlText(sql, credentials.column(\"identityKey\"), credentials.value(\"identityKey\", identityKey))})`,\n };\n\n const connected = yield* makeNativeOAuthConnectedServices(tables, mapping, batch).pipe(\n Effect.mapError(() => configurationError(\"Cannot configure OAuth connected persistence\")),\n );\n\n const revocations = yield* makeNativeOAuthRevocationServices(\n tables,\n {\n ...mapping,\n job,\n constraints: requiredOAuthConnectedRevocationConstraints,\n },\n batch,\n ).pipe(\n Effect.mapError(() => configurationError(\"Cannot configure OAuth revocation persistence\")),\n );\n\n context = context.pipe(\n Context.add(M.OAuthConnectedPersistence, {\n ...connected.oauthConnectedPersistence,\n read: (input) =>\n connected.oauthConnectedPersistence\n .read(input)\n .pipe(\n Effect.map((snapshot) =>\n snapshot === undefined\n ? undefined\n : { ...snapshot, policyRevision: snapshot.revision.securityRevision },\n ),\n ),\n }),\n Context.add(M.OAuthConnectedRevocations, revocations.oauthConnectedRevocations),\n );\n }\n\n return context;\n});\n"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { PersistenceConfigurationError } from "./configuration.mjs";
2
2
  import { PersistenceMappingError } from "./mapping-error.mjs";
3
3
  import { anySqlCondition } from "./sql-change.mjs";
4
+ import { SqlBatchCommit } from "./sql-commit.mjs";
5
+ import { storageTables } from "./storage-tables.mjs";
4
6
  import { requiredPasskeyCredentialConstraints, requiredPasskeyPersistenceConstraints } from "./models/passkey-model.mjs";
5
- import { makeNativePasskeyServices } from "./passkey-native.mjs";
7
+ import { makeBatchPasskeyServices, makeNativePasskeyServices } from "./passkey-native.mjs";
6
8
  import { makePasskeyNativeManagement } from "./passkey-native-write.mjs";
7
- import { storageTables } from "./storage-tables.mjs";
8
9
  import { Context, Effect, Schema } from "effect";
9
10
  import { SqlClient } from "effect/sql";
10
11
  import * as M from "@yielded/auth/Passkey";
@@ -177,6 +178,7 @@ const mappings = Effect.fnUntraced(function* (storage, tables, passwordModules,
177
178
  });
178
179
  /** Managed and mapped tables feed the same native passkey statements. */
179
180
  const makeManagedPasskeys = Effect.fnUntraced(function* (input, tables) {
181
+ const batch = yield* SqlBatchCommit;
180
182
  const policies = yield* Effect.forEach(input.features, (feature) => Effect.map(feature.policy, (policy) => ({
181
183
  feature,
182
184
  policy
@@ -186,7 +188,7 @@ const makeManagedPasskeys = Effect.fnUntraced(function* (input, tables) {
186
188
  const managers = /* @__PURE__ */ new Map();
187
189
  for (const feature of input.features) {
188
190
  const selected = feature.management ? mapping.management(feature) : mapping.base(feature);
189
- const base = yield* makeNativePasskeyServices(tables, selected);
191
+ const base = yield* batch === void 0 ? makeNativePasskeyServices(tables, selected) : makeBatchPasskeyServices(tables, selected);
190
192
  services.set(feature.moduleId, base);
191
193
  if (feature.management) managers.set(feature.moduleId, makePasskeyNativeManagement(base, mapping.management(feature)).passkeyManagementPersistence);
192
194
  }
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-managed.mjs","names":[],"sources":["../../src/internal/passkey-managed.ts"],"sourcesContent":["import * as M from \"@yielded/auth/Passkey\";\nimport { Context, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport {\n PersistenceConfigurationError,\n type MappingInput,\n type PasskeyFeature,\n} from \"./configuration\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"./models/passkey-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativePasskeyServices } from \"./passkey-native\";\nimport type { PasskeyNativeMapping, PasskeyNativeRead } from \"./passkey-native-state\";\nimport {\n makePasskeyNativeManagement,\n type NativePasskeyManagementMapping,\n} from \"./passkey-native-write\";\nimport { anySqlCondition } from \"./sql-change\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\nexport interface ComposedPasskeyInput {\n readonly storage: MappingInput;\n readonly namespace: string;\n readonly dialect: \"pg\" | \"sqlite\";\n readonly features: ReadonlyArray<PasskeyFeature>;\n readonly passwordModules: ReadonlyArray<string>;\n}\n\nconst profileJson = Schema.fromJsonString(M.PasskeyProfile);\n\nconst credentialData = M.PasskeyCredential.mapFields(\n ({ revision: _revision, active: _active, requirement: _requirement, ...fields }) => fields,\n);\n\nconst configurationError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappings = Effect.fnUntraced(function* (\n storage: MappingInput,\n tables: NativeSqlTables,\n passwordModules: ReadonlyArray<string>,\n signInProfiles: ReadonlyArray<M.PasskeyProfile>,\n) {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n const table = (role: StorageRole) => {\n const value = storage.tables[role];\n\n if (value === undefined) throw configurationError(`Missing ${role} mapping`);\n\n return value;\n };\n\n const mapped = <Role extends StorageRole>(role: Role) => ({\n table: table(role),\n // Managed storage validation owns these physical column keys.\n ...(Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])) as {\n readonly [Key in keyof (typeof storageTables)[Role][\"columns\"]]: Key;\n }),\n });\n\n const t = (role: StorageRole) => tables(table(role));\n\n const eq = (role: StorageRole, key: string, value: unknown) =>\n sql`${t(role).column(key)} = ${t(role).value(key, value)}`;\n\n const s = storage.subjects;\n const subject = tables(s.table);\n const active = (value: unknown) => value === true;\n\n const read: PasskeyNativeRead = {\n subject: {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n decodeId: (row) => row[s.id],\n decodeRequirement: s.requirements,\n isActiveStatus: (value) => Object.is(value, s.activeValue),\n activeCondition: sql`${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)}`,\n },\n credential: {\n ...mapped(\"passkeyCredentials\"),\n status: \"active\",\n decode: (row) =>\n Schema.decodeUnknownSync(credentialData)({\n ...row,\n profile: Schema.decodeUnknownSync(profileJson)(row.profile),\n }),\n decodeSubjectId: (row) => row.subjectId,\n isActiveStatus: active,\n activeCondition: eq(\"passkeyCredentials\", \"active\", true),\n },\n authority: {\n ...mapped(\"credentials\"),\n status: \"active\",\n isActiveStatus: active,\n activeCondition: eq(\"credentials\", \"active\", true),\n },\n subjectIds: { toNative: s.toNativeSync, toSubject: s.toSubjectSync, equals: Object.is },\n constraints: requiredPasskeyCredentialConstraints,\n };\n\n const base = (feature: PasskeyFeature): PasskeyNativeMapping => ({\n moduleId: feature.moduleId,\n read,\n flow: { ...mapped(\"passkeyFlows\"), encodeInsert: () => ({}) },\n clock: {\n encodeInstant: storage.encodeInstant,\n decodeInstant: storage.decodeInstantSync,\n engineNowMillis: sql.onDialectOrElse({\n pg: () => sql`cast(extract(epoch from clock_timestamp()) * 1000 as bigint)`,\n orElse: () => sql`cast(round((julianday('now') - 2440587.5) * 86400000) as integer)`,\n }),\n toMillis: (expression: Fragment) => expression,\n fromMillis: (expression: Fragment) => expression,\n },\n telemetry: {\n lastUsedAt: \"lastUsedAt\",\n encodeBackupEligible: (value) => value,\n encodeBackupState: (value) => value,\n },\n constraints: requiredPasskeyPersistenceConstraints,\n });\n\n const metadata = (nativeId: unknown) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} and ${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)})`;\n\n const management = (feature: PasskeyFeature): NativePasskeyManagementMapping => {\n const policy = feature.managementPolicy;\n\n if (policy === undefined)\n throw configurationError(`Missing passkey management policy for ${feature.moduleId}`);\n\n const c = t(\"passkeyCredentials\"),\n f = t(\"credentials\");\n\n return {\n ...base(feature),\n write: {\n credential: {\n name: \"name\",\n createdAt: \"createdAt\",\n encodeInsert: ({ credential, subjectId, summary, marker }) => ({\n credentialId: credential.credentialId,\n subjectId,\n rpId: credential.rpId,\n protocolCredentialId: credential.protocolCredentialId,\n userHandle: credential.userHandle,\n credentialKey: \"\",\n publicKey: credential.publicKey,\n algorithm: credential.algorithm,\n profile: Schema.encodeSync(profileJson)(credential.profile),\n credentialRevision: marker,\n active: true,\n primarySignIn: credential.primarySignIn,\n enrollmentUserVerified: credential.enrollmentUserVerified,\n backupEligible: credential.backupEligible,\n backupState: credential.backupState,\n counter: credential.counter,\n name: summary.name,\n createdAt: storage.encodeInstant(summary.createdAtMillis),\n }),\n encodePrimarySignIn: (value) => value,\n encodeEnrollmentUserVerified: (value) => value,\n encodeBackupEligible: (value) => value,\n activeStatus: true,\n removedStatus: false,\n },\n authority: {\n encodeInsert: ({ subjectId, credential, marker }) => ({\n subjectId,\n credentialId: credential.credentialId,\n revision: marker,\n active: true,\n }),\n activeStatus: true,\n removedStatus: false,\n },\n policy: {\n subjectColumns: subject.keys,\n management: () => policy,\n requirement: s.actionRequirements,\n metadata,\n action: metadata,\n remainingSignIn: (nativeId, excluded, row) =>\n Effect.gen(function* () {\n const requirement = yield* Schema.decodeEffect(M.PasskeyRequirement)(\n yield* s.requirements(row),\n );\n\n const alternatives: Fragment[] = [];\n\n if (\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n alternative.factors.every((factor) => factor === \"possession\"),\n )\n ) {\n alternatives.push(sql`exists(select 1 from ${c.name} join ${f.name} on ${f.column(\"credentialId\")} = ${c.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${c.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n where ${eq(\"passkeyCredentials\", \"subjectId\", nativeId)} and ${c.column(\"credentialId\")} <> ${c.value(\"credentialId\", excluded)} and ${eq(\"passkeyCredentials\", \"active\", true)} and ${eq(\"passkeyCredentials\", \"primarySignIn\", true)} and ${eq(\"passkeyCredentials\", \"enrollmentUserVerified\", true)} and ${anySqlCondition(\n signInProfiles.map((profile) => eq(\"passkeyCredentials\", \"rpId\", profile.rpId)),\n )})`);\n }\n if (\n passwordModules.length > 0 &&\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n !alternative.userVerified &&\n !alternative.phishingResistant &&\n alternative.factors.every((factor) => factor === \"knowledge\"),\n )\n ) {\n const p = t(\"passwords\"),\n i = t(\"identifiers\");\n\n alternatives.push(sql`exists(select 1 from ${p.name} join ${f.name} on ${f.column(\"credentialId\")} = ${p.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${p.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n join ${i.name} on ${eq(\"identifiers\", \"subjectId\", nativeId)} and ${eq(\"identifiers\", \"active\", true)} and ${eq(\"identifiers\", \"namespace\", \"email\")}\n where ${eq(\"passwords\", \"subjectId\", nativeId)} and ${p.column(\"credentialId\")} <> ${p.value(\"credentialId\", excluded)} and ${anySqlCondition(\n passwordModules.map((module) => eq(\"passwords\", \"moduleId\", module)),\n )})`);\n }\n\n return anySqlCondition(alternatives);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause }),\n ),\n ),\n },\n },\n invalidation: {\n window: {\n trigger: \"credential-change\",\n existingSessions: \"immediate\",\n maximumExposureMillis: 0,\n oldAuthenticationEvidence: \"rejected\",\n },\n mutations: [],\n postcondition: ({ subjectId, securityRevision }) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, subjectId)} and ${subject.column(s.securityRevision)} = ${subject.value(s.securityRevision, securityRevision)})`,\n },\n };\n };\n\n return { base, management };\n});\n\n/** Managed and mapped tables feed the same native passkey statements. */\nexport const makeManagedPasskeys = Effect.fnUntraced(\n function* (input: ComposedPasskeyInput, tables: NativeSqlTables) {\n const policies = yield* Effect.forEach(input.features, (feature) =>\n Effect.map(feature.policy, (policy) => ({ feature, policy })),\n );\n\n const mapping = yield* mappings(\n input.storage,\n tables,\n input.passwordModules,\n policies.flatMap(({ feature, policy }) => (feature.management ? [] : policy.profiles)),\n );\n\n const services = new Map<\n string,\n Effect.Success<ReturnType<typeof makeNativePasskeyServices>>\n >();\n\n const managers = new Map<string, M.PasskeyManagementPersistence[\"Service\"]>();\n\n for (const feature of input.features) {\n const selected = feature.management ? mapping.management(feature) : mapping.base(feature);\n const base = yield* makeNativePasskeyServices(tables, selected);\n\n services.set(feature.moduleId, base);\n if (feature.management)\n managers.set(\n feature.moduleId,\n makePasskeyNativeManagement(base, mapping.management(feature))\n .passkeyManagementPersistence,\n );\n }\n\n const service = (moduleId: string) => {\n const value = services.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const manager = (moduleId: string) => {\n const value = managers.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const first = services.values().next().value;\n\n if (first === undefined) return yield* configurationError(\"A passkey module is required\");\n\n let context: Context.Context<never> = Context.make(M.PasskeyCredentials, {\n lookup: (input) => first.passkeyCredentials.lookup(input),\n listForSubject: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyCredentials.listForSubject(input),\n ),\n }).pipe(\n Context.add(M.PasskeyPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(service(input.ceremony.moduleId), (value) =>\n value.passkeyPersistence.issue(input, prepare),\n ),\n context: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.context(input),\n ),\n consume: (input, prepare) =>\n Effect.flatMap(service(input.access.moduleId), (value) =>\n value.passkeyPersistence.consume(input, prepare),\n ),\n cleanup: (input, prepare) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.cleanup(input, prepare),\n ),\n }),\n );\n\n if (managers.size > 0)\n context = context.pipe(\n Context.add(M.PasskeyManagementPersistence, {\n completeEnrollment: (input, prepare) =>\n Effect.flatMap(manager(input.ceremony.moduleId), (value) =>\n value.completeEnrollment(input, prepare),\n ),\n list: (input) => Effect.flatMap(manager(input.moduleId), (value) => value.list(input)),\n inspectRemove: (input) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.inspectRemove(input)),\n rename: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.rename(input, prepare)),\n remove: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.remove(input, prepare)),\n }),\n );\n\n return context;\n },\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : configurationError(\"Unable to configure passkey persistence\"),\n ),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"passkey-managed.mjs","names":[],"sources":["../../src/internal/passkey-managed.ts"],"sourcesContent":["import * as M from \"@yielded/auth/Passkey\";\nimport { Context, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport {\n PersistenceConfigurationError,\n type MappingInput,\n type PasskeyFeature,\n} from \"./configuration\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"./models/passkey-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeBatchPasskeyServices, makeNativePasskeyServices } from \"./passkey-native\";\nimport type { PasskeyNativeMapping, PasskeyNativeRead } from \"./passkey-native-state\";\nimport {\n makePasskeyNativeManagement,\n type NativePasskeyManagementMapping,\n} from \"./passkey-native-write\";\nimport { anySqlCondition } from \"./sql-change\";\nimport { SqlBatchCommit } from \"./sql-commit\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\nexport interface ComposedPasskeyInput {\n readonly storage: MappingInput;\n readonly namespace: string;\n readonly dialect: \"pg\" | \"sqlite\";\n readonly features: ReadonlyArray<PasskeyFeature>;\n readonly passwordModules: ReadonlyArray<string>;\n}\n\nconst profileJson = Schema.fromJsonString(M.PasskeyProfile);\n\nconst credentialData = M.PasskeyCredential.mapFields(\n ({ revision: _revision, active: _active, requirement: _requirement, ...fields }) => fields,\n);\n\nconst configurationError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappings = Effect.fnUntraced(function* (\n storage: MappingInput,\n tables: NativeSqlTables,\n passwordModules: ReadonlyArray<string>,\n signInProfiles: ReadonlyArray<M.PasskeyProfile>,\n) {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n const table = (role: StorageRole) => {\n const value = storage.tables[role];\n\n if (value === undefined) throw configurationError(`Missing ${role} mapping`);\n\n return value;\n };\n\n const mapped = <Role extends StorageRole>(role: Role) => ({\n table: table(role),\n // Managed storage validation owns these physical column keys.\n ...(Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])) as {\n readonly [Key in keyof (typeof storageTables)[Role][\"columns\"]]: Key;\n }),\n });\n\n const t = (role: StorageRole) => tables(table(role));\n\n const eq = (role: StorageRole, key: string, value: unknown) =>\n sql`${t(role).column(key)} = ${t(role).value(key, value)}`;\n\n const s = storage.subjects;\n const subject = tables(s.table);\n const active = (value: unknown) => value === true;\n\n const read: PasskeyNativeRead = {\n subject: {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n decodeId: (row) => row[s.id],\n decodeRequirement: s.requirements,\n isActiveStatus: (value) => Object.is(value, s.activeValue),\n activeCondition: sql`${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)}`,\n },\n credential: {\n ...mapped(\"passkeyCredentials\"),\n status: \"active\",\n decode: (row) =>\n Schema.decodeUnknownSync(credentialData)({\n ...row,\n profile: Schema.decodeUnknownSync(profileJson)(row.profile),\n }),\n decodeSubjectId: (row) => row.subjectId,\n isActiveStatus: active,\n activeCondition: eq(\"passkeyCredentials\", \"active\", true),\n },\n authority: {\n ...mapped(\"credentials\"),\n status: \"active\",\n isActiveStatus: active,\n activeCondition: eq(\"credentials\", \"active\", true),\n },\n subjectIds: { toNative: s.toNativeSync, toSubject: s.toSubjectSync, equals: Object.is },\n constraints: requiredPasskeyCredentialConstraints,\n };\n\n const base = (feature: PasskeyFeature): PasskeyNativeMapping => ({\n moduleId: feature.moduleId,\n read,\n flow: { ...mapped(\"passkeyFlows\"), encodeInsert: () => ({}) },\n clock: {\n encodeInstant: storage.encodeInstant,\n decodeInstant: storage.decodeInstantSync,\n engineNowMillis: sql.onDialectOrElse({\n pg: () => sql`cast(extract(epoch from clock_timestamp()) * 1000 as bigint)`,\n orElse: () => sql`cast(round((julianday('now') - 2440587.5) * 86400000) as integer)`,\n }),\n toMillis: (expression: Fragment) => expression,\n fromMillis: (expression: Fragment) => expression,\n },\n telemetry: {\n lastUsedAt: \"lastUsedAt\",\n encodeBackupEligible: (value) => value,\n encodeBackupState: (value) => value,\n },\n constraints: requiredPasskeyPersistenceConstraints,\n });\n\n const metadata = (nativeId: unknown) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} and ${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)})`;\n\n const management = (feature: PasskeyFeature): NativePasskeyManagementMapping => {\n const policy = feature.managementPolicy;\n\n if (policy === undefined)\n throw configurationError(`Missing passkey management policy for ${feature.moduleId}`);\n\n const c = t(\"passkeyCredentials\"),\n f = t(\"credentials\");\n\n return {\n ...base(feature),\n write: {\n credential: {\n name: \"name\",\n createdAt: \"createdAt\",\n encodeInsert: ({ credential, subjectId, summary, marker }) => ({\n credentialId: credential.credentialId,\n subjectId,\n rpId: credential.rpId,\n protocolCredentialId: credential.protocolCredentialId,\n userHandle: credential.userHandle,\n credentialKey: \"\",\n publicKey: credential.publicKey,\n algorithm: credential.algorithm,\n profile: Schema.encodeSync(profileJson)(credential.profile),\n credentialRevision: marker,\n active: true,\n primarySignIn: credential.primarySignIn,\n enrollmentUserVerified: credential.enrollmentUserVerified,\n backupEligible: credential.backupEligible,\n backupState: credential.backupState,\n counter: credential.counter,\n name: summary.name,\n createdAt: storage.encodeInstant(summary.createdAtMillis),\n }),\n encodePrimarySignIn: (value) => value,\n encodeEnrollmentUserVerified: (value) => value,\n encodeBackupEligible: (value) => value,\n activeStatus: true,\n removedStatus: false,\n },\n authority: {\n encodeInsert: ({ subjectId, credential, marker }) => ({\n subjectId,\n credentialId: credential.credentialId,\n revision: marker,\n active: true,\n }),\n activeStatus: true,\n removedStatus: false,\n },\n policy: {\n subjectColumns: subject.keys,\n management: () => policy,\n requirement: s.actionRequirements,\n metadata,\n action: metadata,\n remainingSignIn: (nativeId, excluded, row) =>\n Effect.gen(function* () {\n const requirement = yield* Schema.decodeEffect(M.PasskeyRequirement)(\n yield* s.requirements(row),\n );\n\n const alternatives: Fragment[] = [];\n\n if (\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n alternative.factors.every((factor) => factor === \"possession\"),\n )\n ) {\n alternatives.push(sql`exists(select 1 from ${c.name} join ${f.name} on ${f.column(\"credentialId\")} = ${c.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${c.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n where ${eq(\"passkeyCredentials\", \"subjectId\", nativeId)} and ${c.column(\"credentialId\")} <> ${c.value(\"credentialId\", excluded)} and ${eq(\"passkeyCredentials\", \"active\", true)} and ${eq(\"passkeyCredentials\", \"primarySignIn\", true)} and ${eq(\"passkeyCredentials\", \"enrollmentUserVerified\", true)} and ${anySqlCondition(\n signInProfiles.map((profile) => eq(\"passkeyCredentials\", \"rpId\", profile.rpId)),\n )})`);\n }\n if (\n passwordModules.length > 0 &&\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n !alternative.userVerified &&\n !alternative.phishingResistant &&\n alternative.factors.every((factor) => factor === \"knowledge\"),\n )\n ) {\n const p = t(\"passwords\"),\n i = t(\"identifiers\");\n\n alternatives.push(sql`exists(select 1 from ${p.name} join ${f.name} on ${f.column(\"credentialId\")} = ${p.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${p.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n join ${i.name} on ${eq(\"identifiers\", \"subjectId\", nativeId)} and ${eq(\"identifiers\", \"active\", true)} and ${eq(\"identifiers\", \"namespace\", \"email\")}\n where ${eq(\"passwords\", \"subjectId\", nativeId)} and ${p.column(\"credentialId\")} <> ${p.value(\"credentialId\", excluded)} and ${anySqlCondition(\n passwordModules.map((module) => eq(\"passwords\", \"moduleId\", module)),\n )})`);\n }\n\n return anySqlCondition(alternatives);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause }),\n ),\n ),\n },\n },\n invalidation: {\n window: {\n trigger: \"credential-change\",\n existingSessions: \"immediate\",\n maximumExposureMillis: 0,\n oldAuthenticationEvidence: \"rejected\",\n },\n mutations: [],\n postcondition: ({ subjectId, securityRevision }) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, subjectId)} and ${subject.column(s.securityRevision)} = ${subject.value(s.securityRevision, securityRevision)})`,\n },\n };\n };\n\n return { base, management };\n});\n\n/** Managed and mapped tables feed the same native passkey statements. */\nexport const makeManagedPasskeys = Effect.fnUntraced(\n function* (input: ComposedPasskeyInput, tables: NativeSqlTables) {\n const batch = yield* SqlBatchCommit;\n\n const policies = yield* Effect.forEach(input.features, (feature) =>\n Effect.map(feature.policy, (policy) => ({ feature, policy })),\n );\n\n const mapping = yield* mappings(\n input.storage,\n tables,\n input.passwordModules,\n policies.flatMap(({ feature, policy }) => (feature.management ? [] : policy.profiles)),\n );\n\n const services = new Map<\n string,\n Effect.Success<ReturnType<typeof makeNativePasskeyServices>>\n >();\n\n const managers = new Map<string, M.PasskeyManagementPersistence[\"Service\"]>();\n\n for (const feature of input.features) {\n const selected = feature.management ? mapping.management(feature) : mapping.base(feature);\n\n const base = yield* batch === undefined\n ? makeNativePasskeyServices(tables, selected)\n : makeBatchPasskeyServices(tables, selected);\n\n services.set(feature.moduleId, base);\n if (feature.management)\n managers.set(\n feature.moduleId,\n makePasskeyNativeManagement(base, mapping.management(feature))\n .passkeyManagementPersistence,\n );\n }\n\n const service = (moduleId: string) => {\n const value = services.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const manager = (moduleId: string) => {\n const value = managers.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const first = services.values().next().value;\n\n if (first === undefined) return yield* configurationError(\"A passkey module is required\");\n\n let context: Context.Context<never> = Context.make(M.PasskeyCredentials, {\n lookup: (input) => first.passkeyCredentials.lookup(input),\n listForSubject: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyCredentials.listForSubject(input),\n ),\n }).pipe(\n Context.add(M.PasskeyPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(service(input.ceremony.moduleId), (value) =>\n value.passkeyPersistence.issue(input, prepare),\n ),\n context: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.context(input),\n ),\n consume: (input, prepare) =>\n Effect.flatMap(service(input.access.moduleId), (value) =>\n value.passkeyPersistence.consume(input, prepare),\n ),\n cleanup: (input, prepare) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.cleanup(input, prepare),\n ),\n }),\n );\n\n if (managers.size > 0)\n context = context.pipe(\n Context.add(M.PasskeyManagementPersistence, {\n completeEnrollment: (input, prepare) =>\n Effect.flatMap(manager(input.ceremony.moduleId), (value) =>\n value.completeEnrollment(input, prepare),\n ),\n list: (input) => Effect.flatMap(manager(input.moduleId), (value) => value.list(input)),\n inspectRemove: (input) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.inspectRemove(input)),\n rename: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.rename(input, prepare)),\n remove: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.remove(input, prepare)),\n }),\n );\n\n return context;\n },\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : configurationError(\"Unable to configure passkey persistence\"),\n ),\n);\n"],"mappings":";;;;;;;;;;;;AAkCA,MAAM,cAAc,OAAO,eAAe,EAAE,cAAc;AAE1D,MAAM,iBAAiB,EAAE,kBAAkB,WACxC,EAAE,UAAU,WAAW,QAAQ,SAAS,aAAa,cAAc,GAAG,aAAa,MACtF;AAEA,MAAM,sBAAsB,WAAmB,8BAA8B,KAAK,EAAE,OAAO,CAAC;AAE5F,MAAM,WAAW,OAAO,WAAW,WACjC,SACA,QACA,iBACA,gBACA;CACA,MAAM,OAAO,OAAO,UAAU,UAAA,CAAW,kBAAkB;CAE3D,MAAM,SAAS,SAAsB;EACnC,MAAM,QAAQ,QAAQ,OAAO;EAE7B,IAAI,UAAU,KAAA,GAAW,MAAM,mBAAmB,WAAW,KAAK,SAAS;EAE3E,OAAO;CACT;CAEA,MAAM,UAAoC,UAAgB;EACxD,OAAO,MAAM,IAAI;EAEjB,GAAI,OAAO,YAAY,OAAO,KAAK,cAAc,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;CAG1F;CAEA,MAAM,KAAK,SAAsB,OAAO,MAAM,IAAI,CAAC;CAEnD,MAAM,MAAM,MAAmB,KAAa,UAC1C,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK;CAEzD,MAAM,IAAI,QAAQ;CAClB,MAAM,UAAU,OAAO,EAAE,KAAK;CAC9B,MAAM,UAAU,UAAmB,UAAU;CAE7C,MAAM,OAA0B;EAC9B,SAAS;GACP,OAAO,EAAE;GACT,IAAI,EAAE;GACN,QAAQ,EAAE;GACV,kBAAkB,EAAE;GACpB,WAAW,QAAQ,IAAI,EAAE;GACzB,mBAAmB,EAAE;GACrB,iBAAiB,UAAU,OAAO,GAAG,OAAO,EAAE,WAAW;GACzD,iBAAiB,GAAG,GAAG,QAAQ,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,QAAQ,EAAE,WAAW;EAC5F;EACA,YAAY;GACV,GAAG,OAAO,oBAAoB;GAC9B,QAAQ;GACR,SAAS,QACP,OAAO,kBAAkB,cAAc,CAAC,CAAC;IACvC,GAAG;IACH,SAAS,OAAO,kBAAkB,WAAW,CAAC,CAAC,IAAI,OAAO;GAC5D,CAAC;GACH,kBAAkB,QAAQ,IAAI;GAC9B,gBAAgB;GAChB,iBAAiB,GAAG,sBAAsB,UAAU,IAAI;EAC1D;EACA,WAAW;GACT,GAAG,OAAO,aAAa;GACvB,QAAQ;GACR,gBAAgB;GAChB,iBAAiB,GAAG,eAAe,UAAU,IAAI;EACnD;EACA,YAAY;GAAE,UAAU,EAAE;GAAc,WAAW,EAAE;GAAe,QAAQ,OAAO;EAAG;EACtF,aAAa;CACf;CAEA,MAAM,QAAQ,aAAmD;EAC/D,UAAU,QAAQ;EAClB;EACA,MAAM;GAAE,GAAG,OAAO,cAAc;GAAG,qBAAqB,CAAC;EAAG;EAC5D,OAAO;GACL,eAAe,QAAQ;GACvB,eAAe,QAAQ;GACvB,iBAAiB,IAAI,gBAAgB;IACnC,UAAU,GAAG;IACb,cAAc,GAAG;GACnB,CAAC;GACD,WAAW,eAAyB;GACpC,aAAa,eAAyB;EACxC;EACA,WAAW;GACT,YAAY;GACZ,uBAAuB,UAAU;GACjC,oBAAoB,UAAU;EAChC;EACA,aAAa;CACf;CAEA,MAAM,YAAY,aAChB,GAAG,wBAAwB,QAAQ,KAAK,SAAS,QAAQ,OAAO,EAAE,EAAE,EAAE,KAAK,QAAQ,MAAM,EAAE,IAAI,QAAQ,EAAE,OAAO,QAAQ,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE;CAEvL,MAAM,cAAc,YAA4D;EAC9E,MAAM,SAAS,QAAQ;EAEvB,IAAI,WAAW,KAAA,GACb,MAAM,mBAAmB,yCAAyC,QAAQ,UAAU;EAEtF,MAAM,IAAI,EAAE,oBAAoB,GAC9B,IAAI,EAAE,aAAa;EAErB,OAAO;GACL,GAAG,KAAK,OAAO;GACf,OAAO;IACL,YAAY;KACV,MAAM;KACN,WAAW;KACX,eAAe,EAAE,YAAY,WAAW,SAAS,cAAc;MAC7D,cAAc,WAAW;MACzB;MACA,MAAM,WAAW;MACjB,sBAAsB,WAAW;MACjC,YAAY,WAAW;MACvB,eAAe;MACf,WAAW,WAAW;MACtB,WAAW,WAAW;MACtB,SAAS,OAAO,WAAW,WAAW,CAAC,CAAC,WAAW,OAAO;MAC1D,oBAAoB;MACpB,QAAQ;MACR,eAAe,WAAW;MAC1B,wBAAwB,WAAW;MACnC,gBAAgB,WAAW;MAC3B,aAAa,WAAW;MACxB,SAAS,WAAW;MACpB,MAAM,QAAQ;MACd,WAAW,QAAQ,cAAc,QAAQ,eAAe;KAC1D;KACA,sBAAsB,UAAU;KAChC,+BAA+B,UAAU;KACzC,uBAAuB,UAAU;KACjC,cAAc;KACd,eAAe;IACjB;IACA,WAAW;KACT,eAAe,EAAE,WAAW,YAAY,cAAc;MACpD;MACA,cAAc,WAAW;MACzB,UAAU;MACV,QAAQ;KACV;KACA,cAAc;KACd,eAAe;IACjB;IACA,QAAQ;KACN,gBAAgB,QAAQ;KACxB,kBAAkB;KAClB,aAAa,EAAE;KACf;KACA,QAAQ;KACR,kBAAkB,UAAU,UAAU,QACpC,OAAO,IAAI,aAAa;MACtB,MAAM,cAAc,OAAO,OAAO,aAAa,EAAE,kBAAkB,CAAC,CAClE,OAAO,EAAE,aAAa,GAAG,CAC3B;MAEA,MAAM,eAA2B,CAAC;MAElC,IACE,YAAY,aAAa,MACtB,gBACC,YAAY,uBAAuB,KACnC,YAAY,QAAQ,OAAO,WAAW,WAAW,YAAY,CACjE,GAEA,aAAa,KAAK,GAAG,wBAAwB,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,OAAO,cAAc,EAAE,KAAK,EAAE,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,UAAU,EAAE,KAAK,EAAE,OAAO,oBAAoB,EAAE,OAAO,GAAG,eAAe,UAAU,IAAI,EAAE;wBAClS,GAAG,sBAAsB,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,QAAQ,EAAE,OAAO,GAAG,sBAAsB,UAAU,IAAI,EAAE,OAAO,GAAG,sBAAsB,iBAAiB,IAAI,EAAE,OAAO,GAAG,sBAAsB,0BAA0B,IAAI,EAAE,OAAO,gBAC5S,eAAe,KAAK,YAAY,GAAG,sBAAsB,QAAQ,QAAQ,IAAI,CAAC,CAChF,EAAE,EAAE;MAEN,IACE,gBAAgB,SAAS,KACzB,YAAY,aAAa,MACtB,gBACC,YAAY,uBAAuB,KACnC,CAAC,YAAY,gBACb,CAAC,YAAY,qBACb,YAAY,QAAQ,OAAO,WAAW,WAAW,WAAW,CAChE,GACA;OACA,MAAM,IAAI,EAAE,WAAW,GACrB,IAAI,EAAE,aAAa;OAErB,aAAa,KAAK,GAAG,wBAAwB,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,OAAO,cAAc,EAAE,KAAK,EAAE,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,UAAU,EAAE,KAAK,EAAE,OAAO,oBAAoB,EAAE,OAAO,GAAG,eAAe,UAAU,IAAI,EAAE;uBACnS,EAAE,KAAK,MAAM,GAAG,eAAe,aAAa,QAAQ,EAAE,OAAO,GAAG,eAAe,UAAU,IAAI,EAAE,OAAO,GAAG,eAAe,aAAa,OAAO,EAAE;wBAC7I,GAAG,aAAa,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,QAAQ,EAAE,OAAO,gBAC5H,gBAAgB,KAAK,WAAW,GAAG,aAAa,YAAY,MAAM,CAAC,CACrE,EAAE,EAAE;MACN;MAEA,OAAO,gBAAgB,YAAY;KACrC,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,UACf,wBAAwB,KAAK;MAAE,WAAW;MAAW;KAAM,CAAC,CAC9D,CACF;IACJ;GACF;GACA,cAAc;IACZ,QAAQ;KACN,SAAS;KACT,kBAAkB;KAClB,uBAAuB;KACvB,2BAA2B;IAC7B;IACA,WAAW,CAAC;IACZ,gBAAgB,EAAE,WAAW,uBAC3B,GAAG,wBAAwB,QAAQ,KAAK,SAAS,QAAQ,OAAO,EAAE,EAAE,EAAE,KAAK,QAAQ,MAAM,EAAE,IAAI,SAAS,EAAE,OAAO,QAAQ,OAAO,EAAE,gBAAgB,EAAE,KAAK,QAAQ,MAAM,EAAE,kBAAkB,gBAAgB,EAAE;GACjN;EACF;CACF;CAEA,OAAO;EAAE;EAAM;CAAW;AAC5B,CAAC;;AAGD,MAAa,sBAAsB,OAAO,WACxC,WAAW,OAA6B,QAAyB;CAC/D,MAAM,QAAQ,OAAO;CAErB,MAAM,WAAW,OAAO,OAAO,QAAQ,MAAM,WAAW,YACtD,OAAO,IAAI,QAAQ,SAAS,YAAY;EAAE;EAAS;CAAO,EAAE,CAC9D;CAEA,MAAM,UAAU,OAAO,SACrB,MAAM,SACN,QACA,MAAM,iBACN,SAAS,SAAS,EAAE,SAAS,aAAc,QAAQ,aAAa,CAAC,IAAI,OAAO,QAAS,CACvF;CAEA,MAAM,2BAAW,IAAI,IAGnB;CAEF,MAAM,2BAAW,IAAI,IAAuD;CAE5E,KAAK,MAAM,WAAW,MAAM,UAAU;EACpC,MAAM,WAAW,QAAQ,aAAa,QAAQ,WAAW,OAAO,IAAI,QAAQ,KAAK,OAAO;EAExF,MAAM,OAAO,OAAO,UAAU,KAAA,IAC1B,0BAA0B,QAAQ,QAAQ,IAC1C,yBAAyB,QAAQ,QAAQ;EAE7C,SAAS,IAAI,QAAQ,UAAU,IAAI;EACnC,IAAI,QAAQ,YACV,SAAS,IACP,QAAQ,UACR,4BAA4B,MAAM,QAAQ,WAAW,OAAO,CAAC,CAAC,CAC3D,4BACL;CACJ;CAEA,MAAM,WAAW,aAAqB;EACpC,MAAM,QAAQ,SAAS,IAAI,QAAQ;EAEnC,OAAO,UAAU,KAAA,IACb,OAAO,KAAK,EAAE,mBAAmB,KAAK,CAAC,CAAC,CAAC,IACzC,OAAO,QAAQ,KAAK;CAC1B;CAEA,MAAM,WAAW,aAAqB;EACpC,MAAM,QAAQ,SAAS,IAAI,QAAQ;EAEnC,OAAO,UAAU,KAAA,IACb,OAAO,KAAK,EAAE,mBAAmB,KAAK,CAAC,CAAC,CAAC,IACzC,OAAO,QAAQ,KAAK;CAC1B;CAEA,MAAM,QAAQ,SAAS,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;CAEvC,IAAI,UAAU,KAAA,GAAW,OAAO,OAAO,mBAAmB,8BAA8B;CAExF,IAAI,UAAkC,QAAQ,KAAK,EAAE,oBAAoB;EACvE,SAAS,UAAU,MAAM,mBAAmB,OAAO,KAAK;EACxD,iBAAiB,UACf,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UACvC,MAAM,mBAAmB,eAAe,KAAK,CAC/C;CACJ,CAAC,CAAC,CAAC,KACD,QAAQ,IAAI,EAAE,oBAAoB;EAChC,QAAQ,OAAO,YACb,OAAO,QAAQ,QAAQ,MAAM,SAAS,QAAQ,IAAI,UAChD,MAAM,mBAAmB,MAAM,OAAO,OAAO,CAC/C;EACF,UAAU,UACR,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UACvC,MAAM,mBAAmB,QAAQ,KAAK,CACxC;EACF,UAAU,OAAO,YACf,OAAO,QAAQ,QAAQ,MAAM,OAAO,QAAQ,IAAI,UAC9C,MAAM,mBAAmB,QAAQ,OAAO,OAAO,CACjD;EACF,UAAU,OAAO,YACf,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UACvC,MAAM,mBAAmB,QAAQ,OAAO,OAAO,CACjD;CACJ,CAAC,CACH;CAEA,IAAI,SAAS,OAAO,GAClB,UAAU,QAAQ,KAChB,QAAQ,IAAI,EAAE,8BAA8B;EAC1C,qBAAqB,OAAO,YAC1B,OAAO,QAAQ,QAAQ,MAAM,SAAS,QAAQ,IAAI,UAChD,MAAM,mBAAmB,OAAO,OAAO,CACzC;EACF,OAAO,UAAU,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,KAAK,KAAK,CAAC;EACrF,gBAAgB,UACd,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,cAAc,KAAK,CAAC;EAC/E,SAAS,OAAO,YACd,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,OAAO,OAAO,OAAO,CAAC;EACjF,SAAS,OAAO,YACd,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,OAAO,OAAO,OAAO,CAAC;CACnF,CAAC,CACH;CAEF,OAAO;AACT,GACA,OAAO,UAAU,UACf,OAAO,GAAG,6BAA6B,CAAC,CAAC,KAAK,IAC1C,QACA,mBAAmB,yCAAyC,CAClE,CACF"}
@@ -1,9 +1,9 @@
1
1
  import { sqlBatchAssertion } from "./d1-planning.mjs";
2
2
  import { appendSqlBatchStatement } from "./sql-commit.mjs";
3
+ import { randomId } from "./crypto.mjs";
3
4
  import { passkeyMatchesAccess, validPasskeyCeremony } from "./passkey-policy.mjs";
4
5
  import { passkeyNativeInvariant } from "./passkey-native-state.mjs";
5
6
  import { preparePasskeyNative } from "./passkey-native.mjs";
6
- import { randomId } from "./crypto.mjs";
7
7
  import { passkeyCanonicalJson, passkeyDigest } from "./passkey-actions.mjs";
8
8
  import { makePasskeyNativeWrites } from "./passkey-native-write.mjs";
9
9
  import { Effect, Schema } from "effect";
@@ -1,10 +1,12 @@
1
1
  import { sqlMapping } from "./native-sql-table.mjs";
2
2
  import { exactSqlText } from "./sql-change.mjs";
3
+ import { canJoinTextColumns } from "./storage-validation.mjs";
3
4
  import { passkeyCredentialKey } from "./passkey-policy.mjs";
4
5
  import { makePasskeyNativeFlow } from "./passkey-native-flow.mjs";
5
6
  import { Effect, Schema } from "effect";
6
7
  import { SqlClient } from "effect/sql";
7
8
  import * as M from "@yielded/auth/Passkey";
9
+ import { AuthenticationClock } from "@yielded/auth/Operations";
8
10
  //#region src/internal/passkey-native-state.ts
9
11
  const passkeyNativeInvariant = (value) => {
10
12
  if (!value) throw M.PasskeyUnavailable.make({});
@@ -119,6 +121,11 @@ const makePasskeyNativeReadState = Effect.fnUntraced(function* (tables, read, cl
119
121
  const selected = (kind, alias) => {
120
122
  return sql`(select * from ${(kind === "subject" ? subject : kind === "authority" ? factor : credential).name} where ${active(kind)}) as ${sql(alias)}`;
121
123
  };
124
+ const joinedIds = yield* canJoinTextColumns([
125
+ subject.unencodedTextColumn?.(read.subject.id),
126
+ credential.unencodedTextColumn?.(read.credential.subjectId),
127
+ factor.unencodedTextColumn?.(read.authority.subjectId)
128
+ ]).pipe(Effect.mapError(() => M.PasskeyUnavailable.make({})));
122
129
  return {
123
130
  sql,
124
131
  tables,
@@ -136,31 +143,32 @@ const makePasskeyNativeReadState = Effect.fnUntraced(function* (tables, read, cl
136
143
  readAuthority,
137
144
  lookup: Effect.fnUntraced(function* (rpId, protocolCredentialId) {
138
145
  const key = yield* passkeyCredentialKey(rpId, protocolCredentialId);
139
- if (subject.unencodedTextColumn?.(read.subject.id) !== void 0 && credential.unencodedTextColumn?.(read.credential.subjectId) !== void 0 && factor.unencodedTextColumn?.(read.authority.subjectId) !== void 0) {
140
- const s = joinedSubject, c = joinedCredential, f = joinedFactor;
141
- const rows = yield* sql`select ${s.fields("s_")}, ${c.fields("c_")}, ${f.fields("f_")}
142
- from ${selected("credential", "passkey_credential")}
143
- join ${selected("subject", "passkey_subject")} on ${exactSqlText(sql, s.column(read.subject.id), c.column(read.credential.subjectId))}
144
- left join ${selected("authority", "passkey_factor")} on ${exactSqlText(sql, f.column(read.authority.subjectId), c.column(read.credential.subjectId))}
145
- where ${exactSqlText(sql, c.column(read.credential.credentialKey), c.value(read.credential.credentialKey, key))} limit 65`;
146
- if (rows.length === 0 || rows.length > 64) return void 0;
147
- const row = c.decode(rows[0], "c_");
148
- const subjectRow = s.decode(rows[0], "s_");
149
- const owner = read.credential.decodeSubjectId(row);
146
+ let nativeId;
147
+ if (!joinedIds) {
148
+ const found = yield* sql`select ${credential.fields("c_")} from ${credential.name} where ${exactSqlText(sql, credential.column(read.credential.credentialKey), credential.value(read.credential.credentialKey, key))} and ${active("credential")}`;
149
+ if (found.length !== 1) return void 0;
150
+ const row = credential.decode(found[0], "c_");
150
151
  invariant(row[read.credential.rpId] === rpId && row[read.credential.protocolCredentialId] === protocolCredentialId);
151
- invariant(rows.every((selected) => {
152
- const other = c.decode(selected, "c_");
153
- const subjectValue = s.decode(selected, "s_");
154
- return sameId(owner, read.credential.decodeSubjectId(other)) && sameId(owner, read.subject.decodeId(subjectValue)) && other[read.credential.credentialId] === row[read.credential.credentialId] && other[read.credential.credentialRevision] === row[read.credential.credentialRevision] && subjectValue[read.subject.securityRevision] === subjectRow[read.subject.securityRevision];
155
- }));
156
- return yield* decodeCredential(row, revision(subjectRow, rows.map((row) => f.decode(row, "f_"))), subjectRow);
152
+ nativeId = read.credential.decodeSubjectId(row);
157
153
  }
158
- const rows = yield* sql`select ${credential.fields("c_")} from ${credential.name} where ${credential.column(read.credential.credentialKey)} = ${credential.value(read.credential.credentialKey, key)} and ${active("credential")}`;
159
- if (rows.length !== 1) return void 0;
160
- const row = credential.decode(rows[0], "c_");
161
- invariant(row[read.credential.rpId] === rpId && row[read.credential.protocolCredentialId] === protocolCredentialId);
162
- const current = yield* readAuthority(read.credential.decodeSubjectId(row), false);
163
- return current === void 0 ? void 0 : yield* decodeCredential(row, current.revision, current.row);
154
+ const s = joinedSubject, c = joinedCredential, f = joinedFactor;
155
+ const bindOwner = (table, key) => joinedIds ? exactSqlText(sql, table.column(key), c.column(read.credential.subjectId)) : sql`${table.column(key)} = ${table.value(key, nativeId)}`;
156
+ const rows = yield* sql`select ${s.fields("s_")}, ${c.fields("c_")}, ${f.fields("f_")}
157
+ from ${selected("credential", "passkey_credential")}
158
+ join ${selected("subject", "passkey_subject")} on ${bindOwner(s, read.subject.id)}
159
+ left join ${selected("authority", "passkey_factor")} on ${bindOwner(f, read.authority.subjectId)}
160
+ where ${exactSqlText(sql, c.column(read.credential.credentialKey), c.value(read.credential.credentialKey, key))}${joinedIds ? sql`` : sql` and ${bindOwner(c, read.credential.subjectId)}`} limit 65`;
161
+ if (rows.length === 0 || rows.length > 64) return void 0;
162
+ const row = c.decode(rows[0], "c_");
163
+ const subjectRow = s.decode(rows[0], "s_");
164
+ const owner = read.credential.decodeSubjectId(row);
165
+ invariant((joinedIds || sameId(owner, nativeId)) && row[read.credential.rpId] === rpId && row[read.credential.protocolCredentialId] === protocolCredentialId);
166
+ invariant(rows.every((selected) => {
167
+ const other = c.decode(selected, "c_");
168
+ const subjectValue = s.decode(selected, "s_");
169
+ return sameId(owner, read.credential.decodeSubjectId(other)) && sameId(owner, read.subject.decodeId(subjectValue)) && other[read.credential.credentialId] === row[read.credential.credentialId] && other[read.credential.credentialRevision] === row[read.credential.credentialRevision] && subjectValue[read.subject.securityRevision] === subjectRow[read.subject.securityRevision];
170
+ }));
171
+ return yield* decodeCredential(row, revision(subjectRow, rows.map((row) => f.decode(row, "f_"))), subjectRow);
164
172
  }),
165
173
  joinedSubject,
166
174
  joinedFactor,
@@ -171,9 +179,11 @@ const makePasskeyNativeReadState = Effect.fnUntraced(function* (tables, read, cl
171
179
  });
172
180
  const makePasskeyNativeState = Effect.fnUntraced(function* (tables, mapping) {
173
181
  const state = yield* makePasskeyNativeReadState(tables, mapping.read, mapping.clock);
182
+ const clockPolicy = yield* AuthenticationClock;
174
183
  const ceremony = yield* makePasskeyNativeFlow(tables, mapping);
175
184
  return {
176
185
  ...state,
186
+ clockPolicy,
177
187
  mapping,
178
188
  ceremony,
179
189
  flow: ceremony.flow,