@effect-auth/core 0.1.0-alpha.6 → 0.1.0-alpha.8

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.
@@ -1,10 +1,16 @@
1
- import { Data, Effect, Layer, Option } from "effect";
1
+ import { Data, Effect, Layer, Option, Redacted } from "effect";
2
2
  import { Column, Executor, Query, Scalar, Table } from "effect-qb/sqlite";
3
- import { AuthFlowId, ChallengeId, CredentialId, Email, SessionId, UnixMillis, UserId, } from "./Identifiers.js";
3
+ import { ApiKeyId, ApiKeyPrefix, ApiKeySecretHash, ApiKeyStore, ApiKeyStoreError, } from "./ApiKey.js";
4
+ import { AuthFlowId, ChallengeId, CredentialId, Email, OAuthAccountId, OAuthProviderId, SessionId, UnixMillis, UserId, } from "./Identifiers.js";
4
5
  import { JwtError, JwtRevocationStore } from "./Jwt.js";
5
6
  import { LoginApprovalReviewError, LoginApprovalReviewStore, } from "./LoginApproval.js";
6
7
  import { LoginRiskContext, LoginRiskSignal } from "./LoginRisk.js";
8
+ import { OAuthAccountStore, OAuthAccountStoreError, } from "./OAuth.js";
9
+ import { PasskeyCredentialId, PasskeyCredentialStore, PasskeyCredentialStoreError, } from "./Passkey.js";
10
+ import { RecoveryCodeHash, RecoveryCodeStore, RecoveryCodeStoreError, } from "./RecoveryCode.js";
11
+ import { RefreshTokenFamilyId, RefreshTokenId, RefreshTokenSecretHash, RefreshTokenStore, RefreshTokenStoreError, } from "./RefreshToken.js";
7
12
  import { CredentialStore, SessionStore, StorageError, UserStore, VerificationStore, } from "./Storage.js";
13
+ import { TotpFactorStore, TotpFactorStoreError, TotpSecret, } from "./Totp.js";
8
14
  import { TrustedDeviceError, TrustedDeviceStore } from "./TrustedDevice.js";
9
15
  export class D1EffectQbError extends Data.TaggedError("D1EffectQbError") {
10
16
  }
@@ -78,12 +84,85 @@ const makeAuthTrustedDeviceTable = (name) => Table.make(name, {
78
84
  expires_at: Column.int(),
79
85
  metadata: Column.text().pipe(Column.nullable),
80
86
  });
87
+ const makeAuthPasskeyCredentialTable = (name) => Table.make(name, {
88
+ id: Column.text().pipe(Column.primaryKey),
89
+ user_id: Column.text(),
90
+ credential_id: Column.text(),
91
+ public_key: Column.text(),
92
+ sign_count: Column.int(),
93
+ transports: Column.text().pipe(Column.nullable),
94
+ backed_up: Column.int().pipe(Column.nullable),
95
+ created_at: Column.int(),
96
+ last_used_at: Column.int().pipe(Column.nullable),
97
+ revoked_at: Column.int().pipe(Column.nullable),
98
+ metadata: Column.text().pipe(Column.nullable),
99
+ });
100
+ const makeAuthTotpFactorTable = (name) => Table.make(name, {
101
+ id: Column.text().pipe(Column.primaryKey),
102
+ user_id: Column.text(),
103
+ secret: Column.text(),
104
+ algorithm: Column.text(),
105
+ digits: Column.int(),
106
+ period: Column.int(),
107
+ created_at: Column.int(),
108
+ confirmed_at: Column.int().pipe(Column.nullable),
109
+ last_used_at: Column.int().pipe(Column.nullable),
110
+ revoked_at: Column.int().pipe(Column.nullable),
111
+ metadata: Column.text().pipe(Column.nullable),
112
+ });
113
+ const makeAuthRecoveryCodeTable = (name) => Table.make(name, {
114
+ id: Column.text().pipe(Column.primaryKey),
115
+ user_id: Column.text(),
116
+ code_hash: Column.text(),
117
+ created_at: Column.int(),
118
+ used_at: Column.int().pipe(Column.nullable),
119
+ revoked_at: Column.int().pipe(Column.nullable),
120
+ metadata: Column.text().pipe(Column.nullable),
121
+ });
122
+ const makeAuthApiKeyTable = (name) => Table.make(name, {
123
+ id: Column.text().pipe(Column.primaryKey),
124
+ user_id: Column.text(),
125
+ prefix: Column.text(),
126
+ secret_hash: Column.text(),
127
+ scopes: Column.text(),
128
+ created_at: Column.int(),
129
+ expires_at: Column.int().pipe(Column.nullable),
130
+ last_used_at: Column.int().pipe(Column.nullable),
131
+ revoked_at: Column.int().pipe(Column.nullable),
132
+ metadata: Column.text().pipe(Column.nullable),
133
+ });
134
+ const makeAuthRefreshTokenTable = (name) => Table.make(name, {
135
+ id: Column.text().pipe(Column.primaryKey),
136
+ family_id: Column.text(),
137
+ user_id: Column.text(),
138
+ secret_hash: Column.text(),
139
+ created_at: Column.int(),
140
+ expires_at: Column.int(),
141
+ last_used_at: Column.int().pipe(Column.nullable),
142
+ rotated_at: Column.int().pipe(Column.nullable),
143
+ replaced_by_id: Column.text().pipe(Column.nullable),
144
+ revoked_at: Column.int().pipe(Column.nullable),
145
+ reuse_detected_at: Column.int().pipe(Column.nullable),
146
+ metadata: Column.text().pipe(Column.nullable),
147
+ });
81
148
  const makeAuthJwtRevocationTable = (name) => Table.make(name, {
82
149
  jwt_id: Column.text().pipe(Column.primaryKey),
83
150
  revoked_at: Column.int(),
84
151
  expires_at: Column.int().pipe(Column.nullable),
85
152
  reason: Column.text().pipe(Column.nullable),
86
153
  });
154
+ const makeAuthOAuthAccountTable = (name) => Table.make(name, {
155
+ id: Column.text().pipe(Column.primaryKey),
156
+ provider_id: Column.text(),
157
+ provider_account_id: Column.text(),
158
+ user_id: Column.text(),
159
+ email: Column.text().pipe(Column.nullable),
160
+ email_verified: Column.int().pipe(Column.nullable),
161
+ created_at: Column.int(),
162
+ updated_at: Column.int(),
163
+ unlinked_at: Column.int().pipe(Column.nullable),
164
+ metadata: Column.text().pipe(Column.nullable),
165
+ });
87
166
  const authSqliteTablesTypeId = Symbol.for("@effect-auth/core/EffectQbSqliteStorage/AuthSqliteTables");
