@effect-auth/core 0.1.0-alpha.10 → 0.1.0-alpha.13

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 (93) hide show
  1. package/README.md +65 -4
  2. package/dist/AuditLog.d.ts +60 -0
  3. package/dist/AuditLog.d.ts.map +1 -1
  4. package/dist/AuditLog.js +105 -0
  5. package/dist/AuditLog.js.map +1 -1
  6. package/dist/AuthKernel.d.ts +1 -1
  7. package/dist/Client.d.ts +72 -1
  8. package/dist/Client.d.ts.map +1 -1
  9. package/dist/Client.js +218 -1
  10. package/dist/Client.js.map +1 -1
  11. package/dist/DomainVerification.d.ts +200 -0
  12. package/dist/DomainVerification.d.ts.map +1 -0
  13. package/dist/DomainVerification.js +304 -0
  14. package/dist/DomainVerification.js.map +1 -0
  15. package/dist/EffectQbSqliteStorage.d.ts +389 -4
  16. package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
  17. package/dist/EffectQbSqliteStorage.js +1191 -4
  18. package/dist/EffectQbSqliteStorage.js.map +1 -1
  19. package/dist/EmailOtp.d.ts +2 -2
  20. package/dist/HttpApi/Api.d.ts +477 -23
  21. package/dist/HttpApi/Api.d.ts.map +1 -1
  22. package/dist/HttpApi/Api.js +705 -4
  23. package/dist/HttpApi/Api.js.map +1 -1
  24. package/dist/HttpApi/Endpoints.d.ts +133 -5
  25. package/dist/HttpApi/Endpoints.d.ts.map +1 -1
  26. package/dist/HttpApi/Endpoints.js +57 -2
  27. package/dist/HttpApi/Endpoints.js.map +1 -1
  28. package/dist/HttpApi/Errors.d.ts +32 -0
  29. package/dist/HttpApi/Errors.d.ts.map +1 -1
  30. package/dist/HttpApi/Errors.js +46 -0
  31. package/dist/HttpApi/Errors.js.map +1 -1
  32. package/dist/HttpApi/Schemas.d.ts +149 -8
  33. package/dist/HttpApi/Schemas.d.ts.map +1 -1
  34. package/dist/HttpApi/Schemas.js +128 -0
  35. package/dist/HttpApi/Schemas.js.map +1 -1
  36. package/dist/Identifiers.d.ts +3 -0
  37. package/dist/Identifiers.d.ts.map +1 -1
  38. package/dist/Identifiers.js +2 -0
  39. package/dist/Identifiers.js.map +1 -1
  40. package/dist/IncidentAction.d.ts +80 -0
  41. package/dist/IncidentAction.d.ts.map +1 -0
  42. package/dist/IncidentAction.js +158 -0
  43. package/dist/IncidentAction.js.map +1 -0
  44. package/dist/Jwt.d.ts +10 -0
  45. package/dist/Jwt.d.ts.map +1 -1
  46. package/dist/Jwt.js +209 -0
  47. package/dist/Jwt.js.map +1 -1
  48. package/dist/LoginNotification.d.ts +1 -1
  49. package/dist/LoginRisk.d.ts +272 -1
  50. package/dist/LoginRisk.d.ts.map +1 -1
  51. package/dist/LoginRisk.js +427 -0
  52. package/dist/LoginRisk.js.map +1 -1
  53. package/dist/MachineAuth.d.ts +73 -0
  54. package/dist/MachineAuth.d.ts.map +1 -0
  55. package/dist/MachineAuth.js +147 -0
  56. package/dist/MachineAuth.js.map +1 -0
  57. package/dist/MagicLink.d.ts +2 -2
  58. package/dist/OAuth.d.ts +957 -1
  59. package/dist/OAuth.d.ts.map +1 -1
  60. package/dist/OAuth.js +1403 -1
  61. package/dist/OAuth.js.map +1 -1
  62. package/dist/Password.d.ts +2 -2
  63. package/dist/Retention.d.ts +98 -0
  64. package/dist/Retention.d.ts.map +1 -0
  65. package/dist/Retention.js +204 -0
  66. package/dist/Retention.js.map +1 -0
  67. package/dist/SecurityTimeline.d.ts +8 -2
  68. package/dist/SecurityTimeline.d.ts.map +1 -1
  69. package/dist/SecurityTimeline.js +75 -0
  70. package/dist/SecurityTimeline.js.map +1 -1
  71. package/dist/StorageMigrations.d.ts +11 -1
  72. package/dist/StorageMigrations.d.ts.map +1 -1
  73. package/dist/StorageMigrations.js +206 -0
  74. package/dist/StorageMigrations.js.map +1 -1
  75. package/dist/Webhook.d.ts +310 -2
  76. package/dist/Webhook.d.ts.map +1 -1
  77. package/dist/Webhook.js +511 -1
  78. package/dist/Webhook.js.map +1 -1
  79. package/dist/index.d.ts +4 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +4 -0
  82. package/dist/index.js.map +1 -1
  83. package/migrations/0014_auth_webhook_outbox.sql +16 -0
  84. package/migrations/0015_auth_webhook_replay.sql +7 -0
  85. package/migrations/0016_auth_login_risk_history.sql +23 -0
  86. package/migrations/0017_auth_audit_log.sql +17 -0
  87. package/migrations/0018_auth_domain_verification.sql +18 -0
  88. package/migrations/0019_auth_oauth_authorization_code.sql +17 -0
  89. package/migrations/0020_auth_oauth_provider_mode_token.sql +24 -0
  90. package/migrations/0021_auth_oauth_client_secret.sql +14 -0
  91. package/migrations/0022_auth_oauth_client.sql +15 -0
  92. package/migrations/0023_auth_oauth_consent.sql +15 -0
  93. package/package.json +17 -1
@@ -1,11 +1,14 @@
1
1
  import { Data, Effect, Layer, Option, Redacted } from "effect";
2
2
  import { Column, Executor, Query, Scalar, Table } from "effect-qb/sqlite";
3
+ import { AuditLogStore, AuditLogStoreError, auditEventActorUserId, auditEventUserId, filterAuditLogEvents, } from "./AuditLog.js";
3
4
  import { ApiKeyId, ApiKeyPrefix, ApiKeySecretHash, ApiKeyStore, ApiKeyStoreError, } from "./ApiKey.js";
4
- import { AuthFlowId, ChallengeId, CredentialId, Email, OAuthAccountId, OAuthProviderId, SessionId, UnixMillis, UserId, } from "./Identifiers.js";
5
+ import { DomainName, DomainVerificationId, DomainVerificationProofToken, DomainVerificationStore, DomainVerificationStoreError, } from "./DomainVerification.js";
6
+ import { AuthFlowId, ChallengeId, CredentialId, Email, OAuthAccountId, OAuthClientId, OAuthProviderId, SessionId, UnixMillis, UserId, } from "./Identifiers.js";
7
+ import { currentUnixMillis } from "./Internal.js";
5
8
  import { JwtError, JwtRevocationStore } from "./Jwt.js";
6
9
  import { LoginApprovalReviewError, LoginApprovalReviewStore, } from "./LoginApproval.js";
7
- import { LoginRiskContext, LoginRiskSignal } from "./LoginRisk.js";
8
- import { OAuthAccountStore, OAuthAccountStoreError, OAuthProviderTokenVault, OAuthProviderTokenVaultError, } from "./OAuth.js";
10
+ import { LoginRiskContext, LoginRiskHistory, LoginRiskHistoryError, LoginRiskSignal, maxLoginRiskLevel, } from "./LoginRisk.js";
11
+ import { OAuthAccountStore, OAuthAccountStoreError, OAuthAuthorizationCodeStore, OAuthAuthorizationCodeStoreError, OAuthClientStore, OAuthClientStoreError, OAuthConsentStore, OAuthConsentStoreError, OAuthClientSecretHash, OAuthClientSecretPrefix, OAuthClientSecretStore, OAuthClientSecretStoreError, OAuthCodeChallenge, OAuthProviderModeTokenStore, OAuthProviderModeTokenStoreError, OAuthProviderTokenVault, OAuthProviderTokenVaultError, } from "./OAuth.js";
9
12
  import { PasskeyCredentialId, PasskeyCredentialStore, PasskeyCredentialStoreError, } from "./Passkey.js";
10
13
  import { RecoveryCodeHash, RecoveryCodeStore, RecoveryCodeStoreError, } from "./RecoveryCode.js";
11
14
  import { RefreshTokenFamilyId, RefreshTokenId, RefreshTokenSecretHash, RefreshTokenStore, RefreshTokenStoreError, } from "./RefreshToken.js";
@@ -13,6 +16,7 @@ import { filterSecurityTimelineEvents, SecurityTimelineEventId, SecurityTimeline
13
16
  import { CredentialStore, SessionStore, StorageError, UserStore, VerificationStore, } from "./Storage.js";
14
17
  import { TotpFactorStore, TotpFactorStoreError, TotpSecret, } from "./Totp.js";
15
18
  import { filterTrustedDevicesForUser, TrustedDeviceError, TrustedDeviceStore, } from "./TrustedDevice.js";
19
+ import { filterDueWebhookOutboxRecords, WebhookOutboxItemId, WebhookOutboxStore, WebhookOutboxStoreError, WebhookReplayStore, WebhookReplayStoreError, } from "./Webhook.js";
16
20
  export class D1EffectQbError extends Data.TaggedError("D1EffectQbError") {
17
21
  }
18
22
  const makeAuthUserTable = (name) => Table.make(name, {
@@ -59,6 +63,17 @@ const makeAuthVerificationTable = (name) => Table.make(name, {
59
63
  metadata: Column.text().pipe(Column.nullable),
60
64
  consumed_at: Column.int().pipe(Column.nullable),
61
65
  });
66
+ const makeAuthAuditLogTable = (name) => Table.make(name, {
67
+ id: Column.text().pipe(Column.nullable),
68
+ type: Column.text(),
69
+ user_id: Column.text().pipe(Column.nullable),
70
+ actor_user_id: Column.text().pipe(Column.nullable),
71
+ occurred_at: Column.int(),
72
+ request_ip_hash: Column.text().pipe(Column.nullable),
73
+ request_user_agent_hash: Column.text().pipe(Column.nullable),
74
+ event: Column.text(),
75
+ created_at: Column.int(),
76
+ });
62
77
  const makeAuthLoginApprovalReviewTable = (name) => Table.make(name, {
63
78
  approval_challenge_id: Column.text().pipe(Column.primaryKey),
64
79
  flow_id: Column.text(),
@@ -77,6 +92,24 @@ const makeAuthLoginApprovalReviewTable = (name) => Table.make(name, {
77
92
  metadata: Column.text().pipe(Column.nullable),
78
93
  review_metadata: Column.text().pipe(Column.nullable),
79
94
  });
95
+ const makeAuthLoginRiskHistoryTable = (name) => Table.make(name, {
96
+ id: Column.text().pipe(Column.primaryKey),
97
+ user_id: Column.text(),
98
+ occurred_at: Column.int(),
99
+ outcome: Column.text(),
100
+ method: Column.text(),
101
+ amr: Column.text(),
102
+ aal: Column.text(),
103
+ device_status: Column.text(),
104
+ device_key: Column.text().pipe(Column.nullable),
105
+ location_key: Column.text().pipe(Column.nullable),
106
+ country: Column.text().pipe(Column.nullable),
107
+ region: Column.text().pipe(Column.nullable),
108
+ latitude_micro: Column.int().pipe(Column.nullable),
109
+ longitude_micro: Column.int().pipe(Column.nullable),
110
+ risk_level: Column.text().pipe(Column.nullable),
111
+ created_at: Column.int(),
112
+ });
80
113
  const makeAuthTrustedDeviceTable = (name) => Table.make(name, {
81
114
  user_id: Column.text(),
82
115
  token_hash: Column.text(),
@@ -179,6 +212,84 @@ const makeAuthOAuthProviderTokenVaultTable = (name) => Table.make(name, {
179
212
  revoked_at: Column.int().pipe(Column.nullable),
180
213
  revocation_reason: Column.text().pipe(Column.nullable),
181
214
  });
215
+ const makeAuthOAuthClientTable = (name) => Table.make(name, {
216
+ id: Column.text().pipe(Column.primaryKey),
217
+ type: Column.text(),
218
+ status: Column.text(),
219
+ name: Column.text().pipe(Column.nullable),
220
+ redirect_uris: Column.text(),
221
+ allowed_grant_types: Column.text(),
222
+ allowed_response_types: Column.text(),
223
+ allowed_scopes: Column.text().pipe(Column.nullable),
224
+ created_at: Column.int().pipe(Column.nullable),
225
+ updated_at: Column.int().pipe(Column.nullable),
226
+ metadata: Column.text().pipe(Column.nullable),
227
+ });
228
+ const makeAuthOAuthConsentTable = (name) => Table.make(name, {
229
+ id: Column.text().pipe(Column.primaryKey),
230
+ user_id: Column.text(),
231
+ client_id: Column.text(),
232
+ scopes: Column.text(),
233
+ granted_at: Column.int(),
234
+ expires_at: Column.int().pipe(Column.nullable),
235
+ revoked_at: Column.int().pipe(Column.nullable),
236
+ metadata: Column.text().pipe(Column.nullable),
237
+ });
238
+ const makeAuthOAuthAuthorizationCodeTable = (name) => Table.make(name, {
239
+ code_hash: Column.text().pipe(Column.primaryKey),
240
+ client_id: Column.text(),
241
+ subject: Column.text(),
242
+ redirect_uri: Column.text(),
243
+ scopes: Column.text(),
244
+ code_challenge: Column.text().pipe(Column.nullable),
245
+ code_challenge_method: Column.text().pipe(Column.nullable),
246
+ issued_at: Column.int(),
247
+ expires_at: Column.int(),
248
+ consumed_at: Column.int().pipe(Column.nullable),
249
+ metadata: Column.text().pipe(Column.nullable),
250
+ });
251
+ const makeAuthOAuthClientSecretTable = (name) => Table.make(name, {
252
+ prefix: Column.text().pipe(Column.primaryKey),
253
+ client_id: Column.text(),
254
+ secret_hash: Column.text(),
255
+ authentication_methods: Column.text(),
256
+ created_at: Column.int(),
257
+ expires_at: Column.int().pipe(Column.nullable),
258
+ last_used_at: Column.int().pipe(Column.nullable),
259
+ revoked_at: Column.int().pipe(Column.nullable),
260
+ metadata: Column.text().pipe(Column.nullable),
261
+ });
262
+ const makeAuthOAuthProviderModeTokenTable = (name) => Table.make(name, {
263
+ token_hash: Column.text().pipe(Column.primaryKey),
264
+ token_type: Column.text(),
265
+ client_id: Column.text(),
266
+ subject: Column.text(),
267
+ scopes: Column.text(),
268
+ issued_at: Column.int(),
269
+ expires_at: Column.int(),
270
+ issuer: Column.text().pipe(Column.nullable),
271
+ audience: Column.text().pipe(Column.nullable),
272
+ jwt_id: Column.text().pipe(Column.nullable),
273
+ revoked_at: Column.int().pipe(Column.nullable),
274
+ revocation_reason: Column.text().pipe(Column.nullable),
275
+ rotated_at: Column.int().pipe(Column.nullable),
276
+ replaced_by_token_hash: Column.text().pipe(Column.nullable),
277
+ metadata: Column.text().pipe(Column.nullable),
278
+ });
279
+ const makeAuthDomainVerificationTable = (name) => Table.make(name, {
280
+ id: Column.text().pipe(Column.primaryKey),
281
+ owner_id: Column.text(),
282
+ domain: Column.text(),
283
+ proof_method: Column.text(),
284
+ proof_token: Column.text(),
285
+ status: Column.text(),
286
+ created_at: Column.int(),
287
+ expires_at: Column.int(),
288
+ verified_at: Column.int().pipe(Column.nullable),
289
+ revoked_at: Column.int().pipe(Column.nullable),
290
+ last_checked_at: Column.int().pipe(Column.nullable),
291
+ metadata: Column.text().pipe(Column.nullable),
292
+ });
182
293
  const makeAuthSecurityTimelineTable = (name) => Table.make(name, {
183
294
  id: Column.text().pipe(Column.primaryKey),
184
295
  user_id: Column.text(),
@@ -191,6 +302,23 @@ const makeAuthSecurityTimelineTable = (name) => Table.make(name, {
191
302
  request: Column.text().pipe(Column.nullable),
192
303
  metadata: Column.text().pipe(Column.nullable),
193
304
  });
305
+ const makeAuthWebhookOutboxTable = (name) => Table.make(name, {
306
+ id: Column.text().pipe(Column.primaryKey),
307
+ endpoint_key: Column.text(),
308
+ event: Column.text(),
309
+ status: Column.text(),
310
+ attempts: Column.int(),
311
+ next_attempt_at: Column.int(),
312
+ created_at: Column.int(),
313
+ updated_at: Column.int(),
314
+ delivered_at: Column.int().pipe(Column.nullable),
315
+ last_error: Column.text().pipe(Column.nullable),
316
+ });
317
+ const makeAuthWebhookReplayTable = (name) => Table.make(name, {
318
+ id: Column.text().pipe(Column.primaryKey),
319
+ expires_at: Column.int(),
320
+ created_at: Column.int(),
321
+ });
194
322
  const authSqliteTablesTypeId = Symbol.for("@effect-auth/core/EffectQbSqliteStorage/AuthSqliteTables");
195
323
  const jsonEncode = (value) => value === undefined ? null : JSON.stringify(value);
196
324
  const jsonDecode = (value) => {
@@ -220,6 +348,7 @@ const queryInsert = (table, values) => Query.insert(table, values);
220
348
  const queryUpdate = (table, values) => Query.update(table, values);
221
349
  const queryEq = (left, right) => Query.eq(left, right);
222
350
  const queryGt = (left, right) => Query.gt(left, right);
351
+ const queryLt = (left, right) => Query.lt(left, right);
223
352
  const queryLte = (left, right) => Query.lte(left, right);
224
353
  const queryIsNull = (value) => Query.isNull(value);
225
354
  const queryIn = (value, first, ...rest) => Query.in(value, first, ...rest);
@@ -353,6 +482,10 @@ const decodeDefaultVerificationRow = (row) => omitUndefined({
353
482
  const decodeVerificationRow = (tables, row) => tables.verification.decode === undefined
354
483
  ? decodeDefaultVerificationRow(row)
355
484
  : tables.verification.decode(row);
485
+ const decodeDefaultAuditLogEvent = (row) => jsonDecode(row.event);
486
+ const decodeAuditLogEvent = (tables, row) => tables.auditLog.decode === undefined
487
+ ? decodeDefaultAuditLogEvent(row)
488
+ : tables.auditLog.decode(row);
356
489
  const decodeDefaultLoginApprovalReviewRow = (row) => omitUndefined({
357
490
  approvalChallengeId: ChallengeId(row.approvalChallengeId),
358
491
  flowId: AuthFlowId(row.flowId),
@@ -376,6 +509,29 @@ const decodeDefaultLoginApprovalReviewRow = (row) => omitUndefined({
376
509
  const decodeLoginApprovalReviewRow = (tables, row) => tables.loginApprovalReview.decode === undefined
377
510
  ? decodeDefaultLoginApprovalReviewRow(row)
378
511
  : tables.loginApprovalReview.decode(row);
512
+ const decodeDefaultLoginRiskHistoryRecord = (row) => omitUndefined({
513
+ id: row.id,
514
+ userId: UserId(row.userId),
515
+ occurredAt: UnixMillis(Number(row.occurredAt)),
516
+ outcome: row.outcome,
517
+ method: row.method,
518
+ amr: decodeAmr(row.amr),
519
+ aal: row.aal,
520
+ device: { status: row.deviceStatus },
521
+ deviceKey: row.deviceKey ?? undefined,
522
+ locationKey: row.locationKey ?? undefined,
523
+ country: row.country ?? undefined,
524
+ region: row.region ?? undefined,
525
+ latitude: row.latitudeMicro === null ? undefined : Number(row.latitudeMicro) / 1_000_000,
526
+ longitude: row.longitudeMicro === null
527
+ ? undefined
528
+ : Number(row.longitudeMicro) / 1_000_000,
529
+ riskLevel: row.riskLevel === null ? undefined : row.riskLevel,
530
+ createdAt: UnixMillis(Number(row.createdAt)),
531
+ });
532
+ const decodeLoginRiskHistoryRecord = (tables, row) => tables.loginRiskHistory.decode === undefined
533
+ ? decodeDefaultLoginRiskHistoryRecord(row)
534
+ : tables.loginRiskHistory.decode(row);
379
535
  const decodeDefaultTrustedDeviceRow = (row) => omitUndefined({
380
536
  userId: UserId(row.userId),
381
537
  tokenHash: row.tokenHash,
@@ -525,6 +681,134 @@ const decodeDefaultOAuthProviderTokenVaultRow = (row) => omitUndefined({
525
681
  const decodeOAuthProviderTokenVaultRow = (tables, row) => tables.oauthProviderTokenVault.decode === undefined
526
682
  ? decodeDefaultOAuthProviderTokenVaultRow(row)
527
683
  : tables.oauthProviderTokenVault.decode(row);
684
+ const decodeDefaultOAuthClientRow = (row) => omitUndefined({
685
+ id: OAuthClientId(row.id),
686
+ type: row.type,
687
+ status: row.status,
688
+ name: row.name ?? undefined,
689
+ redirectUris: decodeStringArray(row.redirectUris),
690
+ allowedGrantTypes: decodeStringArray(row.allowedGrantTypes),
691
+ allowedResponseTypes: decodeStringArray(row.allowedResponseTypes),
692
+ allowedScopes: decodeOptionalStringArray(row.allowedScopes),
693
+ createdAt: optionalUnixMillis(row.createdAt),
694
+ updatedAt: optionalUnixMillis(row.updatedAt),
695
+ metadata: jsonDecode(row.metadata),
696
+ });
697
+ const decodeOAuthClientRow = (tables, row) => tables.oauthClient.decode === undefined
698
+ ? decodeDefaultOAuthClientRow(row)
699
+ : tables.oauthClient.decode(row);
700
+ const decodeDefaultOAuthConsentRow = (row) => omitUndefined({
701
+ userId: UserId(row.userId),
702
+ clientId: OAuthClientId(row.clientId),
703
+ scopes: decodeStringArray(row.scopes),
704
+ grantedAt: UnixMillis(Number(row.grantedAt)),
705
+ expiresAt: optionalUnixMillis(row.expiresAt),
706
+ revokedAt: optionalUnixMillis(row.revokedAt),
707
+ metadata: jsonDecode(row.metadata),
708
+ });
709
+ const decodeOAuthConsentRow = (tables, row) => tables.oauthConsent.decode === undefined
710
+ ? decodeDefaultOAuthConsentRow(row)
711
+ : tables.oauthConsent.decode(row);
712
+ const oauthConsentCoversScopes = (record, scopes = []) => scopes.every((scope) => record.scopes.includes(scope));
713
+ const isOAuthConsentActive = (record, input) => record.revokedAt === undefined &&
714
+ (record.expiresAt === undefined || Number(record.expiresAt) > Number(input.now)) &&
715
+ oauthConsentCoversScopes(record, input.scopes);
716
+ const oauthClientRedirectUriValidationResult = (client, input) => {
717
+ if (client === undefined) {
718
+ return { valid: false, reason: "unknown_client" };
719
+ }
720
+ if (client.status !== "active") {
721
+ return { valid: false, reason: "disabled_client" };
722
+ }
723
+ const responseType = input.responseType ?? "code";
724
+ if (!client.allowedResponseTypes.includes(responseType)) {
725
+ return { valid: false, reason: "unsupported_response_type" };
726
+ }
727
+ if (input.grantType !== undefined &&
728
+ !client.allowedGrantTypes.includes(input.grantType)) {
729
+ return { valid: false, reason: "unsupported_grant_type" };
730
+ }
731
+ if (!client.redirectUris.includes(input.redirectUri)) {
732
+ return { valid: false, reason: "redirect_uri_mismatch" };
733
+ }
734
+ return { valid: true, client };
735
+ };
736
+ const decodeDefaultOAuthAuthorizationCodeRow = (row) => omitUndefined({
737
+ codeHash: row.codeHash,
738
+ clientId: OAuthClientId(row.clientId),
739
+ subject: row.subject,
740
+ redirectUri: row.redirectUri,
741
+ scopes: decodeStringArray(row.scopes),
742
+ codeChallenge: row.codeChallenge === null ? undefined : OAuthCodeChallenge(row.codeChallenge),
743
+ codeChallengeMethod: row.codeChallengeMethod === null
744
+ ? undefined
745
+ : row.codeChallengeMethod,
746
+ issuedAt: UnixMillis(Number(row.issuedAt)),
747
+ expiresAt: UnixMillis(Number(row.expiresAt)),
748
+ consumedAt: optionalUnixMillis(row.consumedAt),
749
+ metadata: jsonDecode(row.metadata),
750
+ });
751
+ const decodeOAuthAuthorizationCodeRow = (tables, row) => tables.oauthAuthorizationCode.decode === undefined
752
+ ? decodeDefaultOAuthAuthorizationCodeRow(row)
753
+ : tables.oauthAuthorizationCode.decode(row);
754
+ const decodeDefaultOAuthClientSecretRow = (row) => omitUndefined({
755
+ prefix: OAuthClientSecretPrefix(row.prefix),
756
+ clientId: OAuthClientId(row.clientId),
757
+ secretHash: OAuthClientSecretHash(row.secretHash),
758
+ authenticationMethods: decodeStringArray(row.authenticationMethods),
759
+ createdAt: UnixMillis(Number(row.createdAt)),
760
+ expiresAt: optionalUnixMillis(row.expiresAt),
761
+ lastUsedAt: optionalUnixMillis(row.lastUsedAt),
762
+ revokedAt: optionalUnixMillis(row.revokedAt),
763
+ metadata: jsonDecode(row.metadata),
764
+ });
765
+ const decodeOAuthClientSecretRow = (tables, row) => tables.oauthClientSecret.decode === undefined
766
+ ? decodeDefaultOAuthClientSecretRow(row)
767
+ : tables.oauthClientSecret.decode(row);
768
+ const decodeOAuthProviderModeTokenAudience = (value) => {
769
+ const decoded = jsonDecode(value);
770
+ if (typeof decoded === "string") {
771
+ return decoded;
772
+ }
773
+ return Array.isArray(decoded) ? decoded.map(String) : undefined;
774
+ };
775
+ const decodeDefaultOAuthProviderModeTokenRow = (row) => omitUndefined({
776
+ tokenHash: row.tokenHash,
777
+ tokenType: row.tokenType,
778
+ clientId: OAuthClientId(row.clientId),
779
+ subject: row.subject,
780
+ scopes: decodeStringArray(row.scopes),
781
+ issuedAt: UnixMillis(Number(row.issuedAt)),
782
+ expiresAt: UnixMillis(Number(row.expiresAt)),
783
+ issuer: row.issuer ?? undefined,
784
+ audience: decodeOAuthProviderModeTokenAudience(row.audience),
785
+ jwtId: row.jwtId ?? undefined,
786
+ revokedAt: optionalUnixMillis(row.revokedAt),
787
+ revocationReason: row.revocationReason ?? undefined,
788
+ rotatedAt: optionalUnixMillis(row.rotatedAt),
789
+ replacedByTokenHash: row.replacedByTokenHash ?? undefined,
790
+ metadata: jsonDecode(row.metadata),
791
+ });
792
+ const decodeOAuthProviderModeTokenRow = (tables, row) => tables.oauthProviderModeToken.decode === undefined
793
+ ? decodeDefaultOAuthProviderModeTokenRow(row)
794
+ : tables.oauthProviderModeToken.decode(row);
795
+ const decodeDefaultDomainVerificationRecord = (row) => omitUndefined({
796
+ id: DomainVerificationId(row.id),
797
+ ownerId: row.ownerId,
798
+ domain: DomainName(row.domain),
799
+ proofMethod: row.proofMethod,
800
+ proofToken: DomainVerificationProofToken(row.proofToken),
801
+ status: row.status,
802
+ createdAt: UnixMillis(Number(row.createdAt)),
803
+ expiresAt: UnixMillis(Number(row.expiresAt)),
804
+ verifiedAt: optionalUnixMillis(row.verifiedAt),
805
+ revokedAt: optionalUnixMillis(row.revokedAt),
806
+ lastCheckedAt: optionalUnixMillis(row.lastCheckedAt),
807
+ metadata: jsonDecode(row.metadata),
808
+ });
809
+ const decodeDomainVerificationRecord = (tables, row) => tables.domainVerification.decode === undefined
810
+ ? decodeDefaultDomainVerificationRecord(row)
811
+ : tables.domainVerification.decode(row);
528
812
  const decodeDefaultSecurityTimelineEvent = (row) => omitUndefined({
529
813
  id: SecurityTimelineEventId(row.id),
530
814
  userId: UserId(row.userId),
@@ -540,6 +824,21 @@ const decodeDefaultSecurityTimelineEvent = (row) => omitUndefined({
540
824
  const decodeSecurityTimelineEvent = (tables, row) => tables.securityTimeline.decode === undefined
541
825
  ? decodeDefaultSecurityTimelineEvent(row)
542
826
  : tables.securityTimeline.decode(row);
827
+ const decodeDefaultWebhookOutboxRecord = (row) => omitUndefined({
828
+ id: WebhookOutboxItemId(row.id),
829
+ endpointKey: row.endpointKey,
830
+ event: jsonDecode(row.event),
831
+ status: row.status,
832
+ attempts: Number(row.attempts),
833
+ nextAttemptAt: UnixMillis(Number(row.nextAttemptAt)),
834
+ createdAt: UnixMillis(Number(row.createdAt)),
835
+ updatedAt: UnixMillis(Number(row.updatedAt)),
836
+ deliveredAt: optionalUnixMillis(row.deliveredAt),
837
+ lastError: row.lastError ?? undefined,
838
+ });
839
+ const decodeWebhookOutboxRecord = (tables, row) => tables.webhookOutbox.decode === undefined
840
+ ? decodeDefaultWebhookOutboxRecord(row)
841
+ : tables.webhookOutbox.decode(row);
543
842
  const providerTokenVaultTokens = (record) => omitUndefined({
544
843
  accessTokenCiphertext: record.accessTokenCiphertext,
545
844
  refreshTokenCiphertext: record.refreshTokenCiphertext,
@@ -596,6 +895,17 @@ const verificationInsert = (row) => ({
596
895
  metadata: jsonEncode(row.metadata),
597
896
  consumed_at: null,
598
897
  });
898
+ const auditLogInsert = (event, createdAt) => ({
899
+ id: event.id ?? null,
900
+ type: event.type,
901
+ user_id: auditEventUserId(event) ?? null,
902
+ actor_user_id: auditEventActorUserId(event) ?? null,
903
+ occurred_at: Number(event.occurredAt),
904
+ request_ip_hash: event.request?.ipHash ?? null,
905
+ request_user_agent_hash: event.request?.userAgentHash ?? null,
906
+ event: JSON.stringify(event),
907
+ created_at: Number(createdAt),
908
+ });
599
909
  const loginApprovalReviewInsert = (row) => ({
600
910
  approval_challenge_id: row.approvalChallengeId,
601
911
  flow_id: row.flowId,
@@ -614,6 +924,27 @@ const loginApprovalReviewInsert = (row) => ({
614
924
  metadata: jsonEncode(row.metadata),
615
925
  review_metadata: jsonEncode(row.reviewMetadata),
616
926
  });
927
+ const coordinateMicro = (value) => value === undefined || !Number.isFinite(value)
928
+ ? null
929
+ : Math.round(value * 1_000_000);
930
+ const loginRiskHistoryInsert = (row) => ({
931
+ id: row.id,
932
+ user_id: row.userId,
933
+ occurred_at: Number(row.occurredAt),
934
+ outcome: row.outcome,
935
+ method: row.method,
936
+ amr: JSON.stringify(row.amr),
937
+ aal: row.aal,
938
+ device_status: row.device.status,
939
+ device_key: row.deviceKey ?? null,
940
+ location_key: row.locationKey ?? null,
941
+ country: row.country ?? null,
942
+ region: row.region ?? null,
943
+ latitude_micro: coordinateMicro(row.latitude),
944
+ longitude_micro: coordinateMicro(row.longitude),
945
+ risk_level: row.riskLevel ?? null,
946
+ created_at: Number(row.createdAt),
947
+ });
617
948
  const userMarkEmailVerifiedUpdate = (input) => ({
618
949
  email_verified: 1,
619
950
  updated_at: Number(input.updatedAt),
@@ -825,6 +1156,119 @@ const oauthProviderTokenVaultMarkRevoked = (input) => ({
825
1156
  revoked_at: Number(input.revokedAt),
826
1157
  revocation_reason: input.reason,
827
1158
  });
1159
+ const oauthClientValues = (record) => ({
1160
+ id: record.id,
1161
+ type: record.type,
1162
+ status: record.status,
1163
+ name: record.name ?? null,
1164
+ redirect_uris: JSON.stringify(record.redirectUris),
1165
+ allowed_grant_types: JSON.stringify(record.allowedGrantTypes),
1166
+ allowed_response_types: JSON.stringify(record.allowedResponseTypes),
1167
+ allowed_scopes: jsonEncode(record.allowedScopes),
1168
+ created_at: record.createdAt === undefined ? null : Number(record.createdAt),
1169
+ updated_at: record.updatedAt === undefined ? null : Number(record.updatedAt),
1170
+ metadata: jsonEncode(record.metadata),
1171
+ });
1172
+ const oauthConsentId = (input) => JSON.stringify([input.userId, input.clientId]);
1173
+ const oauthConsentValues = (record) => ({
1174
+ id: oauthConsentId(record),
1175
+ user_id: record.userId,
1176
+ client_id: record.clientId,
1177
+ scopes: JSON.stringify(record.scopes),
1178
+ granted_at: Number(record.grantedAt),
1179
+ expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
1180
+ revoked_at: record.revokedAt === undefined ? null : Number(record.revokedAt),
1181
+ metadata: jsonEncode(record.metadata),
1182
+ });
1183
+ const oauthConsentRevoke = (input, row) => ({
1184
+ revoked_at: Number(row.revokedAt ?? input.revokedAt),
1185
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
1186
+ });
1187
+ const oauthAuthorizationCodeValues = (record) => ({
1188
+ code_hash: record.codeHash,
1189
+ client_id: record.clientId,
1190
+ subject: record.subject,
1191
+ redirect_uri: record.redirectUri,
1192
+ scopes: JSON.stringify(record.scopes),
1193
+ code_challenge: record.codeChallenge ?? null,
1194
+ code_challenge_method: record.codeChallengeMethod ?? null,
1195
+ issued_at: Number(record.issuedAt),
1196
+ expires_at: Number(record.expiresAt),
1197
+ consumed_at: record.consumedAt === undefined ? null : Number(record.consumedAt),
1198
+ metadata: jsonEncode(record.metadata),
1199
+ });
1200
+ const oauthAuthorizationCodeMarkConsumed = (input) => ({
1201
+ consumed_at: Number(input.consumedAt),
1202
+ });
1203
+ const oauthClientSecretValues = (record) => ({
1204
+ prefix: record.prefix,
1205
+ client_id: record.clientId,
1206
+ secret_hash: record.secretHash,
1207
+ authentication_methods: JSON.stringify(record.authenticationMethods),
1208
+ created_at: Number(record.createdAt),
1209
+ expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
1210
+ last_used_at: record.lastUsedAt === undefined ? null : Number(record.lastUsedAt),
1211
+ revoked_at: record.revokedAt === undefined ? null : Number(record.revokedAt),
1212
+ metadata: jsonEncode(record.metadata),
1213
+ });
1214
+ const oauthClientSecretMarkUsed = (input) => omitUndefined({
1215
+ last_used_at: Number(input.lastUsedAt),
1216
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
1217
+ });
1218
+ const oauthClientSecretRevoke = (input, row) => ({
1219
+ revoked_at: Number(input.revokedAt),
1220
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
1221
+ });
1222
+ const oauthProviderModeTokenValues = (record) => ({
1223
+ token_hash: record.tokenHash,
1224
+ token_type: record.tokenType,
1225
+ client_id: record.clientId,
1226
+ subject: record.subject,
1227
+ scopes: JSON.stringify(record.scopes),
1228
+ issued_at: Number(record.issuedAt),
1229
+ expires_at: Number(record.expiresAt),
1230
+ issuer: record.issuer ?? null,
1231
+ audience: jsonEncode(record.audience),
1232
+ jwt_id: record.jwtId ?? null,
1233
+ revoked_at: record.revokedAt === undefined ? null : Number(record.revokedAt),
1234
+ revocation_reason: record.revocationReason ?? null,
1235
+ rotated_at: record.rotatedAt === undefined ? null : Number(record.rotatedAt),
1236
+ replaced_by_token_hash: record.replacedByTokenHash ?? null,
1237
+ metadata: jsonEncode(record.metadata),
1238
+ });
1239
+ const oauthProviderModeTokenRevoke = (input, row) => ({
1240
+ revoked_at: Number(row.revokedAt ?? input.revokedAt),
1241
+ revocation_reason: row.revocationReason ?? input.reason ?? null,
1242
+ });
1243
+ const oauthProviderModeTokenRotate = (input) => ({
1244
+ rotated_at: Number(input.rotatedAt),
1245
+ replaced_by_token_hash: input.replacedByTokenHash,
1246
+ });
1247
+ const domainVerificationValues = (record) => ({
1248
+ id: record.id,
1249
+ owner_id: record.ownerId,
1250
+ domain: record.domain,
1251
+ proof_method: record.proofMethod,
1252
+ proof_token: record.proofToken,
1253
+ status: record.status,
1254
+ created_at: Number(record.createdAt),
1255
+ expires_at: Number(record.expiresAt),
1256
+ verified_at: record.verifiedAt === undefined ? null : Number(record.verifiedAt),
1257
+ revoked_at: record.revokedAt === undefined ? null : Number(record.revokedAt),
1258
+ last_checked_at: record.lastCheckedAt === undefined ? null : Number(record.lastCheckedAt),
1259
+ metadata: jsonEncode(record.metadata),
1260
+ });
1261
+ const domainVerificationMarkVerified = (input) => omitUndefined({
1262
+ status: "verified",
1263
+ verified_at: Number(input.verifiedAt),
1264
+ last_checked_at: Number(input.verifiedAt),
1265
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
1266
+ });
1267
+ const domainVerificationRevoke = (input, row) => ({
1268
+ status: "revoked",
1269
+ revoked_at: Number(input.revokedAt),
1270
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
1271
+ });
828
1272
  const securityTimelineInsert = (event) => ({
829
1273
  id: event.id,
830
1274
  user_id: event.userId,
@@ -837,17 +1281,59 @@ const securityTimelineInsert = (event) => ({
837
1281
  request: jsonEncode(event.request),
838
1282
  metadata: jsonEncode(event.metadata),
839
1283
  });
1284
+ const webhookOutboxInsert = (record) => ({
1285
+ id: record.id,
1286
+ endpoint_key: record.endpointKey,
1287
+ event: jsonEncode(record.event),
1288
+ status: record.status,
1289
+ attempts: record.attempts,
1290
+ next_attempt_at: Number(record.nextAttemptAt),
1291
+ created_at: Number(record.createdAt),
1292
+ updated_at: Number(record.updatedAt),
1293
+ delivered_at: record.deliveredAt === undefined ? null : Number(record.deliveredAt),
1294
+ last_error: record.lastError ?? null,
1295
+ });
1296
+ const webhookOutboxMarkDelivered = (input) => ({
1297
+ status: "delivered",
1298
+ delivered_at: Number(input.deliveredAt),
1299
+ updated_at: Number(input.deliveredAt),
1300
+ last_error: null,
1301
+ });
1302
+ const webhookOutboxMarkFailed = (input) => ({
1303
+ status: "failed",
1304
+ attempts: input.attempts,
1305
+ next_attempt_at: Number(input.nextAttemptAt),
1306
+ updated_at: Number(input.failedAt),
1307
+ last_error: input.error ?? null,
1308
+ });
1309
+ const webhookOutboxMarkDeadLettered = (input) => ({
1310
+ status: "dead_lettered",
1311
+ updated_at: Number(input.deadLetteredAt),
1312
+ last_error: input.error ?? null,
1313
+ });
1314
+ const webhookReplayInsert = (input) => ({
1315
+ id: input.id,
1316
+ expires_at: Number(input.expiresAt),
1317
+ created_at: Number(input.now),
1318
+ });
1319
+ const webhookReplayUpdate = (input) => ({
1320
+ expires_at: Number(input.expiresAt),
1321
+ created_at: Number(input.now),
1322
+ });
840
1323
  const jwtRevocationUpdate = (record) => ({
841
1324
  revoked_at: Number(record.revokedAt),
842
1325
  expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
843
1326
  reason: record.reason ?? null,
844
1327
  });
1328
+ const authSqliteTableName = (name, defaultName) => name ?? defaultName;
845
1329
  const makeInternalAuthSqliteTables = (names = {}) => {
846
1330
  const user = makeAuthUserTable(names.user ?? "auth_user");
847
1331
  const credential = makeAuthCredentialTable(names.credential ?? "auth_credential");
848
1332
  const session = makeAuthSessionTable(names.session ?? "auth_session");
849
1333
  const verification = makeAuthVerificationTable(names.verification ?? "auth_verification");
1334
+ const auditLog = makeAuthAuditLogTable(authSqliteTableName(names.auditLog, "auth_audit_log"));
850
1335
  const loginApprovalReview = makeAuthLoginApprovalReviewTable(names.loginApprovalReview ?? "auth_login_approval_review");
1336
+ const loginRiskHistory = makeAuthLoginRiskHistoryTable(names.loginRiskHistory ?? "auth_login_risk_history");
851
1337
  const trustedDevice = makeAuthTrustedDeviceTable(names.trustedDevice ?? "auth_trusted_device");
852
1338
  const passkeyCredential = makeAuthPasskeyCredentialTable(names.passkeyCredential ?? "auth_passkey_credential");
853
1339
  const totpFactor = makeAuthTotpFactorTable(names.totpFactor ?? "auth_totp_factor");
@@ -856,8 +1342,16 @@ const makeInternalAuthSqliteTables = (names = {}) => {
856
1342
  const refreshToken = makeAuthRefreshTokenTable(names.refreshToken ?? "auth_refresh_token");
857
1343
  const jwtRevocation = makeAuthJwtRevocationTable(names.jwtRevocation ?? "auth_jwt_revocation");
858
1344
  const oauthAccount = makeAuthOAuthAccountTable(names.oauthAccount ?? "auth_oauth_account");
1345
+ const oauthClient = makeAuthOAuthClientTable(authSqliteTableName(names.oauthClient, "auth_oauth_client"));
1346
+ const oauthConsent = makeAuthOAuthConsentTable(authSqliteTableName(names.oauthConsent, "auth_oauth_consent"));
1347
+ const oauthAuthorizationCode = makeAuthOAuthAuthorizationCodeTable(authSqliteTableName(names.oauthAuthorizationCode, "auth_oauth_authorization_code"));
1348
+ const oauthClientSecret = makeAuthOAuthClientSecretTable(authSqliteTableName(names.oauthClientSecret, "auth_oauth_client_secret"));
1349
+ const oauthProviderModeToken = makeAuthOAuthProviderModeTokenTable(authSqliteTableName(names.oauthProviderModeToken, "auth_oauth_provider_mode_token"));
859
1350
  const oauthProviderTokenVault = makeAuthOAuthProviderTokenVaultTable(names.oauthProviderTokenVault ?? "auth_oauth_provider_token_vault");
1351
+ const domainVerification = makeAuthDomainVerificationTable(authSqliteTableName(names.domainVerification, "auth_domain_verification"));
860
1352
  const securityTimeline = makeAuthSecurityTimelineTable(names.securityTimeline ?? "auth_security_timeline");
1353
+ const webhookOutbox = makeAuthWebhookOutboxTable(names.webhookOutbox ?? "auth_webhook_outbox");
1354
+ const webhookReplay = makeAuthWebhookReplayTable(names.webhookReplay ?? "auth_webhook_replay");
861
1355
  return {
862
1356
  user: {
863
1357
  table: user,
@@ -926,6 +1420,21 @@ const makeInternalAuthSqliteTables = (names = {}) => {
926
1420
  insert: verificationInsert,
927
1421
  consume: verificationConsumeUpdate,
928
1422
  },
1423
+ auditLog: {
1424
+ table: auditLog,
1425
+ select: {
1426
+ id: auditLog.id,
1427
+ type: auditLog.type,
1428
+ userId: auditLog.user_id,
1429
+ actorUserId: auditLog.actor_user_id,
1430
+ occurredAt: auditLog.occurred_at,
1431
+ requestIpHash: auditLog.request_ip_hash,
1432
+ requestUserAgentHash: auditLog.request_user_agent_hash,
1433
+ event: auditLog.event,
1434
+ createdAt: auditLog.created_at,
1435
+ },
1436
+ insert: auditLogInsert,
1437
+ },
929
1438
  loginApprovalReview: {
930
1439
  table: loginApprovalReview,
931
1440
  select: {
@@ -950,6 +1459,28 @@ const makeInternalAuthSqliteTables = (names = {}) => {
950
1459
  approve: loginApprovalReviewApproveUpdate,
951
1460
  deny: loginApprovalReviewDenyUpdate,
952
1461
  },
1462
+ loginRiskHistory: {
1463
+ table: loginRiskHistory,
1464
+ select: {
1465
+ id: loginRiskHistory.id,
1466
+ userId: loginRiskHistory.user_id,
1467
+ occurredAt: loginRiskHistory.occurred_at,
1468
+ outcome: loginRiskHistory.outcome,
1469
+ method: loginRiskHistory.method,
1470
+ amr: loginRiskHistory.amr,
1471
+ aal: loginRiskHistory.aal,
1472
+ deviceStatus: loginRiskHistory.device_status,
1473
+ deviceKey: loginRiskHistory.device_key,
1474
+ locationKey: loginRiskHistory.location_key,
1475
+ country: loginRiskHistory.country,
1476
+ region: loginRiskHistory.region,
1477
+ latitudeMicro: loginRiskHistory.latitude_micro,
1478
+ longitudeMicro: loginRiskHistory.longitude_micro,
1479
+ riskLevel: loginRiskHistory.risk_level,
1480
+ createdAt: loginRiskHistory.created_at,
1481
+ },
1482
+ insert: loginRiskHistoryInsert,
1483
+ },
953
1484
  trustedDevice: {
954
1485
  table: trustedDevice,
955
1486
  select: {
@@ -1085,6 +1616,101 @@ const makeInternalAuthSqliteTables = (names = {}) => {
1085
1616
  insert: oauthAccountInsert,
1086
1617
  unlink: oauthAccountUnlink,
1087
1618
  },
1619
+ oauthClient: {
1620
+ table: oauthClient,
1621
+ select: {
1622
+ id: oauthClient.id,
1623
+ type: oauthClient.type,
1624
+ status: oauthClient.status,
1625
+ name: oauthClient.name,
1626
+ redirectUris: oauthClient.redirect_uris,
1627
+ allowedGrantTypes: oauthClient.allowed_grant_types,
1628
+ allowedResponseTypes: oauthClient.allowed_response_types,
1629
+ allowedScopes: oauthClient.allowed_scopes,
1630
+ createdAt: oauthClient.created_at,
1631
+ updatedAt: oauthClient.updated_at,
1632
+ metadata: oauthClient.metadata,
1633
+ },
1634
+ insert: oauthClientValues,
1635
+ update: oauthClientValues,
1636
+ },
1637
+ oauthConsent: {
1638
+ table: oauthConsent,
1639
+ select: {
1640
+ id: oauthConsent.id,
1641
+ userId: oauthConsent.user_id,
1642
+ clientId: oauthConsent.client_id,
1643
+ scopes: oauthConsent.scopes,
1644
+ grantedAt: oauthConsent.granted_at,
1645
+ expiresAt: oauthConsent.expires_at,
1646
+ revokedAt: oauthConsent.revoked_at,
1647
+ metadata: oauthConsent.metadata,
1648
+ },
1649
+ insert: oauthConsentValues,
1650
+ update: oauthConsentValues,
1651
+ revoke: oauthConsentRevoke,
1652
+ },
1653
+ oauthAuthorizationCode: {
1654
+ table: oauthAuthorizationCode,
1655
+ select: {
1656
+ codeHash: oauthAuthorizationCode.code_hash,
1657
+ clientId: oauthAuthorizationCode.client_id,
1658
+ subject: oauthAuthorizationCode.subject,
1659
+ redirectUri: oauthAuthorizationCode.redirect_uri,
1660
+ scopes: oauthAuthorizationCode.scopes,
1661
+ codeChallenge: oauthAuthorizationCode.code_challenge,
1662
+ codeChallengeMethod: oauthAuthorizationCode.code_challenge_method,
1663
+ issuedAt: oauthAuthorizationCode.issued_at,
1664
+ expiresAt: oauthAuthorizationCode.expires_at,
1665
+ consumedAt: oauthAuthorizationCode.consumed_at,
1666
+ metadata: oauthAuthorizationCode.metadata,
1667
+ },
1668
+ insert: oauthAuthorizationCodeValues,
1669
+ update: oauthAuthorizationCodeValues,
1670
+ markConsumed: oauthAuthorizationCodeMarkConsumed,
1671
+ },
1672
+ oauthClientSecret: {
1673
+ table: oauthClientSecret,
1674
+ select: {
1675
+ prefix: oauthClientSecret.prefix,
1676
+ clientId: oauthClientSecret.client_id,
1677
+ secretHash: oauthClientSecret.secret_hash,
1678
+ authenticationMethods: oauthClientSecret.authentication_methods,
1679
+ createdAt: oauthClientSecret.created_at,
1680
+ expiresAt: oauthClientSecret.expires_at,
1681
+ lastUsedAt: oauthClientSecret.last_used_at,
1682
+ revokedAt: oauthClientSecret.revoked_at,
1683
+ metadata: oauthClientSecret.metadata,
1684
+ },
1685
+ insert: oauthClientSecretValues,
1686
+ update: oauthClientSecretValues,
1687
+ markUsed: oauthClientSecretMarkUsed,
1688
+ revoke: oauthClientSecretRevoke,
1689
+ },
1690
+ oauthProviderModeToken: {
1691
+ table: oauthProviderModeToken,
1692
+ select: {
1693
+ tokenHash: oauthProviderModeToken.token_hash,
1694
+ tokenType: oauthProviderModeToken.token_type,
1695
+ clientId: oauthProviderModeToken.client_id,
1696
+ subject: oauthProviderModeToken.subject,
1697
+ scopes: oauthProviderModeToken.scopes,
1698
+ issuedAt: oauthProviderModeToken.issued_at,
1699
+ expiresAt: oauthProviderModeToken.expires_at,
1700
+ issuer: oauthProviderModeToken.issuer,
1701
+ audience: oauthProviderModeToken.audience,
1702
+ jwtId: oauthProviderModeToken.jwt_id,
1703
+ revokedAt: oauthProviderModeToken.revoked_at,
1704
+ revocationReason: oauthProviderModeToken.revocation_reason,
1705
+ rotatedAt: oauthProviderModeToken.rotated_at,
1706
+ replacedByTokenHash: oauthProviderModeToken.replaced_by_token_hash,
1707
+ metadata: oauthProviderModeToken.metadata,
1708
+ },
1709
+ insert: oauthProviderModeTokenValues,
1710
+ update: oauthProviderModeTokenValues,
1711
+ revoke: oauthProviderModeTokenRevoke,
1712
+ rotate: oauthProviderModeTokenRotate,
1713
+ },
1088
1714
  oauthProviderTokenVault: {
1089
1715
  table: oauthProviderTokenVault,
1090
1716
  select: {
@@ -1106,6 +1732,27 @@ const makeInternalAuthSqliteTables = (names = {}) => {
1106
1732
  update: oauthProviderTokenVaultValues,
1107
1733
  markRevoked: oauthProviderTokenVaultMarkRevoked,
1108
1734
  },
1735
+ domainVerification: {
1736
+ table: domainVerification,
1737
+ select: {
1738
+ id: domainVerification.id,
1739
+ ownerId: domainVerification.owner_id,
1740
+ domain: domainVerification.domain,
1741
+ proofMethod: domainVerification.proof_method,
1742
+ proofToken: domainVerification.proof_token,
1743
+ status: domainVerification.status,
1744
+ createdAt: domainVerification.created_at,
1745
+ expiresAt: domainVerification.expires_at,
1746
+ verifiedAt: domainVerification.verified_at,
1747
+ revokedAt: domainVerification.revoked_at,
1748
+ lastCheckedAt: domainVerification.last_checked_at,
1749
+ metadata: domainVerification.metadata,
1750
+ },
1751
+ insert: domainVerificationValues,
1752
+ update: domainVerificationValues,
1753
+ markVerified: domainVerificationMarkVerified,
1754
+ revoke: domainVerificationRevoke,
1755
+ },
1109
1756
  securityTimeline: {
1110
1757
  table: securityTimeline,
1111
1758
  select: {
@@ -1122,6 +1769,35 @@ const makeInternalAuthSqliteTables = (names = {}) => {
1122
1769
  },
1123
1770
  insert: securityTimelineInsert,
1124
1771
  },
1772
+ webhookOutbox: {
1773
+ table: webhookOutbox,
1774
+ select: {
1775
+ id: webhookOutbox.id,
1776
+ endpointKey: webhookOutbox.endpoint_key,
1777
+ event: webhookOutbox.event,
1778
+ status: webhookOutbox.status,
1779
+ attempts: webhookOutbox.attempts,
1780
+ nextAttemptAt: webhookOutbox.next_attempt_at,
1781
+ createdAt: webhookOutbox.created_at,
1782
+ updatedAt: webhookOutbox.updated_at,
1783
+ deliveredAt: webhookOutbox.delivered_at,
1784
+ lastError: webhookOutbox.last_error,
1785
+ },
1786
+ insert: webhookOutboxInsert,
1787
+ markDelivered: webhookOutboxMarkDelivered,
1788
+ markFailed: webhookOutboxMarkFailed,
1789
+ markDeadLettered: webhookOutboxMarkDeadLettered,
1790
+ },
1791
+ webhookReplay: {
1792
+ table: webhookReplay,
1793
+ select: {
1794
+ id: webhookReplay.id,
1795
+ expiresAt: webhookReplay.expires_at,
1796
+ createdAt: webhookReplay.created_at,
1797
+ },
1798
+ insert: webhookReplayInsert,
1799
+ update: webhookReplayUpdate,
1800
+ },
1125
1801
  };
1126
1802
  };
1127
1803
  const asAuthSqliteTables = (tables) => tables;
@@ -1132,7 +1808,9 @@ const selectUserColumns = (tables) => tables.user.select;
1132
1808
  const selectCredentialColumns = (tables) => tables.credential.select;
1133
1809
  const selectSessionColumns = (tables) => tables.session.select;
1134
1810
  const selectVerificationColumns = (tables) => tables.verification.select;
1811
+ const selectAuditLogColumns = (tables) => tables.auditLog.select;
1135
1812
  const selectLoginApprovalReviewColumns = (tables) => tables.loginApprovalReview.select;
1813
+ const selectLoginRiskHistoryColumns = (tables) => tables.loginRiskHistory.select;
1136
1814
  const selectTrustedDeviceColumns = (tables) => tables.trustedDevice.select;
1137
1815
  const selectPasskeyCredentialColumns = (tables) => tables.passkeyCredential.select;
1138
1816
  const selectTotpFactorColumns = (tables) => tables.totpFactor.select;
@@ -1141,13 +1819,31 @@ const selectApiKeyColumns = (tables) => tables.apiKey.select;
1141
1819
  const selectRefreshTokenColumns = (tables) => tables.refreshToken.select;
1142
1820
  const selectJwtRevocationColumns = (tables) => tables.jwtRevocation.select;
1143
1821
  const selectOAuthAccountColumns = (tables) => tables.oauthAccount.select;
1822
+ const selectOAuthClientColumns = (tables) => tables.oauthClient.select;
1823
+ const selectOAuthConsentColumns = (tables) => tables.oauthConsent.select;
1824
+ const selectOAuthAuthorizationCodeColumns = (tables) => tables.oauthAuthorizationCode.select;
1825
+ const selectOAuthClientSecretColumns = (tables) => tables.oauthClientSecret.select;
1826
+ const selectOAuthProviderModeTokenColumns = (tables) => tables.oauthProviderModeToken.select;
1144
1827
  const selectOAuthProviderTokenVaultColumns = (tables) => tables.oauthProviderTokenVault.select;
1828
+ const selectDomainVerificationColumns = (tables) => tables.domainVerification.select;
1145
1829
  const selectSecurityTimelineColumns = (tables) => tables.securityTimeline.select;
1830
+ const selectWebhookOutboxColumns = (tables) => tables.webhookOutbox.select;
1831
+ const selectWebhookReplayColumns = (tables) => tables.webhookReplay.select;
1146
1832
  const storageError = (entity, operation, cause) => StorageError.fromUnknown(entity, operation, cause);
1833
+ const auditLogStoreError = (operation, cause) => new AuditLogStoreError({
1834
+ operation,
1835
+ message: `Audit log ${operation} failed`,
1836
+ cause,
1837
+ });
1147
1838
  const loginApprovalReviewStoreError = (operation, cause) => new LoginApprovalReviewError({
1148
1839
  message: `Login approval review ${operation} failed`,
1149
1840
  cause,
1150
1841
  });
1842
+ const loginRiskHistoryStoreError = (operation, cause) => new LoginRiskHistoryError({
1843
+ operation,
1844
+ message: `Login risk history ${operation} failed`,
1845
+ cause,
1846
+ });
1151
1847
  const trustedDeviceStoreError = (operation, cause) => new TrustedDeviceError({
1152
1848
  operation,
1153
1849
  message: `Trusted device ${operation} failed`,
@@ -1188,16 +1884,56 @@ const oauthAccountStoreError = (operation, cause) => new OAuthAccountStoreError(
1188
1884
  message: `OAuth account ${operation} failed`,
1189
1885
  cause,
1190
1886
  });
1887
+ const oauthClientStoreError = (operation, cause) => new OAuthClientStoreError({
1888
+ operation,
1889
+ message: `OAuth client ${operation} failed`,
1890
+ cause,
1891
+ });
1892
+ const oauthConsentStoreError = (operation, cause) => new OAuthConsentStoreError({
1893
+ operation,
1894
+ message: `OAuth consent ${operation} failed`,
1895
+ cause,
1896
+ });
1897
+ const oauthAuthorizationCodeStoreError = (operation, cause) => new OAuthAuthorizationCodeStoreError({
1898
+ operation,
1899
+ message: `OAuth authorization code ${operation} failed`,
1900
+ cause,
1901
+ });
1902
+ const oauthClientSecretStoreError = (operation, cause) => new OAuthClientSecretStoreError({
1903
+ operation,
1904
+ message: `OAuth client secret ${operation} failed`,
1905
+ cause,
1906
+ });
1907
+ const oauthProviderModeTokenStoreError = (operation, cause) => new OAuthProviderModeTokenStoreError({
1908
+ operation,
1909
+ message: `OAuth provider-mode token ${operation} failed`,
1910
+ cause,
1911
+ });
1191
1912
  const oauthProviderTokenVaultError = (operation, cause) => new OAuthProviderTokenVaultError({
1192
1913
  operation,
1193
1914
  message: `OAuth provider token vault ${operation} failed`,
1194
1915
  cause,
1195
1916
  });
1917
+ const domainVerificationStoreError = (operation, cause) => new DomainVerificationStoreError({
1918
+ operation,
1919
+ message: `Domain verification ${operation} failed`,
1920
+ cause,
1921
+ });
1196
1922
  const securityTimelineStoreError = (operation, cause) => new SecurityTimelineStoreError({
1197
1923
  operation,
1198
1924
  message: `Security timeline ${operation} failed`,
1199
1925
  cause,
1200
1926
  });
1927
+ const webhookOutboxStoreError = (operation, cause) => new WebhookOutboxStoreError({
1928
+ operation,
1929
+ message: `Webhook outbox ${operation} failed`,
1930
+ cause,
1931
+ });
1932
+ const webhookReplayStoreError = (operation, cause) => new WebhookReplayStoreError({
1933
+ operation,
1934
+ message: `Webhook replay ${operation} failed`,
1935
+ cause,
1936
+ });
1201
1937
  const findUserById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectUserColumns(tables)).pipe(Query.from(tables.user.table), Query.where(queryEq(tables.user.select.id, id)))).pipe(Effect.mapError((cause) => storageError("user", operation, cause)), Effect.map((rows) => rows[0] === undefined
1202
1938
  ? Option.none()
1203
1939
  : Option.some(decodeUserRow(tables, rows[0]))));
@@ -1260,6 +1996,44 @@ const findOAuthAccountByProviderAccount = (executor, tables, input, operation =
1260
1996
  ? Option.none()
1261
1997
  : Option.some(decodeOAuthAccountRow(tables, rows[0]))));
1262
1998
  };
1999
+ const findOAuthClientById = (executor, tables, input, operation = "find") => executePlan(executor, Query.select(selectOAuthClientColumns(tables)).pipe(Query.from(tables.oauthClient.table), Query.where(queryEq(tables.oauthClient.select.id, input.clientId)))).pipe(Effect.mapError((cause) => oauthClientStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2000
+ ? Option.none()
2001
+ : Option.some(decodeOAuthClientRow(tables, rows[0]))));
2002
+ const findOAuthConsentByUserAndClient = (executor, tables, input, operation = "find-active") => executePlan(executor, Query.select(selectOAuthConsentColumns(tables)).pipe(Query.from(tables.oauthConsent.table), Query.where(queryEq(tables.oauthConsent.select.userId, input.userId)), Query.where(queryEq(tables.oauthConsent.select.clientId, input.clientId)))).pipe(Effect.mapError((cause) => oauthConsentStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2003
+ ? Option.none()
2004
+ : Option.some(decodeOAuthConsentRow(tables, rows[0]))));
2005
+ const deleteOAuthConsentRowsBefore = (executor, tables, column, before) => executePlan(executor, Query.delete(tables.oauthConsent.table).pipe(Query.where(queryLt(column, Number(before))), Query.returning({ id: tables.oauthConsent.select.id }))).pipe(Effect.map((rows) => rows.length));
2006
+ const deleteOAuthInactiveConsents = (executor, tables, input) => Effect.gen(function* () {
2007
+ const expiredDeleted = input.beforeExpiresAt === undefined
2008
+ ? 0
2009
+ : yield* deleteOAuthConsentRowsBefore(executor, tables, tables.oauthConsent.select.expiresAt, input.beforeExpiresAt);
2010
+ const revokedDeleted = input.beforeRevokedAt === undefined
2011
+ ? 0
2012
+ : yield* deleteOAuthConsentRowsBefore(executor, tables, tables.oauthConsent.select.revokedAt, input.beforeRevokedAt);
2013
+ return expiredDeleted + revokedDeleted;
2014
+ }).pipe(Effect.mapError((cause) => oauthConsentStoreError("deleteInactive", cause)));
2015
+ const findOAuthAuthorizationCodeByHash = (executor, tables, input, operation = "find") => executePlan(executor, Query.select(selectOAuthAuthorizationCodeColumns(tables)).pipe(Query.from(tables.oauthAuthorizationCode.table), Query.where(queryEq(tables.oauthAuthorizationCode.select.codeHash, input.codeHash)))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2016
+ ? Option.none()
2017
+ : Option.some(decodeOAuthAuthorizationCodeRow(tables, rows[0]))));
2018
+ const findOAuthClientSecretByClientIdAndPrefix = (executor, tables, input, operation = "find") => executePlan(executor, Query.select(selectOAuthClientSecretColumns(tables)).pipe(Query.from(tables.oauthClientSecret.table), Query.where(queryEq(tables.oauthClientSecret.select.clientId, input.clientId)), Query.where(queryEq(tables.oauthClientSecret.select.prefix, input.prefix)))).pipe(Effect.mapError((cause) => oauthClientSecretStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2019
+ ? Option.none()
2020
+ : Option.some(decodeOAuthClientSecretRow(tables, rows[0]))));
2021
+ const findOAuthProviderModeTokenByHash = (executor, tables, input, operation = "find") => executePlan(executor, Query.select(selectOAuthProviderModeTokenColumns(tables)).pipe(Query.from(tables.oauthProviderModeToken.table), Query.where(queryEq(tables.oauthProviderModeToken.select.tokenHash, input.tokenHash)))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2022
+ ? Option.none()
2023
+ : Option.some(decodeOAuthProviderModeTokenRow(tables, rows[0]))));
2024
+ const deleteOAuthProviderModeTokenRowsBefore = (executor, tables, column, before) => executePlan(executor, Query.delete(tables.oauthProviderModeToken.table).pipe(Query.where(queryLt(column, Number(before))), Query.returning({ tokenHash: tables.oauthProviderModeToken.select.tokenHash }))).pipe(Effect.map((rows) => rows.length));
2025
+ const deleteOAuthProviderModeRetiredTokens = (executor, tables, input) => Effect.gen(function* () {
2026
+ const expiredDeleted = input.beforeExpiresAt === undefined
2027
+ ? 0
2028
+ : yield* deleteOAuthProviderModeTokenRowsBefore(executor, tables, tables.oauthProviderModeToken.select.expiresAt, input.beforeExpiresAt);
2029
+ const revokedDeleted = input.beforeRevokedAt === undefined
2030
+ ? 0
2031
+ : yield* deleteOAuthProviderModeTokenRowsBefore(executor, tables, tables.oauthProviderModeToken.select.revokedAt, input.beforeRevokedAt);
2032
+ const rotatedDeleted = input.beforeRotatedAt === undefined
2033
+ ? 0
2034
+ : yield* deleteOAuthProviderModeTokenRowsBefore(executor, tables, tables.oauthProviderModeToken.select.rotatedAt, input.beforeRotatedAt);
2035
+ return expiredDeleted + revokedDeleted + rotatedDeleted;
2036
+ }).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError("deleteRetired", cause)));
1263
2037
  const findOAuthProviderTokenVaultRecordByAccountId = (executor, tables, input, operation, includeRevoked = false) => {
1264
2038
  const base = Query.select(selectOAuthProviderTokenVaultColumns(tables)).pipe(Query.from(tables.oauthProviderTokenVault.table), Query.where(queryEq(tables.oauthProviderTokenVault.select.accountId, input.accountId)));
1265
2039
  const query = includeRevoked
@@ -1269,7 +2043,175 @@ const findOAuthProviderTokenVaultRecordByAccountId = (executor, tables, input, o
1269
2043
  ? Option.none()
1270
2044
  : Option.some(decodeOAuthProviderTokenVaultRow(tables, rows[0]))));
1271
2045
  };
2046
+ const findDomainVerificationById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectDomainVerificationColumns(tables)).pipe(Query.from(tables.domainVerification.table), Query.where(queryEq(tables.domainVerification.select.id, id)))).pipe(Effect.mapError((cause) => domainVerificationStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2047
+ ? Option.none()
2048
+ : Option.some(decodeDomainVerificationRecord(tables, rows[0]))));
2049
+ const findDomainVerificationByOwnerAndDomain = (executor, tables, input, operation = "find") => executePlan(executor, Query.select(selectDomainVerificationColumns(tables)).pipe(Query.from(tables.domainVerification.table), Query.where(queryEq(tables.domainVerification.select.ownerId, input.ownerId)), Query.where(queryEq(tables.domainVerification.select.domain, input.domain)))).pipe(Effect.mapError((cause) => domainVerificationStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2050
+ ? Option.none()
2051
+ : Option.some(decodeDomainVerificationRecord(tables, rows[0]))));
1272
2052
  const listSecurityTimelineByUser = (executor, tables, input, operation = "list") => executePlan(executor, Query.select(selectSecurityTimelineColumns(tables)).pipe(Query.from(tables.securityTimeline.table), Query.where(queryEq(tables.securityTimeline.select.userId, input.userId)))).pipe(Effect.mapError((cause) => securityTimelineStoreError(operation, cause)), Effect.map((rows) => filterSecurityTimelineEvents(rows.map((row) => decodeSecurityTimelineEvent(tables, row)), input)));
2053
+ const deleteSecurityTimelineBefore = (executor, tables, input) => {
2054
+ const base = Query.delete(tables.securityTimeline.table).pipe(Query.where(queryLt(tables.securityTimeline.select.occurredAt, Number(input.beforeOccurredAt))));
2055
+ const query = input.userId === undefined
2056
+ ? base
2057
+ : base.pipe(Query.where(queryEq(tables.securityTimeline.select.userId, input.userId)));
2058
+ return executePlan(executor, query.pipe(Query.returning({ id: tables.securityTimeline.select.id }))).pipe(Effect.mapError((cause) => securityTimelineStoreError("deleteBefore", cause)), Effect.map((rows) => rows.length));
2059
+ };
2060
+ const findWebhookOutboxRecordById = (executor, tables, id, operation) => executePlan(executor, Query.select(selectWebhookOutboxColumns(tables)).pipe(Query.from(tables.webhookOutbox.table), Query.where(queryEq(tables.webhookOutbox.select.id, id)))).pipe(Effect.mapError((cause) => webhookOutboxStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
2061
+ ? Option.none()
2062
+ : Option.some(decodeWebhookOutboxRecord(tables, rows[0]))));
2063
+ const claimDueWebhookOutboxRecords = (executor, tables, input) => Effect.gen(function* () {
2064
+ const now = input.now ?? (yield* currentUnixMillis);
2065
+ const base = Query.select(selectWebhookOutboxColumns(tables)).pipe(Query.from(tables.webhookOutbox.table), Query.where(queryLte(tables.webhookOutbox.select.nextAttemptAt, Number(now))));
2066
+ const query = input.endpointKey === undefined
2067
+ ? base
2068
+ : base.pipe(Query.where(queryEq(tables.webhookOutbox.select.endpointKey, input.endpointKey)));
2069
+ const rows = yield* executePlan(executor, query).pipe(Effect.mapError((cause) => webhookOutboxStoreError("claimDue", cause)));
2070
+ return filterDueWebhookOutboxRecords(rows.map((row) => decodeWebhookOutboxRecord(tables, row)), { ...input, now });
2071
+ });
2072
+ const deleteWebhookOutboxRecordsBefore = (executor, tables, input) => {
2073
+ const [firstStatus, ...remainingStatuses] = input.statuses;
2074
+ const base = Query.delete(tables.webhookOutbox.table).pipe(Query.where(queryLt(tables.webhookOutbox.select.updatedAt, Number(input.beforeUpdatedAt))), Query.where(queryIn(tables.webhookOutbox.select.status, firstStatus, ...remainingStatuses)));
2075
+ const query = input.endpointKey === undefined
2076
+ ? base
2077
+ : base.pipe(Query.where(queryEq(tables.webhookOutbox.select.endpointKey, input.endpointKey)));
2078
+ return executePlan(executor, query.pipe(Query.returning({ id: tables.webhookOutbox.select.id }))).pipe(Effect.mapError((cause) => webhookOutboxStoreError("deleteBefore", cause)), Effect.map((rows) => rows.length));
2079
+ };
2080
+ const deleteExpiredWebhookReplayRecords = (executor, tables, input) => Effect.gen(function* () {
2081
+ const now = input.now ?? (yield* currentUnixMillis);
2082
+ return yield* executePlan(executor, Query.delete(tables.webhookReplay.table).pipe(Query.where(queryLt(tables.webhookReplay.select.expiresAt, Number(now))), Query.returning({ id: tables.webhookReplay.select.id }))).pipe(Effect.mapError((cause) => webhookReplayStoreError("deleteExpired", cause)), Effect.map((rows) => rows.length));
2083
+ });
2084
+ const listAuditLogRecords = (executor, tables, input = {}) => {
2085
+ if (input.types !== undefined && input.types.length === 0) {
2086
+ return Effect.succeed([]);
2087
+ }
2088
+ const base = Query.select(selectAuditLogColumns(tables)).pipe(Query.from(tables.auditLog.table));
2089
+ const byUser = input.userId === undefined
2090
+ ? base
2091
+ : base.pipe(Query.where(queryEq(tables.auditLog.select.userId, input.userId)));
2092
+ const byOccurredAt = input.beforeOccurredAt === undefined
2093
+ ? byUser
2094
+ : byUser.pipe(Query.where(queryLt(tables.auditLog.select.occurredAt, Number(input.beforeOccurredAt))));
2095
+ const query = input.types === undefined
2096
+ ? byOccurredAt
2097
+ : byOccurredAt.pipe(Query.where(queryIn(tables.auditLog.select.type, input.types[0], ...input.types.slice(1))));
2098
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => auditLogStoreError("list", cause)), Effect.map((rows) => filterAuditLogEvents(rows.map((row) => decodeAuditLogEvent(tables, row)), input)));
2099
+ };
2100
+ const deleteAuditLogRecordsBefore = (executor, tables, input) => {
2101
+ const base = Query.delete(tables.auditLog.table).pipe(Query.where(queryLt(tables.auditLog.select.occurredAt, Number(input.beforeOccurredAt))));
2102
+ const query = input.userId === undefined
2103
+ ? base
2104
+ : base.pipe(Query.where(queryEq(tables.auditLog.select.userId, input.userId)));
2105
+ return executePlan(executor, query.pipe(Query.returning({ occurredAt: tables.auditLog.select.occurredAt }))).pipe(Effect.mapError((cause) => auditLogStoreError("deleteBefore", cause)), Effect.map((rows) => rows.length));
2106
+ };
2107
+ const defaultLoginRiskHistoryRecentWindowMillis = 15 * 60 * 1000;
2108
+ const defaultLoginRiskHistoryWindowMillis = 90 * 24 * 60 * 60 * 1000;
2109
+ const defaultLoginRiskImpossibleTravelWindowMillis = 24 * 60 * 60 * 1000;
2110
+ const defaultLoginRiskImpossibleTravelKmh = 900;
2111
+ const positiveWindowMillis = (value, defaultValue) => value === undefined || !Number.isFinite(value)
2112
+ ? defaultValue
2113
+ : Math.max(0, value);
2114
+ const loginRiskHistoryOptions = (options) => ({
2115
+ recentWindowMillis: positiveWindowMillis(options?.recentWindowMillis, defaultLoginRiskHistoryRecentWindowMillis),
2116
+ historyWindowMillis: positiveWindowMillis(options?.historyWindowMillis, defaultLoginRiskHistoryWindowMillis),
2117
+ impossibleTravelWindowMillis: positiveWindowMillis(options?.impossibleTravelWindowMillis, defaultLoginRiskImpossibleTravelWindowMillis),
2118
+ impossibleTravelKmh: options?.impossibleTravelKmh === undefined ||
2119
+ !Number.isFinite(options.impossibleTravelKmh)
2120
+ ? defaultLoginRiskImpossibleTravelKmh
2121
+ : Math.max(0, options.impossibleTravelKmh),
2122
+ });
2123
+ const toRadians = (degrees) => (degrees * Math.PI) / 180;
2124
+ const distanceKm = (first, second) => {
2125
+ const earthRadiusKm = 6371;
2126
+ const latitudeDelta = toRadians(second.latitude - first.latitude);
2127
+ const longitudeDelta = toRadians(second.longitude - first.longitude);
2128
+ const firstLatitude = toRadians(first.latitude);
2129
+ const secondLatitude = toRadians(second.latitude);
2130
+ const haversine = Math.sin(latitudeDelta / 2) ** 2 +
2131
+ Math.cos(firstLatitude) *
2132
+ Math.cos(secondLatitude) *
2133
+ Math.sin(longitudeDelta / 2) ** 2;
2134
+ return (2 * earthRadiusKm * Math.atan2(Math.sqrt(haversine), Math.sqrt(1 - haversine)));
2135
+ };
2136
+ const hasCoordinates = (value) => value.latitude !== undefined &&
2137
+ Number.isFinite(value.latitude) &&
2138
+ value.longitude !== undefined &&
2139
+ Number.isFinite(value.longitude);
2140
+ const loginRiskHistoryLookupResult = (input, rows, options) => {
2141
+ const normalized = loginRiskHistoryOptions(options);
2142
+ const occurredAt = Number(input.occurredAt);
2143
+ const recentStart = occurredAt - normalized.recentWindowMillis;
2144
+ const historyStart = occurredAt - normalized.historyWindowMillis;
2145
+ const impossibleTravelStart = occurredAt - normalized.impossibleTravelWindowMillis;
2146
+ const previousRows = rows.filter((row) => Number(row.occurredAt) < occurredAt);
2147
+ const recentRows = previousRows.filter((row) => Number(row.occurredAt) >= recentStart);
2148
+ const historyRows = previousRows.filter((row) => Number(row.occurredAt) >= historyStart);
2149
+ const recentFailedLoginCount = recentRows.filter((row) => row.outcome === "failure").length;
2150
+ const recentSuccessfulLoginCount = recentRows.filter((row) => row.outcome === "success").length;
2151
+ const knownDevice = input.deviceKey === undefined
2152
+ ? undefined
2153
+ : historyRows.some((row) => row.deviceKey === input.deviceKey);
2154
+ const knownLocation = input.locationKey === undefined
2155
+ ? undefined
2156
+ : historyRows.some((row) => row.locationKey === input.locationKey);
2157
+ const currentLocation = {
2158
+ latitude: input.request?.latitude,
2159
+ longitude: input.request?.longitude,
2160
+ };
2161
+ const impossibleTravel = hasCoordinates(currentLocation)
2162
+ ? previousRows
2163
+ .filter((row) => row.outcome === "success" &&
2164
+ Number(row.occurredAt) >= impossibleTravelStart)
2165
+ .some((row) => {
2166
+ const previousLocation = {
2167
+ latitude: row.latitude,
2168
+ longitude: row.longitude,
2169
+ };
2170
+ if (!hasCoordinates(previousLocation)) {
2171
+ return false;
2172
+ }
2173
+ const elapsedHours = (occurredAt - Number(row.occurredAt)) / 3_600_000;
2174
+ return (elapsedHours > 0 &&
2175
+ distanceKm(previousLocation, currentLocation) / elapsedHours >
2176
+ normalized.impossibleTravelKmh);
2177
+ })
2178
+ : undefined;
2179
+ const recentRiskLevels = recentRows.flatMap((row) => row.riskLevel === undefined ? [] : [row.riskLevel]);
2180
+ const riskLevel = recentRiskLevels.length === 0
2181
+ ? undefined
2182
+ : maxLoginRiskLevel(recentRiskLevels, "unknown");
2183
+ const reasons = [
2184
+ ...(recentFailedLoginCount > 0 ? ["recent-failed-login"] : []),
2185
+ ...(knownDevice === false ? ["new-device"] : []),
2186
+ ...(knownLocation === false ? ["new-location"] : []),
2187
+ ...(impossibleTravel === true ? ["impossible-travel"] : []),
2188
+ ];
2189
+ return omitUndefined({
2190
+ recentLoginCount: recentRows.length,
2191
+ recentFailedLoginCount,
2192
+ recentSuccessfulLoginCount,
2193
+ knownDevice,
2194
+ newDevice: knownDevice === undefined ? undefined : !knownDevice,
2195
+ knownLocation,
2196
+ newLocation: knownLocation === undefined ? undefined : !knownLocation,
2197
+ impossibleTravel,
2198
+ riskLevel,
2199
+ reasons: reasons.length === 0 ? undefined : reasons,
2200
+ });
2201
+ };
2202
+ const lookupLoginRiskHistory = (executor, tables, input, options) => {
2203
+ const normalized = loginRiskHistoryOptions(options);
2204
+ const occurredAt = Number(input.occurredAt);
2205
+ const earliestOccurredAt = Math.min(occurredAt - normalized.recentWindowMillis, occurredAt - normalized.historyWindowMillis, occurredAt - normalized.impossibleTravelWindowMillis);
2206
+ return executePlan(executor, Query.select(selectLoginRiskHistoryColumns(tables)).pipe(Query.from(tables.loginRiskHistory.table), Query.where(queryEq(tables.loginRiskHistory.select.userId, input.userId)), Query.where(queryLt(tables.loginRiskHistory.select.occurredAt, occurredAt)), Query.where(queryGt(tables.loginRiskHistory.select.occurredAt, earliestOccurredAt - 1)))).pipe(Effect.mapError((cause) => loginRiskHistoryStoreError("lookup", cause)), Effect.map((rows) => loginRiskHistoryLookupResult(input, rows.map((row) => decodeLoginRiskHistoryRecord(tables, row)), options)));
2207
+ };
2208
+ const deleteLoginRiskHistoryRecordsBefore = (executor, tables, input) => {
2209
+ const base = Query.delete(tables.loginRiskHistory.table).pipe(Query.where(queryLt(tables.loginRiskHistory.select.occurredAt, Number(input.beforeOccurredAt))));
2210
+ const query = input.userId === undefined
2211
+ ? base
2212
+ : base.pipe(Query.where(queryEq(tables.loginRiskHistory.select.userId, input.userId)));
2213
+ return executePlan(executor, query.pipe(Query.returning({ id: tables.loginRiskHistory.select.id }))).pipe(Effect.mapError((cause) => loginRiskHistoryStoreError("deleteBefore", cause)), Effect.map((rows) => rows.length));
2214
+ };
1273
2215
  export const makeEffectQbSqliteUserStore = (executor, options = {}) => {
1274
2216
  const tables = options.tables ?? authSqliteTables;
1275
2217
  return UserStore.make({
@@ -1349,6 +2291,17 @@ export const makeEffectQbSqliteVerificationStore = (executor, options = {}) => {
1349
2291
  },
1350
2292
  });
1351
2293
  };
2294
+ export const makeEffectQbSqliteAuditLogStore = (executor, options = {}) => {
2295
+ const tables = options.tables ?? authSqliteTables;
2296
+ return AuditLogStore.make({
2297
+ insert: (event) => Effect.gen(function* () {
2298
+ const createdAt = yield* currentUnixMillis;
2299
+ yield* executePlan(executor, queryInsert(tables.auditLog.table, mutationValues(tables.auditLog.insert(event, createdAt)))).pipe(Effect.mapError((cause) => auditLogStoreError("insert", cause)), Effect.asVoid);
2300
+ }),
2301
+ list: (input = {}) => listAuditLogRecords(executor, tables, input),
2302
+ deleteBefore: (input) => deleteAuditLogRecordsBefore(executor, tables, input),
2303
+ });
2304
+ };
1352
2305
  export const makeEffectQbSqliteLoginApprovalReviewStore = (executor, options = {}) => {
1353
2306
  const tables = options.tables ?? authSqliteTables;
1354
2307
  return LoginApprovalReviewStore.make({
@@ -1362,6 +2315,17 @@ export const makeEffectQbSqliteLoginApprovalReviewStore = (executor, options = {
1362
2315
  : Option.some(decodeLoginApprovalReviewRow(tables, rows[0])))),
1363
2316
  });
1364
2317
  };
2318
+ export const makeEffectQbSqliteLoginRiskHistoryStore = (executor, options = {}) => {
2319
+ const tables = options.tables ?? authSqliteTables;
2320
+ return LoginRiskHistory.make({
2321
+ lookup: (input) => lookupLoginRiskHistory(executor, tables, input, options.loginRiskHistory),
2322
+ record: (input) => Effect.gen(function* () {
2323
+ const createdAt = input.createdAt ?? (yield* currentUnixMillis);
2324
+ yield* executePlan(executor, queryInsert(tables.loginRiskHistory.table, mutationValues(tables.loginRiskHistory.insert({ ...input, createdAt })))).pipe(Effect.mapError((cause) => loginRiskHistoryStoreError("record", cause)), Effect.asVoid);
2325
+ }),
2326
+ deleteBefore: (input) => deleteLoginRiskHistoryRecordsBefore(executor, tables, input),
2327
+ });
2328
+ };
1365
2329
  export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) => {
1366
2330
  const tables = options.tables ?? authSqliteTables;
1367
2331
  return TrustedDeviceStore.make({
@@ -1567,6 +2531,118 @@ export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
1567
2531
  })),
1568
2532
  });
1569
2533
  };
2534
+ export const makeEffectQbSqliteOAuthClientStore = (executor, options = {}) => {
2535
+ const tables = options.tables ?? authSqliteTables;
2536
+ return OAuthClientStore.make({
2537
+ upsert: (record) => executePlan(executor, queryInsert(tables.oauthClient.table, mutationValues(tables.oauthClient.insert(record))).pipe(Query.onConflict("id", {
2538
+ update: mutationValues(tables.oauthClient.update(record)),
2539
+ }))).pipe(Effect.mapError((cause) => oauthClientStoreError("upsert", cause)), Effect.asVoid),
2540
+ find: (input) => findOAuthClientById(executor, tables, input),
2541
+ list: (input = {}) => {
2542
+ const base = Query.select(selectOAuthClientColumns(tables)).pipe(Query.from(tables.oauthClient.table));
2543
+ const query = input.includeDisabled === true
2544
+ ? base
2545
+ : base.pipe(Query.where(queryEq(tables.oauthClient.select.status, "active")));
2546
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => oauthClientStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeOAuthClientRow(tables, row))));
2547
+ },
2548
+ validateRedirectUri: (input) => findOAuthClientById(executor, tables, input, "validate-redirect-uri").pipe(Effect.map((client) => oauthClientRedirectUriValidationResult(Option.isNone(client) ? undefined : client.value, input))),
2549
+ });
2550
+ };
2551
+ export const makeEffectQbSqliteOAuthConsentStore = (executor, options = {}) => {
2552
+ const tables = options.tables ?? authSqliteTables;
2553
+ return OAuthConsentStore.make({
2554
+ upsert: (record) => executePlan(executor, queryInsert(tables.oauthConsent.table, mutationValues(tables.oauthConsent.insert(record))).pipe(Query.onConflict("id", {
2555
+ update: mutationValues(tables.oauthConsent.update(record)),
2556
+ }))).pipe(Effect.mapError((cause) => oauthConsentStoreError("upsert", cause)), Effect.asVoid),
2557
+ findActive: (input) => findOAuthConsentByUserAndClient(executor, tables, input).pipe(Effect.map((record) => Option.isNone(record) || !isOAuthConsentActive(record.value, input)
2558
+ ? Option.none()
2559
+ : Option.some(record.value))),
2560
+ listForUser: (input) => executePlan(executor, Query.select(selectOAuthConsentColumns(tables)).pipe(Query.from(tables.oauthConsent.table), Query.where(queryEq(tables.oauthConsent.select.userId, input.userId)))).pipe(Effect.mapError((cause) => oauthConsentStoreError("list-for-user", cause)), Effect.map((rows) => rows
2561
+ .map((row) => decodeOAuthConsentRow(tables, row))
2562
+ .filter((record) => input.includeInactive === true ||
2563
+ isOAuthConsentActive(record, { now: input.now })))),
2564
+ revoke: (input) => findOAuthConsentByUserAndClient(executor, tables, input, "revoke").pipe(Effect.flatMap((row) => {
2565
+ if (Option.isNone(row)) {
2566
+ return Effect.succeed(Option.none());
2567
+ }
2568
+ return executePlan(executor, queryUpdate(tables.oauthConsent.table, mutationValues(tables.oauthConsent.revoke(input, row.value))).pipe(Query.where(queryEq(tables.oauthConsent.select.userId, input.userId)), Query.where(queryEq(tables.oauthConsent.select.clientId, input.clientId)), Query.returning(selectOAuthConsentColumns(tables)))).pipe(Effect.mapError((cause) => oauthConsentStoreError("revoke", cause)), Effect.map((rows) => rows[0] === undefined
2569
+ ? Option.none()
2570
+ : Option.some(decodeOAuthConsentRow(tables, rows[0]))));
2571
+ })),
2572
+ deleteInactive: (input) => deleteOAuthInactiveConsents(executor, tables, input),
2573
+ });
2574
+ };
2575
+ export const makeEffectQbSqliteOAuthAuthorizationCodeStore = (executor, options = {}) => {
2576
+ const tables = options.tables ?? authSqliteTables;
2577
+ return OAuthAuthorizationCodeStore.make({
2578
+ upsert: (record) => executePlan(executor, queryInsert(tables.oauthAuthorizationCode.table, mutationValues(tables.oauthAuthorizationCode.insert(record))).pipe(Query.onConflict("code_hash", {
2579
+ update: mutationValues(tables.oauthAuthorizationCode.update(record)),
2580
+ }))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError("upsert", cause)), Effect.asVoid),
2581
+ findByHash: (input) => findOAuthAuthorizationCodeByHash(executor, tables, input),
2582
+ markConsumed: (input) => executePlan(executor, queryUpdate(tables.oauthAuthorizationCode.table, mutationValues(tables.oauthAuthorizationCode.markConsumed(input))).pipe(Query.where(queryEq(tables.oauthAuthorizationCode.select.codeHash, input.codeHash)), Query.where(queryIsNull(tables.oauthAuthorizationCode.select.consumedAt)), Query.returning({ codeHash: tables.oauthAuthorizationCode.select.codeHash }))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError("mark-consumed", cause)), Effect.flatMap((rows) => {
2583
+ const [consumed] = rows;
2584
+ if (consumed !== undefined) {
2585
+ return findOAuthAuthorizationCodeByHash(executor, tables, { codeHash: consumed.codeHash }, "mark-consumed").pipe(Effect.map((record) => Option.isNone(record)
2586
+ ? { status: "missing" }
2587
+ : {
2588
+ status: "consumed",
2589
+ record: record.value,
2590
+ }));
2591
+ }
2592
+ return findOAuthAuthorizationCodeByHash(executor, tables, { codeHash: input.codeHash }, "mark-consumed").pipe(Effect.map((record) => Option.isNone(record)
2593
+ ? { status: "missing" }
2594
+ : {
2595
+ status: "already_consumed",
2596
+ record: record.value,
2597
+ }));
2598
+ })),
2599
+ });
2600
+ };
2601
+ export const makeEffectQbSqliteOAuthClientSecretStore = (executor, options = {}) => {
2602
+ const tables = options.tables ?? authSqliteTables;
2603
+ return OAuthClientSecretStore.make({
2604
+ upsert: (record) => executePlan(executor, queryInsert(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.insert(record))).pipe(Query.onConflict("prefix", {
2605
+ update: mutationValues(tables.oauthClientSecret.update(record)),
2606
+ }))).pipe(Effect.mapError((cause) => oauthClientSecretStoreError("upsert", cause)), Effect.asVoid),
2607
+ findByClientIdAndPrefix: (input) => findOAuthClientSecretByClientIdAndPrefix(executor, tables, input),
2608
+ markUsed: (input) => executePlan(executor, queryUpdate(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.markUsed(input))).pipe(Query.where(queryEq(tables.oauthClientSecret.select.clientId, input.clientId)), Query.where(queryEq(tables.oauthClientSecret.select.prefix, input.prefix)), Query.where(queryIsNull(tables.oauthClientSecret.select.revokedAt)), Query.returning(selectOAuthClientSecretColumns(tables)))).pipe(Effect.mapError((cause) => oauthClientSecretStoreError("mark-used", cause)), Effect.map((rows) => rows[0] === undefined
2609
+ ? Option.none()
2610
+ : Option.some(decodeOAuthClientSecretRow(tables, rows[0])))),
2611
+ revoke: (input) => findOAuthClientSecretByClientIdAndPrefix(executor, tables, input, "revoke").pipe(Effect.flatMap((row) => {
2612
+ if (Option.isNone(row)) {
2613
+ return Effect.succeed(Option.none());
2614
+ }
2615
+ return executePlan(executor, queryUpdate(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.revoke(input, row.value))).pipe(Query.where(queryEq(tables.oauthClientSecret.select.clientId, input.clientId)), Query.where(queryEq(tables.oauthClientSecret.select.prefix, input.prefix)), Query.returning(selectOAuthClientSecretColumns(tables)))).pipe(Effect.mapError((cause) => oauthClientSecretStoreError("revoke", cause)), Effect.map((rows) => rows[0] === undefined
2616
+ ? Option.none()
2617
+ : Option.some(decodeOAuthClientSecretRow(tables, rows[0]))));
2618
+ })),
2619
+ });
2620
+ };
2621
+ export const makeEffectQbSqliteOAuthProviderModeTokenStore = (executor, options = {}) => {
2622
+ const tables = options.tables ?? authSqliteTables;
2623
+ return OAuthProviderModeTokenStore.make({
2624
+ upsert: (record) => executePlan(executor, queryInsert(tables.oauthProviderModeToken.table, mutationValues(tables.oauthProviderModeToken.insert(record))).pipe(Query.onConflict("token_hash", {
2625
+ update: mutationValues(tables.oauthProviderModeToken.update(record)),
2626
+ }))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError("upsert", cause)), Effect.asVoid),
2627
+ findByHash: (input) => findOAuthProviderModeTokenByHash(executor, tables, input),
2628
+ revokeByHash: (input) => findOAuthProviderModeTokenByHash(executor, tables, input, "revoke").pipe(Effect.flatMap((row) => {
2629
+ if (Option.isNone(row) ||
2630
+ (input.tokenTypeHint !== undefined &&
2631
+ row.value.tokenType !== input.tokenTypeHint)) {
2632
+ return Effect.succeed(Option.none());
2633
+ }
2634
+ return executePlan(executor, queryUpdate(tables.oauthProviderModeToken.table, mutationValues(tables.oauthProviderModeToken.revoke(input, row.value))).pipe(Query.where(queryEq(tables.oauthProviderModeToken.select.tokenHash, input.tokenHash)), Query.returning(selectOAuthProviderModeTokenColumns(tables)))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError("revoke", cause)), Effect.map((rows) => rows[0] === undefined
2635
+ ? Option.none()
2636
+ : Option.some(decodeOAuthProviderModeTokenRow(tables, rows[0]))));
2637
+ })),
2638
+ rotateRefreshToken: (input) => executePlan(executor, queryUpdate(tables.oauthProviderModeToken.table, mutationValues(tables.oauthProviderModeToken.rotate(input))).pipe(Query.where(queryEq(tables.oauthProviderModeToken.select.tokenHash, input.tokenHash)), Query.where(queryEq(tables.oauthProviderModeToken.select.tokenType, "refresh_token")), Query.where(queryIsNull(tables.oauthProviderModeToken.select.revokedAt)), Query.where(queryIsNull(tables.oauthProviderModeToken.select.rotatedAt)), Query.returning({
2639
+ tokenHash: tables.oauthProviderModeToken.select.tokenHash,
2640
+ }))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError("rotate", cause)), Effect.flatMap((rows) => rows[0] === undefined
2641
+ ? Effect.succeed(Option.none())
2642
+ : findOAuthProviderModeTokenByHash(executor, tables, { tokenHash: rows[0].tokenHash }, "rotate"))),
2643
+ deleteRetired: (input) => deleteOAuthProviderModeRetiredTokens(executor, tables, input),
2644
+ });
2645
+ };
1570
2646
  export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}) => {
1571
2647
  const tables = options.tables ?? authSqliteTables;
1572
2648
  return OAuthProviderTokenVault.make({
@@ -1592,11 +2668,112 @@ export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}
1592
2668
  })),
1593
2669
  });
1594
2670
  };
2671
+ export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}) => {
2672
+ const tables = options.tables ?? authSqliteTables;
2673
+ return DomainVerificationStore.make({
2674
+ upsert: (record) => findDomainVerificationByOwnerAndDomain(executor, tables, { ownerId: record.ownerId, domain: record.domain }, "upsert").pipe(Effect.flatMap((existing) => Option.isNone(existing)
2675
+ ? executePlan(executor, queryInsert(tables.domainVerification.table, mutationValues(tables.domainVerification.insert(record))))
2676
+ : executePlan(executor, queryUpdate(tables.domainVerification.table, mutationValues(tables.domainVerification.update(record))).pipe(Query.where(queryEq(tables.domainVerification.select.ownerId, record.ownerId)), Query.where(queryEq(tables.domainVerification.select.domain, record.domain))))), Effect.mapError((cause) => cause instanceof DomainVerificationStoreError
2677
+ ? cause
2678
+ : domainVerificationStoreError("upsert", cause)), Effect.asVoid),
2679
+ findById: (id) => findDomainVerificationById(executor, tables, id),
2680
+ findByOwnerAndDomain: (input) => findDomainVerificationByOwnerAndDomain(executor, tables, input),
2681
+ listByOwner: (input) => {
2682
+ const base = Query.select(selectDomainVerificationColumns(tables)).pipe(Query.from(tables.domainVerification.table), Query.where(queryEq(tables.domainVerification.select.ownerId, input.ownerId)));
2683
+ const query = input.includeRevoked === true
2684
+ ? base
2685
+ : base.pipe(Query.where(queryIsNull(tables.domainVerification.select.revokedAt)));
2686
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => domainVerificationStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeDomainVerificationRecord(tables, row))));
2687
+ },
2688
+ markVerified: (input) => executePlan(executor, queryUpdate(tables.domainVerification.table, mutationValues(tables.domainVerification.markVerified(input))).pipe(Query.where(queryEq(tables.domainVerification.select.id, input.id)), Query.where(queryEq(tables.domainVerification.select.status, "pending")), Query.returning(selectDomainVerificationColumns(tables)))).pipe(Effect.mapError((cause) => domainVerificationStoreError("mark-verified", cause)), Effect.map((rows) => rows[0] === undefined
2689
+ ? Option.none()
2690
+ : Option.some(decodeDomainVerificationRecord(tables, rows[0])))),
2691
+ revoke: (input) => findDomainVerificationById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((record) => {
2692
+ if (Option.isNone(record)) {
2693
+ return Effect.void;
2694
+ }
2695
+ return executePlan(executor, queryUpdate(tables.domainVerification.table, mutationValues(tables.domainVerification.revoke(input, record.value))).pipe(Query.where(queryEq(tables.domainVerification.select.id, input.id)))).pipe(Effect.mapError((cause) => domainVerificationStoreError("revoke", cause)), Effect.asVoid);
2696
+ })),
2697
+ });
2698
+ };
1595
2699
  export const makeEffectQbSqliteSecurityTimelineStore = (executor, options = {}) => {
1596
2700
  const tables = options.tables ?? authSqliteTables;
1597
2701
  return SecurityTimelineStore.make({
1598
2702
  insert: (event) => executePlan(executor, queryInsert(tables.securityTimeline.table, mutationValues(tables.securityTimeline.insert(event)))).pipe(Effect.mapError((cause) => securityTimelineStoreError("insert", cause)), Effect.asVoid),
1599
2703
  listByUser: (input) => listSecurityTimelineByUser(executor, tables, input),
2704
+ deleteBefore: (input) => deleteSecurityTimelineBefore(executor, tables, input),
2705
+ });
2706
+ };
2707
+ export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) => {
2708
+ const tables = options.tables ?? authSqliteTables;
2709
+ return WebhookOutboxStore.make({
2710
+ enqueue: (input) => Effect.gen(function* () {
2711
+ const now = input.now ?? (yield* currentUnixMillis);
2712
+ const record = {
2713
+ id: input.id,
2714
+ endpointKey: input.endpointKey,
2715
+ event: input.event,
2716
+ status: "pending",
2717
+ attempts: 0,
2718
+ nextAttemptAt: input.nextAttemptAt ?? now,
2719
+ createdAt: now,
2720
+ updatedAt: now,
2721
+ };
2722
+ yield* executePlan(executor, queryInsert(tables.webhookOutbox.table, mutationValues(tables.webhookOutbox.insert(record)))).pipe(Effect.mapError((cause) => webhookOutboxStoreError("enqueue", cause)), Effect.asVoid);
2723
+ return record;
2724
+ }),
2725
+ claimDue: (input) => claimDueWebhookOutboxRecords(executor, tables, input),
2726
+ markDelivered: (input) => Effect.gen(function* () {
2727
+ const existing = yield* findWebhookOutboxRecordById(executor, tables, input.id, "markDelivered");
2728
+ if (Option.isNone(existing) || existing.value.status === "dead_lettered") {
2729
+ return;
2730
+ }
2731
+ const deliveredAt = input.deliveredAt ?? (yield* currentUnixMillis);
2732
+ yield* executePlan(executor, queryUpdate(tables.webhookOutbox.table, mutationValues(tables.webhookOutbox.markDelivered({ ...input, deliveredAt }))).pipe(Query.where(queryEq(tables.webhookOutbox.select.id, input.id)))).pipe(Effect.mapError((cause) => webhookOutboxStoreError("markDelivered", cause)), Effect.asVoid);
2733
+ }),
2734
+ markFailed: (input) => Effect.gen(function* () {
2735
+ const existing = yield* findWebhookOutboxRecordById(executor, tables, input.id, "markFailed");
2736
+ if (Option.isNone(existing) ||
2737
+ existing.value.status === "delivered" ||
2738
+ existing.value.status === "dead_lettered") {
2739
+ return;
2740
+ }
2741
+ const failedAt = input.failedAt ?? (yield* currentUnixMillis);
2742
+ yield* executePlan(executor, queryUpdate(tables.webhookOutbox.table, mutationValues(tables.webhookOutbox.markFailed({
2743
+ ...input,
2744
+ failedAt,
2745
+ attempts: existing.value.attempts + 1,
2746
+ }))).pipe(Query.where(queryEq(tables.webhookOutbox.select.id, input.id)))).pipe(Effect.mapError((cause) => webhookOutboxStoreError("markFailed", cause)), Effect.asVoid);
2747
+ }),
2748
+ markDeadLettered: (input) => Effect.gen(function* () {
2749
+ const existing = yield* findWebhookOutboxRecordById(executor, tables, input.id, "markDeadLettered");
2750
+ if (Option.isNone(existing) ||
2751
+ existing.value.status === "delivered" ||
2752
+ existing.value.status === "dead_lettered") {
2753
+ return;
2754
+ }
2755
+ const deadLetteredAt = input.deadLetteredAt ?? (yield* currentUnixMillis);
2756
+ yield* executePlan(executor, queryUpdate(tables.webhookOutbox.table, mutationValues(tables.webhookOutbox.markDeadLettered({
2757
+ ...input,
2758
+ deadLetteredAt,
2759
+ }))).pipe(Query.where(queryEq(tables.webhookOutbox.select.id, input.id)))).pipe(Effect.mapError((cause) => webhookOutboxStoreError("markDeadLettered", cause)), Effect.asVoid);
2760
+ }),
2761
+ deleteBefore: (input) => deleteWebhookOutboxRecordsBefore(executor, tables, input),
2762
+ });
2763
+ };
2764
+ export const makeEffectQbSqliteWebhookReplayStore = (executor, options = {}) => {
2765
+ const tables = options.tables ?? authSqliteTables;
2766
+ return WebhookReplayStore.make({
2767
+ claim: (input) => Effect.gen(function* () {
2768
+ const now = input.now ?? (yield* currentUnixMillis);
2769
+ const claim = { ...input, now };
2770
+ const rows = yield* executePlan(executor, queryInsert(tables.webhookReplay.table, mutationValues(tables.webhookReplay.insert(claim))).pipe(Query.onConflict("id", {
2771
+ update: mutationValues(tables.webhookReplay.update(claim)),
2772
+ where: queryLt(tables.webhookReplay.select.expiresAt, Number(now)),
2773
+ }), Query.returning(selectWebhookReplayColumns(tables)))).pipe(Effect.mapError((cause) => webhookReplayStoreError("claim", cause)));
2774
+ return rows.length > 0;
2775
+ }),
2776
+ deleteExpired: (input = {}) => deleteExpiredWebhookReplayRecords(executor, tables, input),
1600
2777
  });
1601
2778
  };
1602
2779
  export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
@@ -1604,7 +2781,9 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
1604
2781
  credentialStore: makeEffectQbSqliteCredentialStore(executor, options),
1605
2782
  sessionStore: makeEffectQbSqliteSessionStore(executor, options),
1606
2783
  verificationStore: makeEffectQbSqliteVerificationStore(executor, options),
2784
+ auditLogStore: makeEffectQbSqliteAuditLogStore(executor, options),
1607
2785
  loginApprovalReviewStore: makeEffectQbSqliteLoginApprovalReviewStore(executor, options),
2786
+ loginRiskHistoryStore: makeEffectQbSqliteLoginRiskHistoryStore(executor, options),
1608
2787
  trustedDeviceStore: makeEffectQbSqliteTrustedDeviceStore(executor, options),
1609
2788
  passkeyCredentialStore: makeEffectQbSqlitePasskeyCredentialStore(executor, options),
1610
2789
  totpFactorStore: makeEffectQbSqliteTotpFactorStore(executor, options),
@@ -1613,10 +2792,18 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
1613
2792
  refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, options),