88
167
  const jsonEncode = (value) => value === undefined ? null : JSON.stringify(value);
89
168
  const jsonDecode = (value) => {
@@ -120,6 +199,7 @@ const executePlan = (executor, plan) => executor.execute(plan);
120
199
  const omitUndefined = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
121
200
  const sessionMetadataEnvelopeKey = "__effectAuthSession";
122
201
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
202
+ const metadataWithReason = (metadata, key, reason) => reason === undefined ? metadata : { ...metadata, [key]: reason };
123
203
  const isLoginRiskLevel = (value) => value === "unknown" ||
124
204
  value === "low" ||
125
205
  value === "medium" ||
@@ -279,6 +359,92 @@ const decodeDefaultTrustedDeviceRow = (row) => omitUndefined({
279
359
  const decodeTrustedDeviceRow = (tables, row) => tables.trustedDevice.decode === undefined
280
360
  ? decodeDefaultTrustedDeviceRow(row)
281
361
  : tables.trustedDevice.decode(row);
362
+ const decodePasskeyTransports = (value) => {
363
+ const decoded = jsonDecode(value);
364
+ return Array.isArray(decoded) ? decoded : undefined;
365
+ };
366
+ const decodeDefaultPasskeyCredentialRow = (row) => omitUndefined({
367
+ id: CredentialId(row.id),
368
+ userId: UserId(row.userId),
369
+ credentialId: PasskeyCredentialId(row.credentialId),
370
+ publicKey: row.publicKey,
371
+ signCount: Number(row.signCount),
372
+ transports: decodePasskeyTransports(row.transports),
373
+ backedUp: row.backedUp === null || row.backedUp === undefined
374
+ ? undefined
375
+ : decodeBoolean(row.backedUp),
376
+ createdAt: UnixMillis(Number(row.createdAt)),
377
+ lastUsedAt: optionalUnixMillis(row.lastUsedAt),
378
+ revokedAt: optionalUnixMillis(row.revokedAt),
379
+ metadata: jsonDecode(row.metadata),
380
+ });
381
+ const decodePasskeyCredentialRow = (tables, row) => tables.passkeyCredential.decode === undefined
382
+ ? decodeDefaultPasskeyCredentialRow(row)
383
+ : tables.passkeyCredential.decode(row);
384
+ const decodeDefaultTotpFactorRow = (row) => omitUndefined({
385
+ id: CredentialId(row.id),
386
+ userId: UserId(row.userId),
387
+ secret: Redacted.make(TotpSecret(row.secret)),
388
+ algorithm: row.algorithm,
389
+ digits: Number(row.digits),
390
+ period: Number(row.period),
391
+ createdAt: UnixMillis(Number(row.createdAt)),
392
+ confirmedAt: optionalUnixMillis(row.confirmedAt),
393
+ lastUsedAt: optionalUnixMillis(row.lastUsedAt),
394
+ revokedAt: optionalUnixMillis(row.revokedAt),
395
+ metadata: jsonDecode(row.metadata),
396
+ });
397
+ const decodeTotpFactorRow = (tables, row) => tables.totpFactor.decode === undefined
398
+ ? decodeDefaultTotpFactorRow(row)
399
+ : tables.totpFactor.decode(row);
400
+ const decodeDefaultRecoveryCodeRow = (row) => omitUndefined({
401
+ id: CredentialId(row.id),
402
+ userId: UserId(row.userId),
403
+ codeHash: RecoveryCodeHash(row.codeHash),
404
+ createdAt: UnixMillis(Number(row.createdAt)),
405
+ usedAt: optionalUnixMillis(row.usedAt),
406
+ revokedAt: optionalUnixMillis(row.revokedAt),
407
+ metadata: jsonDecode(row.metadata),
408
+ });
409
+ const decodeRecoveryCodeRow = (tables, row) => tables.recoveryCode.decode === undefined
410
+ ? decodeDefaultRecoveryCodeRow(row)
411
+ : tables.recoveryCode.decode(row);
412
+ const decodeStringArray = (value) => {
413
+ const decoded = jsonDecode(value);
414
+ return Array.isArray(decoded) ? decoded.map(String) : [];
415
+ };
416
+ const decodeDefaultApiKeyRow = (row) => omitUndefined({
417
+ id: ApiKeyId(row.id),
418
+ userId: UserId(row.userId),
419
+ prefix: ApiKeyPrefix(row.prefix),
420
+ secretHash: ApiKeySecretHash(row.secretHash),
421
+ scopes: decodeStringArray(row.scopes),
422
+ createdAt: UnixMillis(Number(row.createdAt)),
423
+ expiresAt: optionalUnixMillis(row.expiresAt),
424
+ lastUsedAt: optionalUnixMillis(row.lastUsedAt),
425
+ revokedAt: optionalUnixMillis(row.revokedAt),
426
+ metadata: jsonDecode(row.metadata),
427
+ });
428
+ const decodeApiKeyRow = (tables, row) => tables.apiKey.decode === undefined
429
+ ? decodeDefaultApiKeyRow(row)
430
+ : tables.apiKey.decode(row);
431
+ const decodeDefaultRefreshTokenRow = (row) => omitUndefined({
432
+ id: RefreshTokenId(row.id),
433
+ familyId: RefreshTokenFamilyId(row.familyId),
434
+ userId: UserId(row.userId),
435
+ secretHash: RefreshTokenSecretHash(row.secretHash),
436
+ createdAt: UnixMillis(Number(row.createdAt)),
437
+ expiresAt: UnixMillis(Number(row.expiresAt)),
438
+ lastUsedAt: optionalUnixMillis(row.lastUsedAt),
439
+ rotatedAt: optionalUnixMillis(row.rotatedAt),
440
+ replacedById: row.replacedById === null ? undefined : RefreshTokenId(row.replacedById),
441
+ revokedAt: optionalUnixMillis(row.revokedAt),
442
+ reuseDetectedAt: optionalUnixMillis(row.reuseDetectedAt),
443
+ metadata: jsonDecode(row.metadata),
444
+ });
445
+ const decodeRefreshTokenRow = (tables, row) => tables.refreshToken.decode === undefined
446
+ ? decodeDefaultRefreshTokenRow(row)
447
+ : tables.refreshToken.decode(row);
282
448
  const decodeDefaultJwtRevocationRow = (row) => omitUndefined({
283
449
  jwtId: row.jwtId,
284
450
  revokedAt: UnixMillis(Number(row.revokedAt)),
@@ -288,6 +454,23 @@ const decodeDefaultJwtRevocationRow = (row) => omitUndefined({
288
454
  const decodeJwtRevocationRow = (tables, row) => tables.jwtRevocation.decode === undefined
289
455
  ? decodeDefaultJwtRevocationRow(row)
290
456
  : tables.jwtRevocation.decode(row);
457
+ const decodeDefaultOAuthAccountRow = (row) => omitUndefined({
458
+ id: OAuthAccountId(row.id),
459
+ providerId: OAuthProviderId(row.providerId),
460
+ providerAccountId: row.providerAccountId,
461
+ userId: UserId(row.userId),
462
+ email: row.email === null ? undefined : Email(row.email),
463
+ emailVerified: row.emailVerified === null || row.emailVerified === undefined
464
+ ? undefined
465
+ : decodeBoolean(row.emailVerified),
466
+ createdAt: UnixMillis(Number(row.createdAt)),
467
+ updatedAt: UnixMillis(Number(row.updatedAt)),
468
+ unlinkedAt: optionalUnixMillis(row.unlinkedAt),
469
+ metadata: jsonDecode(row.metadata),
470
+ });
471
+ const decodeOAuthAccountRow = (tables, row) => tables.oauthAccount.decode === undefined
472
+ ? decodeDefaultOAuthAccountRow(row)
473
+ : tables.oauthAccount.decode(row);
291
474
  const userInsert = (row) => ({
292
475
  id: row.id,
293
476
  email: row.email,
@@ -408,12 +591,142 @@ const trustedDeviceUpdate = (input, existing) => ({
408
591
  expires_at: Number(input.expiresAt),
409
592
  metadata: jsonEncode(input.metadata ?? existing.metadata),
410
593
  });
594
+ const passkeyCredentialInsert = (row) => ({
595
+ id: row.id,
596
+ user_id: row.userId,
597
+ credential_id: row.credentialId,
598
+ public_key: row.publicKey,
599
+ sign_count: row.signCount,
600
+ transports: jsonEncode(row.transports),
601
+ backed_up: row.backedUp === undefined ? null : row.backedUp ? 1 : 0,
602
+ created_at: Number(row.createdAt),
603
+ last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
604
+ revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
605
+ metadata: jsonEncode(row.metadata),
606
+ });
607
+ const passkeyCredentialUpdateSignCount = (input) => omitUndefined({
608
+ sign_count: input.signCount,
609
+ last_used_at: Number(input.lastUsedAt),
610
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
611
+ });
612
+ const passkeyCredentialRevoke = (input, row) => ({
613
+ revoked_at: Number(input.revokedAt),
614
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
615
+ });
616
+ const totpFactorInsert = (row) => ({
617
+ id: row.id,
618
+ user_id: row.userId,
619
+ secret: Redacted.value(row.secret),
620
+ algorithm: row.algorithm,
621
+ digits: row.digits,
622
+ period: row.period,
623
+ created_at: Number(row.createdAt),
624
+ confirmed_at: row.confirmedAt === undefined ? null : Number(row.confirmedAt),
625
+ last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
626
+ revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
627
+ metadata: jsonEncode(row.metadata),
628
+ });
629
+ const totpFactorConfirm = (input) => omitUndefined({
630
+ confirmed_at: Number(input.confirmedAt),
631
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
632
+ });
633
+ const totpFactorMarkUsed = (input) => omitUndefined({
634
+ last_used_at: Number(input.lastUsedAt),
635
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
636
+ });
637
+ const totpFactorRevoke = (input, row) => ({
638
+ revoked_at: Number(input.revokedAt),
639
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
640
+ });
641
+ const recoveryCodeInsert = (row) => ({
642
+ id: row.id,
643
+ user_id: row.userId,
644
+ code_hash: row.codeHash,
645
+ created_at: Number(row.createdAt),
646
+ used_at: row.usedAt === undefined ? null : Number(row.usedAt),
647
+ revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
648
+ metadata: jsonEncode(row.metadata),
649
+ });
650
+ const recoveryCodeMarkUsed = (input) => omitUndefined({
651
+ used_at: Number(input.usedAt),
652
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
653
+ });
654
+ const recoveryCodeRevoke = (input, row) => ({
655
+ revoked_at: Number(input.revokedAt),
656
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
657
+ });
658
+ const apiKeyInsert = (row) => ({
659
+ id: row.id,
660
+ user_id: row.userId,
661
+ prefix: row.prefix,
662
+ secret_hash: row.secretHash,
663
+ scopes: JSON.stringify(row.scopes),
664
+ created_at: Number(row.createdAt),
665
+ expires_at: row.expiresAt === undefined ? null : Number(row.expiresAt),
666
+ last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
667
+ revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
668
+ metadata: jsonEncode(row.metadata),
669
+ });
670
+ const apiKeyMarkUsed = (input) => omitUndefined({
671
+ last_used_at: Number(input.lastUsedAt),
672
+ metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
673
+ });
674
+ const apiKeyRevoke = (input, row) => ({
675
+ revoked_at: Number(input.revokedAt),
676
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
677
+ });
678
+ const refreshTokenInsert = (row) => ({
679
+ id: row.id,
680
+ family_id: row.familyId,
681
+ user_id: row.userId,
682
+ secret_hash: row.secretHash,
683
+ created_at: Number(row.createdAt),
684
+ expires_at: Number(row.expiresAt),
685
+ last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
686
+ rotated_at: row.rotatedAt === undefined ? null : Number(row.rotatedAt),
687
+ replaced_by_id: row.replacedById ?? null,
688
+ revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
689
+ reuse_detected_at: row.reuseDetectedAt === undefined ? null : Number(row.reuseDetectedAt),
690
+ metadata: jsonEncode(row.metadata),
691
+ });
692
+ const refreshTokenRotate = (input) => ({
693
+ last_used_at: Number(input.rotatedAt),
694
+ rotated_at: Number(input.rotatedAt),
695
+ replaced_by_id: input.replacement.id,
696
+ });
697
+ const refreshTokenMarkReuseDetected = (input) => ({
698
+ reuse_detected_at: Number(input.reuseDetectedAt),
699
+ });
700
+ const refreshTokenRevoke = (input, row) => ({
701
+ revoked_at: Number(input.revokedAt),
702
+ metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
703
+ });
704
+ const refreshTokenRevokeFamily = (input, row) => ({
705
+ revoked_at: Number(input.revokedAt),
706
+ metadata: jsonEncode(metadataWithReason(row.metadata, "familyRevokeReason", input.reason)),
707
+ });
411
708
  const jwtRevocationInsert = (record) => ({
412
709
  jwt_id: record.jwtId,
413
710
  revoked_at: Number(record.revokedAt),
414
711
  expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
415
712
  reason: record.reason ?? null,
416
713
  });
714
+ const oauthAccountInsert = (row) => ({
715
+ id: row.id,
716
+ provider_id: row.providerId,
717
+ provider_account_id: row.providerAccountId,
718
+ user_id: row.userId,
719
+ email: row.email ?? null,
720
+ email_verified: row.emailVerified === undefined ? null : row.emailVerified ? 1 : 0,
721
+ created_at: Number(row.createdAt),
722
+ updated_at: Number(row.updatedAt),
723
+ unlinked_at: row.unlinkedAt === undefined ? null : Number(row.unlinkedAt),
724
+ metadata: jsonEncode(row.metadata),
725
+ });
726
+ const oauthAccountUnlink = (input, row) => ({
727
+ unlinked_at: Number(input.unlinkedAt),
728
+ metadata: jsonEncode(metadataWithReason(row.metadata, "unlinkReason", input.reason)),
729
+ });
417
730
  const jwtRevocationUpdate = (record) => ({
418
731
  revoked_at: Number(record.revokedAt),
419
732
  expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
@@ -426,7 +739,13 @@ const makeInternalAuthSqliteTables = (names = {}) => {
426
739
  const verification = makeAuthVerificationTable(names.verification ?? "auth_verification");
427
740
  const loginApprovalReview = makeAuthLoginApprovalReviewTable(names.loginApprovalReview ?? "auth_login_approval_review");
428
741
  const trustedDevice = makeAuthTrustedDeviceTable(names.trustedDevice ?? "auth_trusted_device");
742
+ const passkeyCredential = makeAuthPasskeyCredentialTable(names.passkeyCredential ?? "auth_passkey_credential");
743
+ const totpFactor = makeAuthTotpFactorTable(names.totpFactor ?? "auth_totp_factor");
744
+ const recoveryCode = makeAuthRecoveryCodeTable(names.recoveryCode ?? "auth_recovery_code");
745
+ const apiKey = makeAuthApiKeyTable(names.apiKey ?? "auth_api_key");
746
+ const refreshToken = makeAuthRefreshTokenTable(names.refreshToken ?? "auth_refresh_token");
429
747
  const jwtRevocation = makeAuthJwtRevocationTable(names.jwtRevocation ?? "auth_jwt_revocation");
748
+ const oauthAccount = makeAuthOAuthAccountTable(names.oauthAccount ?? "auth_oauth_account");
430
749
  return {
431
750
  user: {
432
751
  table: user,
@@ -532,6 +851,100 @@ const makeInternalAuthSqliteTables = (names = {}) => {
532
851
  insert: trustedDeviceInsert,
533
852
  update: trustedDeviceUpdate,
534
853
  },
854
+ passkeyCredential: {
855
+ table: passkeyCredential,
856
+ select: {
857
+ id: passkeyCredential.id,
858
+ userId: passkeyCredential.user_id,
859
+ credentialId: passkeyCredential.credential_id,
860
+ publicKey: passkeyCredential.public_key,
861
+ signCount: passkeyCredential.sign_count,
862
+ transports: passkeyCredential.transports,
863
+ backedUp: passkeyCredential.backed_up,
864
+ createdAt: passkeyCredential.created_at,
865
+ lastUsedAt: passkeyCredential.last_used_at,
866
+ revokedAt: passkeyCredential.revoked_at,
867
+ metadata: passkeyCredential.metadata,
868
+ },
869
+ insert: passkeyCredentialInsert,
870
+ updateSignCount: passkeyCredentialUpdateSignCount,
871
+ revoke: passkeyCredentialRevoke,
872
+ },
873
+ totpFactor: {
874
+ table: totpFactor,
875
+ select: {
876
+ id: totpFactor.id,
877
+ userId: totpFactor.user_id,
878
+ secret: totpFactor.secret,
879
+ algorithm: totpFactor.algorithm,
880
+ digits: totpFactor.digits,
881
+ period: totpFactor.period,
882
+ createdAt: totpFactor.created_at,
883
+ confirmedAt: totpFactor.confirmed_at,
884
+ lastUsedAt: totpFactor.last_used_at,
885
+ revokedAt: totpFactor.revoked_at,
886
+ metadata: totpFactor.metadata,
887
+ },
888
+ insert: totpFactorInsert,
889
+ confirm: totpFactorConfirm,
890
+ markUsed: totpFactorMarkUsed,
891
+ revoke: totpFactorRevoke,
892
+ },
893
+ recoveryCode: {
894
+ table: recoveryCode,
895
+ select: {
896
+ id: recoveryCode.id,
897
+ userId: recoveryCode.user_id,
898
+ codeHash: recoveryCode.code_hash,
899
+ createdAt: recoveryCode.created_at,
900
+ usedAt: recoveryCode.used_at,
901
+ revokedAt: recoveryCode.revoked_at,
902
+ metadata: recoveryCode.metadata,
903
+ },
904
+ insert: recoveryCodeInsert,
905
+ markUsed: recoveryCodeMarkUsed,
906
+ revoke: recoveryCodeRevoke,
907
+ },
908
+ apiKey: {
909
+ table: apiKey,
910
+ select: {
911
+ id: apiKey.id,
912
+ userId: apiKey.user_id,
913
+ prefix: apiKey.prefix,
914
+ secretHash: apiKey.secret_hash,
915
+ scopes: apiKey.scopes,
916
+ createdAt: apiKey.created_at,
917
+ expiresAt: apiKey.expires_at,
918
+ lastUsedAt: apiKey.last_used_at,
919
+ revokedAt: apiKey.revoked_at,
920
+ metadata: apiKey.metadata,
921
+ },
922
+ insert: apiKeyInsert,
923
+ markUsed: apiKeyMarkUsed,
924
+ revoke: apiKeyRevoke,
925
+ },
926
+ refreshToken: {
927
+ table: refreshToken,
928
+ select: {
929
+ id: refreshToken.id,
930
+ familyId: refreshToken.family_id,
931
+ userId: refreshToken.user_id,
932
+ secretHash: refreshToken.secret_hash,
933
+ createdAt: refreshToken.created_at,
934
+ expiresAt: refreshToken.expires_at,
935
+ lastUsedAt: refreshToken.last_used_at,
936
+ rotatedAt: refreshToken.rotated_at,
937
+ replacedById: refreshToken.replaced_by_id,
938
+ revokedAt: refreshToken.revoked_at,
939
+ reuseDetectedAt: refreshToken.reuse_detected_at,
940
+ metadata: refreshToken.metadata,
941
+ },
942
+ insert: refreshTokenInsert,
943
+ rotate: refreshTokenRotate,
944
+ markReuseDetected: refreshTokenMarkReuseDetected,
945
+ revoke: refreshTokenRevoke,
946
+ revokeFamily: refreshTokenRevokeFamily,
947
+ },
535
948
  jwtRevocation: {
536
949
  table: jwtRevocation,
537
950
  select: {
@@ -543,6 +956,23 @@ const makeInternalAuthSqliteTables = (names = {}) => {
543
956
  insert: jwtRevocationInsert,
544
957
  update: jwtRevocationUpdate,
545
958
  },
959
+ oauthAccount: {
960
+ table: oauthAccount,
961
+ select: {
962
+ id: oauthAccount.id,
963
+ providerId: oauthAccount.provider_id,
964
+ providerAccountId: oauthAccount.provider_account_id,
965
+ userId: oauthAccount.user_id,
966
+ email: oauthAccount.email,
967
+ emailVerified: oauthAccount.email_verified,
968
+ createdAt: oauthAccount.created_at,
969
+ updatedAt: oauthAccount.updated_at,
970
+ unlinkedAt: oauthAccount.unlinked_at,
971
+ metadata: oauthAccount.metadata,
972
+ },
973
+ insert: oauthAccountInsert,
974
+ unlink: oauthAccountUnlink,
975
+ },
546
976
  };
547
977
  };
548
978
  const asAuthSqliteTables = (tables) => tables;
@@ -555,7 +985,13 @@ const selectSessionColumns = (tables) => tables.session.select;
555
985
  const selectVerificationColumns = (tables) => tables.verification.select;
556
986
  const selectLoginApprovalReviewColumns = (tables) => tables.loginApprovalReview.select;
557
987
  const selectTrustedDeviceColumns = (tables) => tables.trustedDevice.select;
988
+ const selectPasskeyCredentialColumns = (tables) => tables.passkeyCredential.select;
989
+ const selectTotpFactorColumns = (tables) => tables.totpFactor.select;
990
+ const selectRecoveryCodeColumns = (tables) => tables.recoveryCode.select;
991
+ const selectApiKeyColumns = (tables) => tables.apiKey.select;
992
+ const selectRefreshTokenColumns = (tables) => tables.refreshToken.select;
558
993
  const selectJwtRevocationColumns = (tables) => tables.jwtRevocation.select;
994
+ const selectOAuthAccountColumns = (tables) => tables.oauthAccount.select;
559
995
  const storageError = (entity, operation, cause) => StorageError.fromUnknown(entity, operation, cause);
560
996
  const loginApprovalReviewStoreError = (operation, cause) => new LoginApprovalReviewError({
561
997
  message: `Login approval review ${operation} failed`,
@@ -566,11 +1002,41 @@ const trustedDeviceStoreError = (operation, cause) => new TrustedDeviceError({
566
1002
  message: `Trusted device ${operation} failed`,
567
1003
  cause,
568
1004
  });
1005
+ const passkeyCredentialStoreError = (operation, cause) => new PasskeyCredentialStoreError({
1006
+ operation,
1007
+ message: `Passkey credential ${operation} failed`,
1008
+ cause,
1009
+ });
1010
+ const totpFactorStoreError = (operation, cause) => new TotpFactorStoreError({
1011
+ operation,
1012
+ message: `TOTP factor ${operation} failed`,
1013
+ cause,
1014
+ });
1015
+ const recoveryCodeStoreError = (operation, cause) => new RecoveryCodeStoreError({
1016
+ operation,
1017
+ message: `Recovery code ${operation} failed`,
1018
+ cause,
1019
+ });
1020
+ const apiKeyStoreError = (operation, cause) => new ApiKeyStoreError({
1021
+ operation,
1022
+ message: `API key ${operation} failed`,
1023
+ cause,
1024
+ });
1025
+ const refreshTokenStoreError = (operation, cause) => new RefreshTokenStoreError({
1026
+ operation,
1027
+ message: `Refresh token ${operation} failed`,
1028
+ cause,
1029
+ });
569
1030
  const jwtRevocationStoreError = (operation, cause) => new JwtError({
570
1031
  operation: "revocation",
571
1032
  message: `JWT revocation ${operation} failed`,
572
1033
  cause,
573
1034
  });
1035
+ const oauthAccountStoreError = (operation, cause) => new OAuthAccountStoreError({
1036
+ operation,
1037
+ message: `OAuth account ${operation} failed`,
1038
+ cause,
1039
+ });
574
1040
  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
575
1041
  ? Option.none()
576
1042
  : Option.some(decodeUserRow(tables, rows[0]))));
@@ -599,6 +1065,39 @@ const findTrustedDevice = (executor, tables, input, operation = "status") => exe
599
1065
  const findTrustedDeviceIncludingExpired = (executor, tables, input) => executePlan(executor, Query.select(selectTrustedDeviceColumns(tables)).pipe(Query.from(tables.trustedDevice.table), Query.where(queryEq(tables.trustedDevice.select.userId, input.userId)), Query.where(queryEq(tables.trustedDevice.select.tokenHash, input.tokenHash)))).pipe(Effect.mapError((cause) => trustedDeviceStoreError("trust", cause)), Effect.map((rows) => rows[0] === undefined
600
1066
  ? Option.none()
601
1067
  : Option.some(decodeTrustedDeviceRow(tables, rows[0]))));
1068
+ const findPasskeyCredentialByCredentialId = (executor, tables, credentialId, operation = "find") => executePlan(executor, Query.select(selectPasskeyCredentialColumns(tables)).pipe(Query.from(tables.passkeyCredential.table), Query.where(queryEq(tables.passkeyCredential.select.credentialId, credentialId)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1069
+ ? Option.none()
1070
+ : Option.some(decodePasskeyCredentialRow(tables, rows[0]))));
1071
+ const findTotpFactorById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectTotpFactorColumns(tables)).pipe(Query.from(tables.totpFactor.table), Query.where(queryEq(tables.totpFactor.select.id, id)))).pipe(Effect.mapError((cause) => totpFactorStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1072
+ ? Option.none()
1073
+ : Option.some(decodeTotpFactorRow(tables, rows[0]))));
1074
+ const findRecoveryCodeById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectRecoveryCodeColumns(tables)).pipe(Query.from(tables.recoveryCode.table), Query.where(queryEq(tables.recoveryCode.select.id, id)))).pipe(Effect.mapError((cause) => recoveryCodeStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1075
+ ? Option.none()
1076
+ : Option.some(decodeRecoveryCodeRow(tables, rows[0]))));
1077
+ const findApiKeyById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectApiKeyColumns(tables)).pipe(Query.from(tables.apiKey.table), Query.where(queryEq(tables.apiKey.select.id, id)))).pipe(Effect.mapError((cause) => apiKeyStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1078
+ ? Option.none()
1079
+ : Option.some(decodeApiKeyRow(tables, rows[0]))));
1080
+ const findApiKeyByPrefix = (executor, tables, prefix, operation = "find") => executePlan(executor, Query.select(selectApiKeyColumns(tables)).pipe(Query.from(tables.apiKey.table), Query.where(queryEq(tables.apiKey.select.prefix, prefix)))).pipe(Effect.mapError((cause) => apiKeyStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1081
+ ? Option.none()
1082
+ : Option.some(decodeApiKeyRow(tables, rows[0]))));
1083
+ const findRefreshTokenById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.id, id)))).pipe(Effect.mapError((cause) => refreshTokenStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1084
+ ? Option.none()
1085
+ : Option.some(decodeRefreshTokenRow(tables, rows[0]))));
1086
+ const findRefreshTokenBySecretHash = (executor, tables, hash, operation = "find") => executePlan(executor, Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.secretHash, hash)))).pipe(Effect.mapError((cause) => refreshTokenStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1087
+ ? Option.none()
1088
+ : Option.some(decodeRefreshTokenRow(tables, rows[0]))));
1089
+ const findOAuthAccountById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectOAuthAccountColumns(tables)).pipe(Query.from(tables.oauthAccount.table), Query.where(queryEq(tables.oauthAccount.select.id, id)))).pipe(Effect.mapError((cause) => oauthAccountStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1090
+ ? Option.none()
1091
+ : Option.some(decodeOAuthAccountRow(tables, rows[0]))));
1092
+ const findOAuthAccountByProviderAccount = (executor, tables, input, operation = "find") => {
1093
+ const base = Query.select(selectOAuthAccountColumns(tables)).pipe(Query.from(tables.oauthAccount.table), Query.where(queryEq(tables.oauthAccount.select.providerId, input.providerId)), Query.where(queryEq(tables.oauthAccount.select.providerAccountId, input.providerAccountId)));
1094
+ const query = input.includeUnlinked === true
1095
+ ? base
1096
+ : base.pipe(Query.where(queryIsNull(tables.oauthAccount.select.unlinkedAt)));
1097
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => oauthAccountStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
1098
+ ? Option.none()
1099
+ : Option.some(decodeOAuthAccountRow(tables, rows[0]))));
1100
+ };
602
1101
  export const makeEffectQbSqliteUserStore = (executor, options = {}) => {
603
1102
  const tables = options.tables ?? authSqliteTables;
604
1103
  return UserStore.make({
@@ -705,6 +1204,155 @@ export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) =>
705
1204
  : Effect.fail(trustedDeviceStoreError("trust", "missing row")))),