1614
2793
  jwtRevocationStore: makeEffectQbSqliteJwtRevocationStore(executor, options),
1615
2794
  oauthAccountStore: makeEffectQbSqliteOAuthAccountStore(executor, options),
2795
+ oauthClientStore: makeEffectQbSqliteOAuthClientStore(executor, options),
2796
+ oauthConsentStore: makeEffectQbSqliteOAuthConsentStore(executor, options),
2797
+ oauthAuthorizationCodeStore: makeEffectQbSqliteOAuthAuthorizationCodeStore(executor, options),
2798
+ oauthClientSecretStore: makeEffectQbSqliteOAuthClientSecretStore(executor, options),
2799
+ oauthProviderModeTokenStore: makeEffectQbSqliteOAuthProviderModeTokenStore(executor, options),
1616
2800
  oauthProviderTokenVault: makeEffectQbSqliteOAuthProviderTokenVault(executor, options),
2801
+ domainVerificationStore: makeEffectQbSqliteDomainVerificationStore(executor, options),
1617
2802
  securityTimelineStore: makeEffectQbSqliteSecurityTimelineStore(executor, options),
2803
+ webhookOutboxStore: makeEffectQbSqliteWebhookOutboxStore(executor, options),
2804
+ webhookReplayStore: makeEffectQbSqliteWebhookReplayStore(executor, options),
1618
2805
  });