706
1205
  });
707
1206
  };
1207
+ export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {}) => {
1208
+ const tables = options.tables ?? authSqliteTables;
1209
+ return PasskeyCredentialStore.make({
1210
+ insert: (row) => executePlan(executor, queryInsert(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.insert(row)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("insert", cause)), Effect.asVoid),
1211
+ findByCredentialId: (credentialId) => findPasskeyCredentialByCredentialId(executor, tables, credentialId),
1212
+ listByUser: (input) => {
1213
+ const base = Query.select(selectPasskeyCredentialColumns(tables)).pipe(Query.from(tables.passkeyCredential.table), Query.where(queryEq(tables.passkeyCredential.select.userId, input.userId)));
1214
+ const query = input.includeRevoked === true
1215
+ ? base
1216
+ : base.pipe(Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)));
1217
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodePasskeyCredentialRow(tables, row))));
1218
+ },
1219
+ updateSignCount: (input) => executePlan(executor, queryUpdate(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.updateSignCount(input))).pipe(Query.where(queryEq(tables.passkeyCredential.select.credentialId, input.credentialId)), Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)), Query.returning(selectPasskeyCredentialColumns(tables)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("update-sign-count", cause)), Effect.map((rows) => rows[0] === undefined
1220
+ ? Option.none()
1221
+ : Option.some(decodePasskeyCredentialRow(tables, rows[0])))),
1222
+ revoke: (input) => findPasskeyCredentialByCredentialId(executor, tables, input.credentialId, "revoke").pipe(Effect.flatMap((row) => {
1223
+ if (Option.isNone(row)) {
1224
+ return Effect.void;
1225
+ }
1226
+ return executePlan(executor, queryUpdate(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.revoke(input, row.value))).pipe(Query.where(queryEq(tables.passkeyCredential.select.credentialId, input.credentialId)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("revoke", cause)), Effect.asVoid);
1227
+ })),
1228
+ });
1229
+ };
1230
+ export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
1231
+ const tables = options.tables ?? authSqliteTables;
1232
+ return TotpFactorStore.make({
1233
+ insert: (row) => executePlan(executor, queryInsert(tables.totpFactor.table, mutationValues(tables.totpFactor.insert(row)))).pipe(Effect.mapError((cause) => totpFactorStoreError("insert", cause)), Effect.asVoid),
1234
+ findById: (id) => findTotpFactorById(executor, tables, id),
1235
+ listByUser: (input) => {
1236
+ const byUser = Query.select(selectTotpFactorColumns(tables)).pipe(Query.from(tables.totpFactor.table), Query.where(queryEq(tables.totpFactor.select.userId, input.userId)));
1237
+ const byRevoked = input.includeRevoked === true
1238
+ ? byUser
1239
+ : byUser.pipe(Query.where(queryIsNull(tables.totpFactor.select.revokedAt)));
1240
+ const query = input.includeUnconfirmed === true
1241
+ ? byRevoked
1242
+ : byRevoked.pipe(Query.where(queryGt(tables.totpFactor.select.confirmedAt, 0)));
1243
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => totpFactorStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeTotpFactorRow(tables, row))));
1244
+ },
1245
+ confirm: (input) => executePlan(executor, queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.confirm(input))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.id)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.returning(selectTotpFactorColumns(tables)))).pipe(Effect.mapError((cause) => totpFactorStoreError("confirm", cause)), Effect.map((rows) => rows[0] === undefined
1246
+ ? Option.none()
1247
+ : Option.some(decodeTotpFactorRow(tables, rows[0])))),
1248
+ markUsed: (input) => executePlan(executor, queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.markUsed(input))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.id)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.where(queryGt(tables.totpFactor.select.confirmedAt, 0)), Query.returning(selectTotpFactorColumns(tables)))).pipe(Effect.mapError((cause) => totpFactorStoreError("mark-used", cause)), Effect.map((rows) => rows[0] === undefined
1249
+ ? Option.none()
1250
+ : Option.some(decodeTotpFactorRow(tables, rows[0])))),
1251
+ revoke: (input) => findTotpFactorById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
1252
+ if (Option.isNone(row)) {
1253
+ return Effect.void;
1254
+ }
1255
+ return executePlan(executor, queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.revoke(input, row.value))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.id)))).pipe(Effect.mapError((cause) => totpFactorStoreError("revoke", cause)), Effect.asVoid);
1256
+ })),
1257
+ });
1258
+ };
1259
+ export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
1260
+ const tables = options.tables ?? authSqliteTables;
1261
+ return RecoveryCodeStore.make({
1262
+ insertMany: (rows) => Effect.forEach(rows, (row) => executePlan(executor, queryInsert(tables.recoveryCode.table, mutationValues(tables.recoveryCode.insert(row)))).pipe(Effect.mapError((cause) => recoveryCodeStoreError("insert", cause)), Effect.asVoid), { discard: true }),
1263
+ findById: (id) => findRecoveryCodeById(executor, tables, id),
1264
+ listByUser: (input) => {
1265
+ const byUser = Query.select(selectRecoveryCodeColumns(tables)).pipe(Query.from(tables.recoveryCode.table), Query.where(queryEq(tables.recoveryCode.select.userId, input.userId)));
1266
+ const byUsed = input.includeUsed === true
1267
+ ? byUser
1268
+ : byUser.pipe(Query.where(queryIsNull(tables.recoveryCode.select.usedAt)));
1269
+ const query = input.includeRevoked === true
1270
+ ? byUsed
1271
+ : byUsed.pipe(Query.where(queryIsNull(tables.recoveryCode.select.revokedAt)));
1272
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => recoveryCodeStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRecoveryCodeRow(tables, row))));
1273
+ },
1274
+ markUsed: (input) => executePlan(executor, queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.markUsed(input))).pipe(Query.where(queryEq(tables.recoveryCode.select.id, input.id)), Query.where(queryIsNull(tables.recoveryCode.select.usedAt)), Query.where(queryIsNull(tables.recoveryCode.select.revokedAt)), Query.returning({ id: tables.recoveryCode.select.id }))).pipe(Effect.mapError((cause) => recoveryCodeStoreError("mark-used", cause)), Effect.flatMap((rows) => rows[0] === undefined
1275
+ ? Effect.succeed(Option.none())
1276
+ : findRecoveryCodeById(executor, tables, input.id, "mark-used"))),
1277
+ revoke: (input) => findRecoveryCodeById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
1278
+ if (Option.isNone(row)) {
1279
+ return Effect.void;
1280
+ }
1281
+ return executePlan(executor, queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.revoke(input, row.value))).pipe(Query.where(queryEq(tables.recoveryCode.select.id, input.id)))).pipe(Effect.mapError((cause) => recoveryCodeStoreError("revoke", cause)), Effect.asVoid);
1282
+ })),
1283
+ });
1284
+ };
1285
+ export const makeEffectQbSqliteApiKeyStore = (executor, options = {}) => {
1286
+ const tables = options.tables ?? authSqliteTables;
1287
+ return ApiKeyStore.make({
1288
+ insert: (row) => executePlan(executor, queryInsert(tables.apiKey.table, mutationValues(tables.apiKey.insert(row)))).pipe(Effect.mapError((cause) => apiKeyStoreError("insert", cause)), Effect.asVoid),
1289
+ findById: (id) => findApiKeyById(executor, tables, id),
1290
+ findByPrefix: (prefix) => findApiKeyByPrefix(executor, tables, prefix),
1291
+ listByUser: (input) => {
1292
+ const base = Query.select(selectApiKeyColumns(tables)).pipe(Query.from(tables.apiKey.table), Query.where(queryEq(tables.apiKey.select.userId, input.userId)));
1293
+ const query = input.includeRevoked === true
1294
+ ? base
1295
+ : base.pipe(Query.where(queryIsNull(tables.apiKey.select.revokedAt)));
1296
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => apiKeyStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeApiKeyRow(tables, row))));
1297
+ },
1298
+ markUsed: (input) => executePlan(executor, queryUpdate(tables.apiKey.table, mutationValues(tables.apiKey.markUsed(input))).pipe(Query.where(queryEq(tables.apiKey.select.id, input.id)), Query.where(queryIsNull(tables.apiKey.select.revokedAt)), Query.returning(selectApiKeyColumns(tables)))).pipe(Effect.mapError((cause) => apiKeyStoreError("mark-used", cause)), Effect.map((rows) => rows[0] === undefined
1299
+ ? Option.none()
1300
+ : Option.some(decodeApiKeyRow(tables, rows[0])))),
1301
+ revoke: (input) => findApiKeyById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
1302
+ if (Option.isNone(row)) {
1303
+ return Effect.void;
1304
+ }
1305
+ return executePlan(executor, queryUpdate(tables.apiKey.table, mutationValues(tables.apiKey.revoke(input, row.value))).pipe(Query.where(queryEq(tables.apiKey.select.id, input.id)))).pipe(Effect.mapError((cause) => apiKeyStoreError("revoke", cause)), Effect.asVoid);
1306
+ })),
1307
+ });
1308
+ };
1309
+ export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
1310
+ const tables = options.tables ?? authSqliteTables;
1311
+ return RefreshTokenStore.make({
1312
+ insert: (row) => executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(row)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("insert", cause)), Effect.asVoid),
1313
+ findById: (id) => findRefreshTokenById(executor, tables, id),
1314
+ findBySecretHash: (hash) => findRefreshTokenBySecretHash(executor, tables, hash),
1315
+ listByUser: (input) => {
1316
+ const base = Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.userId, input.userId)));
1317
+ const query = input.includeRevoked === true
1318
+ ? base
1319
+ : base.pipe(Query.where(queryIsNull(tables.refreshToken.select.revokedAt)));
1320
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => refreshTokenStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRefreshTokenRow(tables, row))));
1321
+ },
1322
+ listByFamily: (input) => {
1323
+ const base = Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.familyId, input.familyId)));
1324
+ const query = input.includeRevoked === true
1325
+ ? base
1326
+ : base.pipe(Query.where(queryIsNull(tables.refreshToken.select.revokedAt)));
1327
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => refreshTokenStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRefreshTokenRow(tables, row))));
1328
+ },
1329
+ rotate: (input) => executePlan(executor, queryUpdate(tables.refreshToken.table, mutationValues(tables.refreshToken.rotate(input))).pipe(Query.where(queryEq(tables.refreshToken.select.id, input.id)), Query.where(queryIsNull(tables.refreshToken.select.rotatedAt)), Query.where(queryIsNull(tables.refreshToken.select.revokedAt)), Query.returning({ id: tables.refreshToken.select.id }))).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.flatMap((rows) => {
1330
+ const [updated] = rows;
1331
+ if (updated === undefined) {
1332
+ return Effect.succeed(Option.none());
1333
+ }
1334
+ return findRefreshTokenById(executor, tables, RefreshTokenId(updated.id), "rotate").pipe(Effect.flatMap((previous) => {
1335
+ if (Option.isNone(previous)) {
1336
+ return Effect.succeed(Option.none());
1337
+ }
1338
+ return executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(input.replacement)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.as(Option.some({
1339
+ previous: previous.value,
1340
+ replacement: input.replacement,
1341
+ })));
1342
+ }));
1343
+ })),
1344
+ markReuseDetected: (input) => executePlan(executor, queryUpdate(tables.refreshToken.table, mutationValues(tables.refreshToken.markReuseDetected(input))).pipe(Query.where(queryEq(tables.refreshToken.select.id, input.id)), Query.returning(selectRefreshTokenColumns(tables)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("reuse", cause)), Effect.map((rows) => rows[0] === undefined
1345
+ ? Option.none()
1346
+ : Option.some(decodeRefreshTokenRow(tables, rows[0])))),
1347
+ revoke: (input) => findRefreshTokenById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
1348
+ if (Option.isNone(row)) {
1349
+ return Effect.void;
1350
+ }
1351
+ return executePlan(executor, queryUpdate(tables.refreshToken.table, mutationValues(tables.refreshToken.revoke(input, row.value))).pipe(Query.where(queryEq(tables.refreshToken.select.id, input.id)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("revoke", cause)), Effect.asVoid);
1352
+ })),
1353
+ revokeFamily: (input) => executePlan(executor, Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.familyId, input.familyId)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("revoke", cause)), Effect.flatMap((rows) => Effect.forEach(rows.map((row) => decodeRefreshTokenRow(tables, row)), (row) => executePlan(executor, queryUpdate(tables.refreshToken.table, mutationValues(tables.refreshToken.revokeFamily(input, row))).pipe(Query.where(queryEq(tables.refreshToken.select.id, row.id)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("revoke", cause)), Effect.asVoid), { discard: true }))),
1354
+ });
1355
+ };
708
1356
  export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) => {
709
1357
  const tables = options.tables ?? authSqliteTables;
710
1358
  return JwtRevocationStore.make({
@@ -719,6 +1367,32 @@ export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) =>
719
1367
  find: (jwtId) => findJwtRevocation(executor, tables, jwtId),
720
1368
  });
721
1369
  };
1370
+ export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
1371
+ const tables = options.tables ?? authSqliteTables;
1372
+ return OAuthAccountStore.make({
1373
+ insert: (row) => executePlan(executor, queryInsert(tables.oauthAccount.table, mutationValues(tables.oauthAccount.insert(row)))).pipe(Effect.mapError((cause) => oauthAccountStoreError("insert", cause)), Effect.asVoid),
1374
+ findById: (id) => findOAuthAccountById(executor, tables, id),
1375
+ findByProviderAccount: (input) => findOAuthAccountByProviderAccount(executor, tables, input),
1376
+ listByUser: (input) => {
1377
+ const base = Query.select(selectOAuthAccountColumns(tables)).pipe(Query.from(tables.oauthAccount.table), Query.where(queryEq(tables.oauthAccount.select.userId, input.userId)));
1378
+ const query = input.includeUnlinked === true
1379
+ ? base
1380
+ : base.pipe(Query.where(queryIsNull(tables.oauthAccount.select.unlinkedAt)));
1381
+ return executePlan(executor, query).pipe(Effect.mapError((cause) => oauthAccountStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeOAuthAccountRow(tables, row))));
1382
+ },
1383
+ unlink: (input) => findOAuthAccountByProviderAccount(executor, tables, {
1384
+ providerId: input.providerId,
1385
+ providerAccountId: input.providerAccountId,
1386
+ }, "unlink").pipe(Effect.flatMap((row) => {
1387
+ if (Option.isNone(row)) {
1388
+ return Effect.succeed(Option.none());
1389
+ }
1390
+ return executePlan(executor, queryUpdate(tables.oauthAccount.table, mutationValues(tables.oauthAccount.unlink(input, row.value))).pipe(Query.where(queryEq(tables.oauthAccount.select.id, row.value.id)), Query.returning(selectOAuthAccountColumns(tables)))).pipe(Effect.mapError((cause) => oauthAccountStoreError("unlink", cause)), Effect.map((rows) => rows[0] === undefined
1391
+ ? Option.none()
1392
+ : Option.some(decodeOAuthAccountRow(tables, rows[0]))));
1393
+ })),
1394
+ });
1395
+ };
722
1396
  export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