1619
- export const EffectQbSqliteAuthStorageLive = (executor, options = {}) => Layer.mergeAll(Layer.succeed(UserStore)(makeEffectQbSqliteUserStore(executor, options)), Layer.succeed(CredentialStore)(makeEffectQbSqliteCredentialStore(executor, options)), Layer.succeed(SessionStore)(makeEffectQbSqliteSessionStore(executor, options)), Layer.succeed(VerificationStore)(makeEffectQbSqliteVerificationStore(executor, options)), Layer.succeed(LoginApprovalReviewStore)(makeEffectQbSqliteLoginApprovalReviewStore(executor, options)), Layer.succeed(TrustedDeviceStore)(makeEffectQbSqliteTrustedDeviceStore(executor, options)), Layer.succeed(PasskeyCredentialStore)(makeEffectQbSqlitePasskeyCredentialStore(executor, options)), Layer.succeed(TotpFactorStore)(makeEffectQbSqliteTotpFactorStore(executor, options)), Layer.succeed(RecoveryCodeStore)(makeEffectQbSqliteRecoveryCodeStore(executor, options)), Layer.succeed(ApiKeyStore)(makeEffectQbSqliteApiKeyStore(executor, options)), Layer.succeed(RefreshTokenStore)(makeEffectQbSqliteRefreshTokenStore(executor, options)), Layer.succeed(JwtRevocationStore)(makeEffectQbSqliteJwtRevocationStore(executor, options)), Layer.succeed(OAuthAccountStore)(makeEffectQbSqliteOAuthAccountStore(executor, options)), Layer.succeed(OAuthProviderTokenVault)(makeEffectQbSqliteOAuthProviderTokenVault(executor, options)), Layer.succeed(SecurityTimelineStore)(makeEffectQbSqliteSecurityTimelineStore(executor, options)));
2806
+ export const EffectQbSqliteAuthStorageLive = (executor, options = {}) => Layer.mergeAll(Layer.succeed(UserStore)(makeEffectQbSqliteUserStore(executor, options)), Layer.succeed(CredentialStore)(makeEffectQbSqliteCredentialStore(executor, options)), Layer.succeed(SessionStore)(makeEffectQbSqliteSessionStore(executor, options)), Layer.succeed(VerificationStore)(makeEffectQbSqliteVerificationStore(executor, options)), Layer.succeed(AuditLogStore)(makeEffectQbSqliteAuditLogStore(executor, options)), Layer.succeed(LoginApprovalReviewStore)(makeEffectQbSqliteLoginApprovalReviewStore(executor, options)), Layer.succeed(LoginRiskHistory)(makeEffectQbSqliteLoginRiskHistoryStore(executor, options)), Layer.succeed(TrustedDeviceStore)(makeEffectQbSqliteTrustedDeviceStore(executor, options)), Layer.succeed(PasskeyCredentialStore)(makeEffectQbSqlitePasskeyCredentialStore(executor, options)), Layer.succeed(TotpFactorStore)(makeEffectQbSqliteTotpFactorStore(executor, options)), Layer.succeed(RecoveryCodeStore)(makeEffectQbSqliteRecoveryCodeStore(executor, options)), Layer.succeed(ApiKeyStore)(makeEffectQbSqliteApiKeyStore(executor, options)), Layer.succeed(RefreshTokenStore)(makeEffectQbSqliteRefreshTokenStore(executor, options)), Layer.succeed(JwtRevocationStore)(makeEffectQbSqliteJwtRevocationStore(executor, options)), Layer.succeed(OAuthAccountStore)(makeEffectQbSqliteOAuthAccountStore(executor, options)), Layer.succeed(OAuthClientStore)(makeEffectQbSqliteOAuthClientStore(executor, options)), Layer.succeed(OAuthConsentStore)(makeEffectQbSqliteOAuthConsentStore(executor, options)), Layer.succeed(OAuthAuthorizationCodeStore)(makeEffectQbSqliteOAuthAuthorizationCodeStore(executor, options)), Layer.succeed(OAuthClientSecretStore)(makeEffectQbSqliteOAuthClientSecretStore(executor, options)), Layer.succeed(OAuthProviderModeTokenStore)(makeEffectQbSqliteOAuthProviderModeTokenStore(executor, options)), Layer.succeed(OAuthProviderTokenVault)(makeEffectQbSqliteOAuthProviderTokenVault(executor, options)), Layer.succeed(DomainVerificationStore)(makeEffectQbSqliteDomainVerificationStore(executor, options)), Layer.succeed(SecurityTimelineStore)(makeEffectQbSqliteSecurityTimelineStore(executor, options)), Layer.succeed(WebhookOutboxStore)(makeEffectQbSqliteWebhookOutboxStore(executor, options)), Layer.succeed(WebhookReplayStore)(makeEffectQbSqliteWebhookReplayStore(executor, options)));
1620
2807
  export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
1621
2808
  driver: Executor.driver((query) => Effect.tryPromise({
1622
2809
  try: () => database