723
1397
  userStore: makeEffectQbSqliteUserStore(executor, options),
724
1398
  credentialStore: makeEffectQbSqliteCredentialStore(executor, options),
@@ -726,9 +1400,15 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
726
1400
  verificationStore: makeEffectQbSqliteVerificationStore(executor, options),
727
1401
  loginApprovalReviewStore: makeEffectQbSqliteLoginApprovalReviewStore(executor, options),
728
1402
  trustedDeviceStore: makeEffectQbSqliteTrustedDeviceStore(executor, options),
1403
+ passkeyCredentialStore: makeEffectQbSqlitePasskeyCredentialStore(executor, options),
1404
+ totpFactorStore: makeEffectQbSqliteTotpFactorStore(executor, options),
1405
+ recoveryCodeStore: makeEffectQbSqliteRecoveryCodeStore(executor, options),
1406
+ apiKeyStore: makeEffectQbSqliteApiKeyStore(executor, options),
1407
+ refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, options),
729
1408
  jwtRevocationStore: makeEffectQbSqliteJwtRevocationStore(executor, options),
1409
+ oauthAccountStore: makeEffectQbSqliteOAuthAccountStore(executor, options),
730
1410
  });
731
- 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(JwtRevocationStore)(makeEffectQbSqliteJwtRevocationStore(executor, options)));
1411
+ 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)));
732
1412
  export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
733
1413
  driver: Executor.driver((query) => Effect.tryPromise({
734
1414
  try: () => database