@effect-auth/core 0.1.0-alpha.7 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -52
- package/dist/EffectQbSqliteStorage.d.ts +269 -2
- package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
- package/dist/EffectQbSqliteStorage.js +816 -3
- package/dist/EffectQbSqliteStorage.js.map +1 -1
- package/dist/OAuth.d.ts +55 -0
- package/dist/OAuth.d.ts.map +1 -1
- package/dist/OAuth.js +58 -0
- package/dist/OAuth.js.map +1 -1
- package/dist/StorageMigrations.d.ts +2 -1
- package/dist/StorageMigrations.d.ts.map +1 -1
- package/dist/StorageMigrations.js +24 -0
- package/dist/StorageMigrations.js.map +1 -1
- package/dist/cloudflare/RateLimitDurableObject.d.ts +1 -1
- package/migrations/0012_auth_oauth_provider_token_vault.sql +20 -0
- package/package.json +2 -14
- package/dist/D1Kysely.d.ts +0 -43
- package/dist/D1Kysely.d.ts.map +0 -1
- package/dist/D1Kysely.js +0 -78
- package/dist/D1Kysely.js.map +0 -1
- package/dist/KyselyStorage.d.ts +0 -217
- package/dist/KyselyStorage.d.ts.map +0 -1
- package/dist/KyselyStorage.js +0 -1383
- package/dist/KyselyStorage.js.map +0 -1
|
@@ -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 {
|
|
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, OAuthProviderTokenVault, OAuthProviderTokenVaultError, } 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,100 @@ 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
|
+
});
|
|
166
|
+
const makeAuthOAuthProviderTokenVaultTable = (name) => Table.make(name, {
|
|
167
|
+
account_id: Column.text().pipe(Column.primaryKey),
|
|
168
|
+
user_id: Column.text(),
|
|
169
|
+
provider_id: Column.text(),
|
|
170
|
+
provider_account_id: Column.text(),
|
|
171
|
+
access_token_ciphertext: Column.text(),
|
|
172
|
+
refresh_token_ciphertext: Column.text().pipe(Column.nullable),
|
|
173
|
+
id_token_ciphertext: Column.text().pipe(Column.nullable),
|
|
174
|
+
token_type: Column.text(),
|
|
175
|
+
scopes: Column.text().pipe(Column.nullable),
|
|
176
|
+
expires_at: Column.int().pipe(Column.nullable),
|
|
177
|
+
updated_at: Column.int(),
|
|
178
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
179
|
+
revocation_reason: Column.text().pipe(Column.nullable),
|
|
180
|
+
});
|
|
87
181
|
const authSqliteTablesTypeId = Symbol.for("@effect-auth/core/EffectQbSqliteStorage/AuthSqliteTables");
|
|
88
182
|
const jsonEncode = (value) => value === undefined ? null : JSON.stringify(value);
|
|
89
183
|
const jsonDecode = (value) => {
|
|
@@ -120,6 +214,7 @@ const executePlan = (executor, plan) => executor.execute(plan);
|
|
|
120
214
|
const omitUndefined = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
121
215
|
const sessionMetadataEnvelopeKey = "__effectAuthSession";
|
|
122
216
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
217
|
+
const metadataWithReason = (metadata, key, reason) => reason === undefined ? metadata : { ...metadata, [key]: reason };
|
|
123
218
|
const isLoginRiskLevel = (value) => value === "unknown" ||
|
|
124
219
|
value === "low" ||
|
|
125
220
|
value === "medium" ||
|
|
@@ -279,6 +374,96 @@ const decodeDefaultTrustedDeviceRow = (row) => omitUndefined({
|
|
|
279
374
|
const decodeTrustedDeviceRow = (tables, row) => tables.trustedDevice.decode === undefined
|
|
280
375
|
? decodeDefaultTrustedDeviceRow(row)
|
|
281
376
|
: tables.trustedDevice.decode(row);
|
|
377
|
+
const decodePasskeyTransports = (value) => {
|
|
378
|
+
const decoded = jsonDecode(value);
|
|
379
|
+
return Array.isArray(decoded) ? decoded : undefined;
|
|
380
|
+
};
|
|
381
|
+
const decodeDefaultPasskeyCredentialRow = (row) => omitUndefined({
|
|
382
|
+
id: CredentialId(row.id),
|
|
383
|
+
userId: UserId(row.userId),
|
|
384
|
+
credentialId: PasskeyCredentialId(row.credentialId),
|
|
385
|
+
publicKey: row.publicKey,
|
|
386
|
+
signCount: Number(row.signCount),
|
|
387
|
+
transports: decodePasskeyTransports(row.transports),
|
|
388
|
+
backedUp: row.backedUp === null || row.backedUp === undefined
|
|
389
|
+
? undefined
|
|
390
|
+
: decodeBoolean(row.backedUp),
|
|
391
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
392
|
+
lastUsedAt: optionalUnixMillis(row.lastUsedAt),
|
|
393
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
394
|
+
metadata: jsonDecode(row.metadata),
|
|
395
|
+
});
|
|
396
|
+
const decodePasskeyCredentialRow = (tables, row) => tables.passkeyCredential.decode === undefined
|
|
397
|
+
? decodeDefaultPasskeyCredentialRow(row)
|
|
398
|
+
: tables.passkeyCredential.decode(row);
|
|
399
|
+
const decodeDefaultTotpFactorRow = (row) => omitUndefined({
|
|
400
|
+
id: CredentialId(row.id),
|
|
401
|
+
userId: UserId(row.userId),
|
|
402
|
+
secret: Redacted.make(TotpSecret(row.secret)),
|
|
403
|
+
algorithm: row.algorithm,
|
|
404
|
+
digits: Number(row.digits),
|
|
405
|
+
period: Number(row.period),
|
|
406
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
407
|
+
confirmedAt: optionalUnixMillis(row.confirmedAt),
|
|
408
|
+
lastUsedAt: optionalUnixMillis(row.lastUsedAt),
|
|
409
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
410
|
+
metadata: jsonDecode(row.metadata),
|
|
411
|
+
});
|
|
412
|
+
const decodeTotpFactorRow = (tables, row) => tables.totpFactor.decode === undefined
|
|
413
|
+
? decodeDefaultTotpFactorRow(row)
|
|
414
|
+
: tables.totpFactor.decode(row);
|
|
415
|
+
const decodeDefaultRecoveryCodeRow = (row) => omitUndefined({
|
|
416
|
+
id: CredentialId(row.id),
|
|
417
|
+
userId: UserId(row.userId),
|
|
418
|
+
codeHash: RecoveryCodeHash(row.codeHash),
|
|
419
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
420
|
+
usedAt: optionalUnixMillis(row.usedAt),
|
|
421
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
422
|
+
metadata: jsonDecode(row.metadata),
|
|
423
|
+
});
|
|
424
|
+
const decodeRecoveryCodeRow = (tables, row) => tables.recoveryCode.decode === undefined
|
|
425
|
+
? decodeDefaultRecoveryCodeRow(row)
|
|
426
|
+
: tables.recoveryCode.decode(row);
|
|
427
|
+
const decodeStringArray = (value) => {
|
|
428
|
+
const decoded = jsonDecode(value);
|
|
429
|
+
return Array.isArray(decoded) ? decoded.map(String) : [];
|
|
430
|
+
};
|
|
431
|
+
const decodeOptionalStringArray = (value) => {
|
|
432
|
+
const decoded = jsonDecode(value);
|
|
433
|
+
return Array.isArray(decoded) ? decoded.map(String) : undefined;
|
|
434
|
+
};
|
|
435
|
+
const decodeDefaultApiKeyRow = (row) => omitUndefined({
|
|
436
|
+
id: ApiKeyId(row.id),
|
|
437
|
+
userId: UserId(row.userId),
|
|
438
|
+
prefix: ApiKeyPrefix(row.prefix),
|
|
439
|
+
secretHash: ApiKeySecretHash(row.secretHash),
|
|
440
|
+
scopes: decodeStringArray(row.scopes),
|
|
441
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
442
|
+
expiresAt: optionalUnixMillis(row.expiresAt),
|
|
443
|
+
lastUsedAt: optionalUnixMillis(row.lastUsedAt),
|
|
444
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
445
|
+
metadata: jsonDecode(row.metadata),
|
|
446
|
+
});
|
|
447
|
+
const decodeApiKeyRow = (tables, row) => tables.apiKey.decode === undefined
|
|
448
|
+
? decodeDefaultApiKeyRow(row)
|
|
449
|
+
: tables.apiKey.decode(row);
|
|
450
|
+
const decodeDefaultRefreshTokenRow = (row) => omitUndefined({
|
|
451
|
+
id: RefreshTokenId(row.id),
|
|
452
|
+
familyId: RefreshTokenFamilyId(row.familyId),
|
|
453
|
+
userId: UserId(row.userId),
|
|
454
|
+
secretHash: RefreshTokenSecretHash(row.secretHash),
|
|
455
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
456
|
+
expiresAt: UnixMillis(Number(row.expiresAt)),
|
|
457
|
+
lastUsedAt: optionalUnixMillis(row.lastUsedAt),
|
|
458
|
+
rotatedAt: optionalUnixMillis(row.rotatedAt),
|
|
459
|
+
replacedById: row.replacedById === null ? undefined : RefreshTokenId(row.replacedById),
|
|
460
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
461
|
+
reuseDetectedAt: optionalUnixMillis(row.reuseDetectedAt),
|
|
462
|
+
metadata: jsonDecode(row.metadata),
|
|
463
|
+
});
|
|
464
|
+
const decodeRefreshTokenRow = (tables, row) => tables.refreshToken.decode === undefined
|
|
465
|
+
? decodeDefaultRefreshTokenRow(row)
|
|
466
|
+
: tables.refreshToken.decode(row);
|
|
282
467
|
const decodeDefaultJwtRevocationRow = (row) => omitUndefined({
|
|
283
468
|
jwtId: row.jwtId,
|
|
284
469
|
revokedAt: UnixMillis(Number(row.revokedAt)),
|
|
@@ -288,6 +473,53 @@ const decodeDefaultJwtRevocationRow = (row) => omitUndefined({
|
|
|
288
473
|
const decodeJwtRevocationRow = (tables, row) => tables.jwtRevocation.decode === undefined
|
|
289
474
|
? decodeDefaultJwtRevocationRow(row)
|
|
290
475
|
: tables.jwtRevocation.decode(row);
|
|
476
|
+
const decodeDefaultOAuthAccountRow = (row) => omitUndefined({
|
|
477
|
+
id: OAuthAccountId(row.id),
|
|
478
|
+
providerId: OAuthProviderId(row.providerId),
|
|
479
|
+
providerAccountId: row.providerAccountId,
|
|
480
|
+
userId: UserId(row.userId),
|
|
481
|
+
email: row.email === null ? undefined : Email(row.email),
|
|
482
|
+
emailVerified: row.emailVerified === null || row.emailVerified === undefined
|
|
483
|
+
? undefined
|
|
484
|
+
: decodeBoolean(row.emailVerified),
|
|
485
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
486
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
487
|
+
unlinkedAt: optionalUnixMillis(row.unlinkedAt),
|
|
488
|
+
metadata: jsonDecode(row.metadata),
|
|
489
|
+
});
|
|
490
|
+
const decodeOAuthAccountRow = (tables, row) => tables.oauthAccount.decode === undefined
|
|
491
|
+
? decodeDefaultOAuthAccountRow(row)
|
|
492
|
+
: tables.oauthAccount.decode(row);
|
|
493
|
+
const decodeDefaultOAuthProviderTokenVaultRow = (row) => omitUndefined({
|
|
494
|
+
accountId: OAuthAccountId(row.accountId),
|
|
495
|
+
userId: UserId(row.userId),
|
|
496
|
+
providerId: OAuthProviderId(row.providerId),
|
|
497
|
+
providerAccountId: row.providerAccountId,
|
|
498
|
+
accessTokenCiphertext: Redacted.make(row.accessTokenCiphertext),
|
|
499
|
+
refreshTokenCiphertext: row.refreshTokenCiphertext === null
|
|
500
|
+
? undefined
|
|
501
|
+
: Redacted.make(row.refreshTokenCiphertext),
|
|
502
|
+
idTokenCiphertext: row.idTokenCiphertext === null
|
|
503
|
+
? undefined
|
|
504
|
+
: Redacted.make(row.idTokenCiphertext),
|
|
505
|
+
tokenType: row.tokenType,
|
|
506
|
+
scopes: decodeOptionalStringArray(row.scopes),
|
|
507
|
+
expiresAt: optionalUnixMillis(row.expiresAt),
|
|
508
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
509
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
510
|
+
revocationReason: row.revocationReason ?? undefined,
|
|
511
|
+
});
|
|
512
|
+
const decodeOAuthProviderTokenVaultRow = (tables, row) => tables.oauthProviderTokenVault.decode === undefined
|
|
513
|
+
? decodeDefaultOAuthProviderTokenVaultRow(row)
|
|
514
|
+
: tables.oauthProviderTokenVault.decode(row);
|
|
515
|
+
const providerTokenVaultTokens = (record) => omitUndefined({
|
|
516
|
+
accessTokenCiphertext: record.accessTokenCiphertext,
|
|
517
|
+
refreshTokenCiphertext: record.refreshTokenCiphertext,
|
|
518
|
+
idTokenCiphertext: record.idTokenCiphertext,
|
|
519
|
+
tokenType: record.tokenType,
|
|
520
|
+
scopes: record.scopes === undefined ? undefined : [...record.scopes],
|
|
521
|
+
expiresAt: record.expiresAt,
|
|
522
|
+
});
|
|
291
523
|
const userInsert = (row) => ({
|
|
292
524
|
id: row.id,
|
|
293
525
|
email: row.email,
|
|
@@ -408,12 +640,163 @@ const trustedDeviceUpdate = (input, existing) => ({
|
|
|
408
640
|
expires_at: Number(input.expiresAt),
|
|
409
641
|
metadata: jsonEncode(input.metadata ?? existing.metadata),
|
|
410
642
|
});
|
|
643
|
+
const passkeyCredentialInsert = (row) => ({
|
|
644
|
+
id: row.id,
|
|
645
|
+
user_id: row.userId,
|
|
646
|
+
credential_id: row.credentialId,
|
|
647
|
+
public_key: row.publicKey,
|
|
648
|
+
sign_count: row.signCount,
|
|
649
|
+
transports: jsonEncode(row.transports),
|
|
650
|
+
backed_up: row.backedUp === undefined ? null : row.backedUp ? 1 : 0,
|
|
651
|
+
created_at: Number(row.createdAt),
|
|
652
|
+
last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
|
|
653
|
+
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
654
|
+
metadata: jsonEncode(row.metadata),
|
|
655
|
+
});
|
|
656
|
+
const passkeyCredentialUpdateSignCount = (input) => omitUndefined({
|
|
657
|
+
sign_count: input.signCount,
|
|
658
|
+
last_used_at: Number(input.lastUsedAt),
|
|
659
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
660
|
+
});
|
|
661
|
+
const passkeyCredentialRevoke = (input, row) => ({
|
|
662
|
+
revoked_at: Number(input.revokedAt),
|
|
663
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
664
|
+
});
|
|
665
|
+
const totpFactorInsert = (row) => ({
|
|
666
|
+
id: row.id,
|
|
667
|
+
user_id: row.userId,
|
|
668
|
+
secret: Redacted.value(row.secret),
|
|
669
|
+
algorithm: row.algorithm,
|
|
670
|
+
digits: row.digits,
|
|
671
|
+
period: row.period,
|
|
672
|
+
created_at: Number(row.createdAt),
|
|
673
|
+
confirmed_at: row.confirmedAt === undefined ? null : Number(row.confirmedAt),
|
|
674
|
+
last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
|
|
675
|
+
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
676
|
+
metadata: jsonEncode(row.metadata),
|
|
677
|
+
});
|
|
678
|
+
const totpFactorConfirm = (input) => omitUndefined({
|
|
679
|
+
confirmed_at: Number(input.confirmedAt),
|
|
680
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
681
|
+
});
|
|
682
|
+
const totpFactorMarkUsed = (input) => omitUndefined({
|
|
683
|
+
last_used_at: Number(input.lastUsedAt),
|
|
684
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
685
|
+
});
|
|
686
|
+
const totpFactorRevoke = (input, row) => ({
|
|
687
|
+
revoked_at: Number(input.revokedAt),
|
|
688
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
689
|
+
});
|
|
690
|
+
const recoveryCodeInsert = (row) => ({
|
|
691
|
+
id: row.id,
|
|
692
|
+
user_id: row.userId,
|
|
693
|
+
code_hash: row.codeHash,
|
|
694
|
+
created_at: Number(row.createdAt),
|
|
695
|
+
used_at: row.usedAt === undefined ? null : Number(row.usedAt),
|
|
696
|
+
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
697
|
+
metadata: jsonEncode(row.metadata),
|
|
698
|
+
});
|
|
699
|
+
const recoveryCodeMarkUsed = (input) => omitUndefined({
|
|
700
|
+
used_at: Number(input.usedAt),
|
|
701
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
702
|
+
});
|
|
703
|
+
const recoveryCodeRevoke = (input, row) => ({
|
|
704
|
+
revoked_at: Number(input.revokedAt),
|
|
705
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
706
|
+
});
|
|
707
|
+
const apiKeyInsert = (row) => ({
|
|
708
|
+
id: row.id,
|
|
709
|
+
user_id: row.userId,
|
|
710
|
+
prefix: row.prefix,
|
|
711
|
+
secret_hash: row.secretHash,
|
|
712
|
+
scopes: JSON.stringify(row.scopes),
|
|
713
|
+
created_at: Number(row.createdAt),
|
|
714
|
+
expires_at: row.expiresAt === undefined ? null : Number(row.expiresAt),
|
|
715
|
+
last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
|
|
716
|
+
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
717
|
+
metadata: jsonEncode(row.metadata),
|
|
718
|
+
});
|
|
719
|
+
const apiKeyMarkUsed = (input) => omitUndefined({
|
|
720
|
+
last_used_at: Number(input.lastUsedAt),
|
|
721
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
722
|
+
});
|
|
723
|
+
const apiKeyRevoke = (input, row) => ({
|
|
724
|
+
revoked_at: Number(input.revokedAt),
|
|
725
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
726
|
+
});
|
|
727
|
+
const refreshTokenInsert = (row) => ({
|
|
728
|
+
id: row.id,
|
|
729
|
+
family_id: row.familyId,
|
|
730
|
+
user_id: row.userId,
|
|
731
|
+
secret_hash: row.secretHash,
|
|
732
|
+
created_at: Number(row.createdAt),
|
|
733
|
+
expires_at: Number(row.expiresAt),
|
|
734
|
+
last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
|
|
735
|
+
rotated_at: row.rotatedAt === undefined ? null : Number(row.rotatedAt),
|
|
736
|
+
replaced_by_id: row.replacedById ?? null,
|
|
737
|
+
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
738
|
+
reuse_detected_at: row.reuseDetectedAt === undefined ? null : Number(row.reuseDetectedAt),
|
|
739
|
+
metadata: jsonEncode(row.metadata),
|
|
740
|
+
});
|
|
741
|
+
const refreshTokenRotate = (input) => ({
|
|
742
|
+
last_used_at: Number(input.rotatedAt),
|
|
743
|
+
rotated_at: Number(input.rotatedAt),
|
|
744
|
+
replaced_by_id: input.replacement.id,
|
|
745
|
+
});
|
|
746
|
+
const refreshTokenMarkReuseDetected = (input) => ({
|
|
747
|
+
reuse_detected_at: Number(input.reuseDetectedAt),
|
|
748
|
+
});
|
|
749
|
+
const refreshTokenRevoke = (input, row) => ({
|
|
750
|
+
revoked_at: Number(input.revokedAt),
|
|
751
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
752
|
+
});
|
|
753
|
+
const refreshTokenRevokeFamily = (input, row) => ({
|
|
754
|
+
revoked_at: Number(input.revokedAt),
|
|
755
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "familyRevokeReason", input.reason)),
|
|
756
|
+
});
|
|
411
757
|
const jwtRevocationInsert = (record) => ({
|
|
412
758
|
jwt_id: record.jwtId,
|
|
413
759
|
revoked_at: Number(record.revokedAt),
|
|
414
760
|
expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
|
|
415
761
|
reason: record.reason ?? null,
|
|
416
762
|
});
|
|
763
|
+
const oauthAccountInsert = (row) => ({
|
|
764
|
+
id: row.id,
|
|
765
|
+
provider_id: row.providerId,
|
|
766
|
+
provider_account_id: row.providerAccountId,
|
|
767
|
+
user_id: row.userId,
|
|
768
|
+
email: row.email ?? null,
|
|
769
|
+
email_verified: row.emailVerified === undefined ? null : row.emailVerified ? 1 : 0,
|
|
770
|
+
created_at: Number(row.createdAt),
|
|
771
|
+
updated_at: Number(row.updatedAt),
|
|
772
|
+
unlinked_at: row.unlinkedAt === undefined ? null : Number(row.unlinkedAt),
|
|
773
|
+
metadata: jsonEncode(row.metadata),
|
|
774
|
+
});
|
|
775
|
+
const oauthAccountUnlink = (input, row) => ({
|
|
776
|
+
unlinked_at: Number(input.unlinkedAt),
|
|
777
|
+
metadata: jsonEncode(metadataWithReason(row.metadata, "unlinkReason", input.reason)),
|
|
778
|
+
});
|
|
779
|
+
const oauthProviderTokenVaultValues = (row) => ({
|
|
780
|
+
account_id: row.accountId,
|
|
781
|
+
user_id: row.userId,
|
|
782
|
+
provider_id: row.providerId,
|
|
783
|
+
provider_account_id: row.providerAccountId,
|
|
784
|
+
access_token_ciphertext: Redacted.value(row.accessTokenCiphertext),
|
|
785
|
+
refresh_token_ciphertext: row.refreshTokenCiphertext === undefined
|
|
786
|
+
? null
|
|
787
|
+
: Redacted.value(row.refreshTokenCiphertext),
|
|
788
|
+
id_token_ciphertext: row.idTokenCiphertext === undefined ? null : Redacted.value(row.idTokenCiphertext),
|
|
789
|
+
token_type: row.tokenType,
|
|
790
|
+
scopes: jsonEncode(row.scopes),
|
|
791
|
+
expires_at: row.expiresAt === undefined ? null : Number(row.expiresAt),
|
|
792
|
+
updated_at: Number(row.updatedAt),
|
|
793
|
+
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
794
|
+
revocation_reason: row.revocationReason ?? null,
|
|
795
|
+
});
|
|
796
|
+
const oauthProviderTokenVaultMarkRevoked = (input) => ({
|
|
797
|
+
revoked_at: Number(input.revokedAt),
|
|
798
|
+
revocation_reason: input.reason,
|
|
799
|
+
});
|
|
417
800
|
const jwtRevocationUpdate = (record) => ({
|
|
418
801
|
revoked_at: Number(record.revokedAt),
|
|
419
802
|
expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
|
|
@@ -426,7 +809,14 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
426
809
|
const verification = makeAuthVerificationTable(names.verification ?? "auth_verification");
|
|
427
810
|
const loginApprovalReview = makeAuthLoginApprovalReviewTable(names.loginApprovalReview ?? "auth_login_approval_review");
|
|
428
811
|
const trustedDevice = makeAuthTrustedDeviceTable(names.trustedDevice ?? "auth_trusted_device");
|
|
812
|
+
const passkeyCredential = makeAuthPasskeyCredentialTable(names.passkeyCredential ?? "auth_passkey_credential");
|
|
813
|
+
const totpFactor = makeAuthTotpFactorTable(names.totpFactor ?? "auth_totp_factor");
|
|
814
|
+
const recoveryCode = makeAuthRecoveryCodeTable(names.recoveryCode ?? "auth_recovery_code");
|
|
815
|
+
const apiKey = makeAuthApiKeyTable(names.apiKey ?? "auth_api_key");
|
|
816
|
+
const refreshToken = makeAuthRefreshTokenTable(names.refreshToken ?? "auth_refresh_token");
|
|
429
817
|
const jwtRevocation = makeAuthJwtRevocationTable(names.jwtRevocation ?? "auth_jwt_revocation");
|
|
818
|
+
const oauthAccount = makeAuthOAuthAccountTable(names.oauthAccount ?? "auth_oauth_account");
|
|
819
|
+
const oauthProviderTokenVault = makeAuthOAuthProviderTokenVaultTable(names.oauthProviderTokenVault ?? "auth_oauth_provider_token_vault");
|
|
430
820
|
return {
|
|
431
821
|
user: {
|
|
432
822
|
table: user,
|
|
@@ -532,6 +922,100 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
532
922
|
insert: trustedDeviceInsert,
|
|
533
923
|
update: trustedDeviceUpdate,
|
|
534
924
|
},
|
|
925
|
+
passkeyCredential: {
|
|
926
|
+
table: passkeyCredential,
|
|
927
|
+
select: {
|
|
928
|
+
id: passkeyCredential.id,
|
|
929
|
+
userId: passkeyCredential.user_id,
|
|
930
|
+
credentialId: passkeyCredential.credential_id,
|
|
931
|
+
publicKey: passkeyCredential.public_key,
|
|
932
|
+
signCount: passkeyCredential.sign_count,
|
|
933
|
+
transports: passkeyCredential.transports,
|
|
934
|
+
backedUp: passkeyCredential.backed_up,
|
|
935
|
+
createdAt: passkeyCredential.created_at,
|
|
936
|
+
lastUsedAt: passkeyCredential.last_used_at,
|
|
937
|
+
revokedAt: passkeyCredential.revoked_at,
|
|
938
|
+
metadata: passkeyCredential.metadata,
|
|
939
|
+
},
|
|
940
|
+
insert: passkeyCredentialInsert,
|
|
941
|
+
updateSignCount: passkeyCredentialUpdateSignCount,
|
|
942
|
+
revoke: passkeyCredentialRevoke,
|
|
943
|
+
},
|
|
944
|
+
totpFactor: {
|
|
945
|
+
table: totpFactor,
|
|
946
|
+
select: {
|
|
947
|
+
id: totpFactor.id,
|
|
948
|
+
userId: totpFactor.user_id,
|
|
949
|
+
secret: totpFactor.secret,
|
|
950
|
+
algorithm: totpFactor.algorithm,
|
|
951
|
+
digits: totpFactor.digits,
|
|
952
|
+
period: totpFactor.period,
|
|
953
|
+
createdAt: totpFactor.created_at,
|
|
954
|
+
confirmedAt: totpFactor.confirmed_at,
|
|
955
|
+
lastUsedAt: totpFactor.last_used_at,
|
|
956
|
+
revokedAt: totpFactor.revoked_at,
|
|
957
|
+
metadata: totpFactor.metadata,
|
|
958
|
+
},
|
|
959
|
+
insert: totpFactorInsert,
|
|
960
|
+
confirm: totpFactorConfirm,
|
|
961
|
+
markUsed: totpFactorMarkUsed,
|
|
962
|
+
revoke: totpFactorRevoke,
|
|
963
|
+
},
|
|
964
|
+
recoveryCode: {
|
|
965
|
+
table: recoveryCode,
|
|
966
|
+
select: {
|
|
967
|
+
id: recoveryCode.id,
|
|
968
|
+
userId: recoveryCode.user_id,
|
|
969
|
+
codeHash: recoveryCode.code_hash,
|
|
970
|
+
createdAt: recoveryCode.created_at,
|
|
971
|
+
usedAt: recoveryCode.used_at,
|
|
972
|
+
revokedAt: recoveryCode.revoked_at,
|
|
973
|
+
metadata: recoveryCode.metadata,
|
|
974
|
+
},
|
|
975
|
+
insert: recoveryCodeInsert,
|
|
976
|
+
markUsed: recoveryCodeMarkUsed,
|
|
977
|
+
revoke: recoveryCodeRevoke,
|
|
978
|
+
},
|
|
979
|
+
apiKey: {
|
|
980
|
+
table: apiKey,
|
|
981
|
+
select: {
|
|
982
|
+
id: apiKey.id,
|
|
983
|
+
userId: apiKey.user_id,
|
|
984
|
+
prefix: apiKey.prefix,
|
|
985
|
+
secretHash: apiKey.secret_hash,
|
|
986
|
+
scopes: apiKey.scopes,
|
|
987
|
+
createdAt: apiKey.created_at,
|
|
988
|
+
expiresAt: apiKey.expires_at,
|
|
989
|
+
lastUsedAt: apiKey.last_used_at,
|
|
990
|
+
revokedAt: apiKey.revoked_at,
|
|
991
|
+
metadata: apiKey.metadata,
|
|
992
|
+
},
|
|
993
|
+
insert: apiKeyInsert,
|
|
994
|
+
markUsed: apiKeyMarkUsed,
|
|
995
|
+
revoke: apiKeyRevoke,
|
|
996
|
+
},
|
|
997
|
+
refreshToken: {
|
|
998
|
+
table: refreshToken,
|
|
999
|
+
select: {
|
|
1000
|
+
id: refreshToken.id,
|
|
1001
|
+
familyId: refreshToken.family_id,
|
|
1002
|
+
userId: refreshToken.user_id,
|
|
1003
|
+
secretHash: refreshToken.secret_hash,
|
|
1004
|
+
createdAt: refreshToken.created_at,
|
|
1005
|
+
expiresAt: refreshToken.expires_at,
|
|
1006
|
+
lastUsedAt: refreshToken.last_used_at,
|
|
1007
|
+
rotatedAt: refreshToken.rotated_at,
|
|
1008
|
+
replacedById: refreshToken.replaced_by_id,
|
|
1009
|
+
revokedAt: refreshToken.revoked_at,
|
|
1010
|
+
reuseDetectedAt: refreshToken.reuse_detected_at,
|
|
1011
|
+
metadata: refreshToken.metadata,
|
|
1012
|
+
},
|
|
1013
|
+
insert: refreshTokenInsert,
|
|
1014
|
+
rotate: refreshTokenRotate,
|
|
1015
|
+
markReuseDetected: refreshTokenMarkReuseDetected,
|
|
1016
|
+
revoke: refreshTokenRevoke,
|
|
1017
|
+
revokeFamily: refreshTokenRevokeFamily,
|
|
1018
|
+
},
|
|
535
1019
|
jwtRevocation: {
|
|
536
1020
|
table: jwtRevocation,
|
|
537
1021
|
select: {
|
|
@@ -543,6 +1027,44 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
543
1027
|
insert: jwtRevocationInsert,
|
|
544
1028
|
update: jwtRevocationUpdate,
|
|
545
1029
|
},
|
|
1030
|
+
oauthAccount: {
|
|
1031
|
+
table: oauthAccount,
|
|
1032
|
+
select: {
|
|
1033
|
+
id: oauthAccount.id,
|
|
1034
|
+
providerId: oauthAccount.provider_id,
|
|
1035
|
+
providerAccountId: oauthAccount.provider_account_id,
|
|
1036
|
+
userId: oauthAccount.user_id,
|
|
1037
|
+
email: oauthAccount.email,
|
|
1038
|
+
emailVerified: oauthAccount.email_verified,
|
|
1039
|
+
createdAt: oauthAccount.created_at,
|
|
1040
|
+
updatedAt: oauthAccount.updated_at,
|
|
1041
|
+
unlinkedAt: oauthAccount.unlinked_at,
|
|
1042
|
+
metadata: oauthAccount.metadata,
|
|
1043
|
+
},
|
|
1044
|
+
insert: oauthAccountInsert,
|
|
1045
|
+
unlink: oauthAccountUnlink,
|
|
1046
|
+
},
|
|
1047
|
+
oauthProviderTokenVault: {
|
|
1048
|
+
table: oauthProviderTokenVault,
|
|
1049
|
+
select: {
|
|
1050
|
+
accountId: oauthProviderTokenVault.account_id,
|
|
1051
|
+
userId: oauthProviderTokenVault.user_id,
|
|
1052
|
+
providerId: oauthProviderTokenVault.provider_id,
|
|
1053
|
+
providerAccountId: oauthProviderTokenVault.provider_account_id,
|
|
1054
|
+
accessTokenCiphertext: oauthProviderTokenVault.access_token_ciphertext,
|
|
1055
|
+
refreshTokenCiphertext: oauthProviderTokenVault.refresh_token_ciphertext,
|
|
1056
|
+
idTokenCiphertext: oauthProviderTokenVault.id_token_ciphertext,
|
|
1057
|
+
tokenType: oauthProviderTokenVault.token_type,
|
|
1058
|
+
scopes: oauthProviderTokenVault.scopes,
|
|
1059
|
+
expiresAt: oauthProviderTokenVault.expires_at,
|
|
1060
|
+
updatedAt: oauthProviderTokenVault.updated_at,
|
|
1061
|
+
revokedAt: oauthProviderTokenVault.revoked_at,
|
|
1062
|
+
revocationReason: oauthProviderTokenVault.revocation_reason,
|
|
1063
|
+
},
|
|
1064
|
+
insert: oauthProviderTokenVaultValues,
|
|
1065
|
+
update: oauthProviderTokenVaultValues,
|
|
1066
|
+
markRevoked: oauthProviderTokenVaultMarkRevoked,
|
|
1067
|
+
},
|
|
546
1068
|
};
|
|
547
1069
|
};
|
|
548
1070
|
const asAuthSqliteTables = (tables) => tables;
|
|
@@ -555,7 +1077,14 @@ const selectSessionColumns = (tables) => tables.session.select;
|
|
|
555
1077
|
const selectVerificationColumns = (tables) => tables.verification.select;
|
|
556
1078
|
const selectLoginApprovalReviewColumns = (tables) => tables.loginApprovalReview.select;
|
|
557
1079
|
const selectTrustedDeviceColumns = (tables) => tables.trustedDevice.select;
|
|
1080
|
+
const selectPasskeyCredentialColumns = (tables) => tables.passkeyCredential.select;
|
|
1081
|
+
const selectTotpFactorColumns = (tables) => tables.totpFactor.select;
|
|
1082
|
+
const selectRecoveryCodeColumns = (tables) => tables.recoveryCode.select;
|
|
1083
|
+
const selectApiKeyColumns = (tables) => tables.apiKey.select;
|
|
1084
|
+
const selectRefreshTokenColumns = (tables) => tables.refreshToken.select;
|
|
558
1085
|
const selectJwtRevocationColumns = (tables) => tables.jwtRevocation.select;
|
|
1086
|
+
const selectOAuthAccountColumns = (tables) => tables.oauthAccount.select;
|
|
1087
|
+
const selectOAuthProviderTokenVaultColumns = (tables) => tables.oauthProviderTokenVault.select;
|
|
559
1088
|
const storageError = (entity, operation, cause) => StorageError.fromUnknown(entity, operation, cause);
|
|
560
1089
|
const loginApprovalReviewStoreError = (operation, cause) => new LoginApprovalReviewError({
|
|
561
1090
|
message: `Login approval review ${operation} failed`,
|
|
@@ -566,11 +1095,46 @@ const trustedDeviceStoreError = (operation, cause) => new TrustedDeviceError({
|
|
|
566
1095
|
message: `Trusted device ${operation} failed`,
|
|
567
1096
|
cause,
|
|
568
1097
|
});
|
|
1098
|
+
const passkeyCredentialStoreError = (operation, cause) => new PasskeyCredentialStoreError({
|
|
1099
|
+
operation,
|
|
1100
|
+
message: `Passkey credential ${operation} failed`,
|
|
1101
|
+
cause,
|
|
1102
|
+
});
|
|
1103
|
+
const totpFactorStoreError = (operation, cause) => new TotpFactorStoreError({
|
|
1104
|
+
operation,
|
|
1105
|
+
message: `TOTP factor ${operation} failed`,
|
|
1106
|
+
cause,
|
|
1107
|
+
});
|
|
1108
|
+
const recoveryCodeStoreError = (operation, cause) => new RecoveryCodeStoreError({
|
|
1109
|
+
operation,
|
|
1110
|
+
message: `Recovery code ${operation} failed`,
|
|
1111
|
+
cause,
|
|
1112
|
+
});
|
|
1113
|
+
const apiKeyStoreError = (operation, cause) => new ApiKeyStoreError({
|
|
1114
|
+
operation,
|
|
1115
|
+
message: `API key ${operation} failed`,
|
|
1116
|
+
cause,
|
|
1117
|
+
});
|
|
1118
|
+
const refreshTokenStoreError = (operation, cause) => new RefreshTokenStoreError({
|
|
1119
|
+
operation,
|
|
1120
|
+
message: `Refresh token ${operation} failed`,
|
|
1121
|
+
cause,
|
|
1122
|
+
});
|
|
569
1123
|
const jwtRevocationStoreError = (operation, cause) => new JwtError({
|
|
570
1124
|
operation: "revocation",
|
|
571
1125
|
message: `JWT revocation ${operation} failed`,
|
|
572
1126
|
cause,
|
|
573
1127
|
});
|
|
1128
|
+
const oauthAccountStoreError = (operation, cause) => new OAuthAccountStoreError({
|
|
1129
|
+
operation,
|
|
1130
|
+
message: `OAuth account ${operation} failed`,
|
|
1131
|
+
cause,
|
|
1132
|
+
});
|
|
1133
|
+
const oauthProviderTokenVaultError = (operation, cause) => new OAuthProviderTokenVaultError({
|
|
1134
|
+
operation,
|
|
1135
|
+
message: `OAuth provider token vault ${operation} failed`,
|
|
1136
|
+
cause,
|
|
1137
|
+
});
|
|
574
1138
|
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
1139
|
? Option.none()
|
|
576
1140
|
: Option.some(decodeUserRow(tables, rows[0]))));
|
|
@@ -599,6 +1163,48 @@ const findTrustedDevice = (executor, tables, input, operation = "status") => exe
|
|
|
599
1163
|
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
1164
|
? Option.none()
|
|
601
1165
|
: Option.some(decodeTrustedDeviceRow(tables, rows[0]))));
|
|
1166
|
+
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
|
|
1167
|
+
? Option.none()
|
|
1168
|
+
: Option.some(decodePasskeyCredentialRow(tables, rows[0]))));
|
|
1169
|
+
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
|
|
1170
|
+
? Option.none()
|
|
1171
|
+
: Option.some(decodeTotpFactorRow(tables, rows[0]))));
|
|
1172
|
+
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
|
|
1173
|
+
? Option.none()
|
|
1174
|
+
: Option.some(decodeRecoveryCodeRow(tables, rows[0]))));
|
|
1175
|
+
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
|
|
1176
|
+
? Option.none()
|
|
1177
|
+
: Option.some(decodeApiKeyRow(tables, rows[0]))));
|
|
1178
|
+
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
|
|
1179
|
+
? Option.none()
|
|
1180
|
+
: Option.some(decodeApiKeyRow(tables, rows[0]))));
|
|
1181
|
+
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
|
|
1182
|
+
? Option.none()
|
|
1183
|
+
: Option.some(decodeRefreshTokenRow(tables, rows[0]))));
|
|
1184
|
+
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
|
|
1185
|
+
? Option.none()
|
|
1186
|
+
: Option.some(decodeRefreshTokenRow(tables, rows[0]))));
|
|
1187
|
+
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
|
|
1188
|
+
? Option.none()
|
|
1189
|
+
: Option.some(decodeOAuthAccountRow(tables, rows[0]))));
|
|
1190
|
+
const findOAuthAccountByProviderAccount = (executor, tables, input, operation = "find") => {
|
|
1191
|
+
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)));
|
|
1192
|
+
const query = input.includeUnlinked === true
|
|
1193
|
+
? base
|
|
1194
|
+
: base.pipe(Query.where(queryIsNull(tables.oauthAccount.select.unlinkedAt)));
|
|
1195
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => oauthAccountStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1196
|
+
? Option.none()
|
|
1197
|
+
: Option.some(decodeOAuthAccountRow(tables, rows[0]))));
|
|
1198
|
+
};
|
|
1199
|
+
const findOAuthProviderTokenVaultRecordByAccountId = (executor, tables, input, operation, includeRevoked = false) => {
|
|
1200
|
+
const base = Query.select(selectOAuthProviderTokenVaultColumns(tables)).pipe(Query.from(tables.oauthProviderTokenVault.table), Query.where(queryEq(tables.oauthProviderTokenVault.select.accountId, input.accountId)));
|
|
1201
|
+
const query = includeRevoked
|
|
1202
|
+
? base
|
|
1203
|
+
: base.pipe(Query.where(queryIsNull(tables.oauthProviderTokenVault.select.revokedAt)));
|
|
1204
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => oauthProviderTokenVaultError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1205
|
+
? Option.none()
|
|
1206
|
+
: Option.some(decodeOAuthProviderTokenVaultRow(tables, rows[0]))));
|
|
1207
|
+
};
|
|
602
1208
|
export const makeEffectQbSqliteUserStore = (executor, options = {}) => {
|
|
603
1209
|
const tables = options.tables ?? authSqliteTables;
|
|
604
1210
|
return UserStore.make({
|
|
@@ -705,6 +1311,155 @@ export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) =>
|
|
|
705
1311
|
: Effect.fail(trustedDeviceStoreError("trust", "missing row")))),
|
|
706
1312
|
});
|
|
707
1313
|
};
|
|
1314
|
+
export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {}) => {
|
|
1315
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1316
|
+
return PasskeyCredentialStore.make({
|
|
1317
|
+
insert: (row) => executePlan(executor, queryInsert(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.insert(row)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("insert", cause)), Effect.asVoid),
|
|
1318
|
+
findByCredentialId: (credentialId) => findPasskeyCredentialByCredentialId(executor, tables, credentialId),
|
|
1319
|
+
listByUser: (input) => {
|
|
1320
|
+
const base = Query.select(selectPasskeyCredentialColumns(tables)).pipe(Query.from(tables.passkeyCredential.table), Query.where(queryEq(tables.passkeyCredential.select.userId, input.userId)));
|
|
1321
|
+
const query = input.includeRevoked === true
|
|
1322
|
+
? base
|
|
1323
|
+
: base.pipe(Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)));
|
|
1324
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodePasskeyCredentialRow(tables, row))));
|
|
1325
|
+
},
|
|
1326
|
+
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
|
|
1327
|
+
? Option.none()
|
|
1328
|
+
: Option.some(decodePasskeyCredentialRow(tables, rows[0])))),
|
|
1329
|
+
revoke: (input) => findPasskeyCredentialByCredentialId(executor, tables, input.credentialId, "revoke").pipe(Effect.flatMap((row) => {
|
|
1330
|
+
if (Option.isNone(row)) {
|
|
1331
|
+
return Effect.void;
|
|
1332
|
+
}
|
|
1333
|
+
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);
|
|
1334
|
+
})),
|
|
1335
|
+
});
|
|
1336
|
+
};
|
|
1337
|
+
export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
1338
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1339
|
+
return TotpFactorStore.make({
|
|
1340
|
+
insert: (row) => executePlan(executor, queryInsert(tables.totpFactor.table, mutationValues(tables.totpFactor.insert(row)))).pipe(Effect.mapError((cause) => totpFactorStoreError("insert", cause)), Effect.asVoid),
|
|
1341
|
+
findById: (id) => findTotpFactorById(executor, tables, id),
|
|
1342
|
+
listByUser: (input) => {
|
|
1343
|
+
const byUser = Query.select(selectTotpFactorColumns(tables)).pipe(Query.from(tables.totpFactor.table), Query.where(queryEq(tables.totpFactor.select.userId, input.userId)));
|
|
1344
|
+
const byRevoked = input.includeRevoked === true
|
|
1345
|
+
? byUser
|
|
1346
|
+
: byUser.pipe(Query.where(queryIsNull(tables.totpFactor.select.revokedAt)));
|
|
1347
|
+
const query = input.includeUnconfirmed === true
|
|
1348
|
+
? byRevoked
|
|
1349
|
+
: byRevoked.pipe(Query.where(queryGt(tables.totpFactor.select.confirmedAt, 0)));
|
|
1350
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => totpFactorStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeTotpFactorRow(tables, row))));
|
|
1351
|
+
},
|
|
1352
|
+
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
|
|
1353
|
+
? Option.none()
|
|
1354
|
+
: Option.some(decodeTotpFactorRow(tables, rows[0])))),
|
|
1355
|
+
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
|
|
1356
|
+
? Option.none()
|
|
1357
|
+
: Option.some(decodeTotpFactorRow(tables, rows[0])))),
|
|
1358
|
+
revoke: (input) => findTotpFactorById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
1359
|
+
if (Option.isNone(row)) {
|
|
1360
|
+
return Effect.void;
|
|
1361
|
+
}
|
|
1362
|
+
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);
|
|
1363
|
+
})),
|
|
1364
|
+
});
|
|
1365
|
+
};
|
|
1366
|
+
export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
1367
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1368
|
+
return RecoveryCodeStore.make({
|
|
1369
|
+
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 }),
|
|
1370
|
+
findById: (id) => findRecoveryCodeById(executor, tables, id),
|
|
1371
|
+
listByUser: (input) => {
|
|
1372
|
+
const byUser = Query.select(selectRecoveryCodeColumns(tables)).pipe(Query.from(tables.recoveryCode.table), Query.where(queryEq(tables.recoveryCode.select.userId, input.userId)));
|
|
1373
|
+
const byUsed = input.includeUsed === true
|
|
1374
|
+
? byUser
|
|
1375
|
+
: byUser.pipe(Query.where(queryIsNull(tables.recoveryCode.select.usedAt)));
|
|
1376
|
+
const query = input.includeRevoked === true
|
|
1377
|
+
? byUsed
|
|
1378
|
+
: byUsed.pipe(Query.where(queryIsNull(tables.recoveryCode.select.revokedAt)));
|
|
1379
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => recoveryCodeStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRecoveryCodeRow(tables, row))));
|
|
1380
|
+
},
|
|
1381
|
+
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
|
|
1382
|
+
? Effect.succeed(Option.none())
|
|
1383
|
+
: findRecoveryCodeById(executor, tables, input.id, "mark-used"))),
|
|
1384
|
+
revoke: (input) => findRecoveryCodeById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
1385
|
+
if (Option.isNone(row)) {
|
|
1386
|
+
return Effect.void;
|
|
1387
|
+
}
|
|
1388
|
+
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);
|
|
1389
|
+
})),
|
|
1390
|
+
});
|
|
1391
|
+
};
|
|
1392
|
+
export const makeEffectQbSqliteApiKeyStore = (executor, options = {}) => {
|
|
1393
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1394
|
+
return ApiKeyStore.make({
|
|
1395
|
+
insert: (row) => executePlan(executor, queryInsert(tables.apiKey.table, mutationValues(tables.apiKey.insert(row)))).pipe(Effect.mapError((cause) => apiKeyStoreError("insert", cause)), Effect.asVoid),
|
|
1396
|
+
findById: (id) => findApiKeyById(executor, tables, id),
|
|
1397
|
+
findByPrefix: (prefix) => findApiKeyByPrefix(executor, tables, prefix),
|
|
1398
|
+
listByUser: (input) => {
|
|
1399
|
+
const base = Query.select(selectApiKeyColumns(tables)).pipe(Query.from(tables.apiKey.table), Query.where(queryEq(tables.apiKey.select.userId, input.userId)));
|
|
1400
|
+
const query = input.includeRevoked === true
|
|
1401
|
+
? base
|
|
1402
|
+
: base.pipe(Query.where(queryIsNull(tables.apiKey.select.revokedAt)));
|
|
1403
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => apiKeyStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeApiKeyRow(tables, row))));
|
|
1404
|
+
},
|
|
1405
|
+
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
|
|
1406
|
+
? Option.none()
|
|
1407
|
+
: Option.some(decodeApiKeyRow(tables, rows[0])))),
|
|
1408
|
+
revoke: (input) => findApiKeyById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
1409
|
+
if (Option.isNone(row)) {
|
|
1410
|
+
return Effect.void;
|
|
1411
|
+
}
|
|
1412
|
+
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);
|
|
1413
|
+
})),
|
|
1414
|
+
});
|
|
1415
|
+
};
|
|
1416
|
+
export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
1417
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1418
|
+
return RefreshTokenStore.make({
|
|
1419
|
+
insert: (row) => executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(row)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("insert", cause)), Effect.asVoid),
|
|
1420
|
+
findById: (id) => findRefreshTokenById(executor, tables, id),
|
|
1421
|
+
findBySecretHash: (hash) => findRefreshTokenBySecretHash(executor, tables, hash),
|
|
1422
|
+
listByUser: (input) => {
|
|
1423
|
+
const base = Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.userId, input.userId)));
|
|
1424
|
+
const query = input.includeRevoked === true
|
|
1425
|
+
? base
|
|
1426
|
+
: base.pipe(Query.where(queryIsNull(tables.refreshToken.select.revokedAt)));
|
|
1427
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => refreshTokenStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRefreshTokenRow(tables, row))));
|
|
1428
|
+
},
|
|
1429
|
+
listByFamily: (input) => {
|
|
1430
|
+
const base = Query.select(selectRefreshTokenColumns(tables)).pipe(Query.from(tables.refreshToken.table), Query.where(queryEq(tables.refreshToken.select.familyId, input.familyId)));
|
|
1431
|
+
const query = input.includeRevoked === true
|
|
1432
|
+
? base
|
|
1433
|
+
: base.pipe(Query.where(queryIsNull(tables.refreshToken.select.revokedAt)));
|
|
1434
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => refreshTokenStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRefreshTokenRow(tables, row))));
|
|
1435
|
+
},
|
|
1436
|
+
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) => {
|
|
1437
|
+
const [updated] = rows;
|
|
1438
|
+
if (updated === undefined) {
|
|
1439
|
+
return Effect.succeed(Option.none());
|
|
1440
|
+
}
|
|
1441
|
+
return findRefreshTokenById(executor, tables, RefreshTokenId(updated.id), "rotate").pipe(Effect.flatMap((previous) => {
|
|
1442
|
+
if (Option.isNone(previous)) {
|
|
1443
|
+
return Effect.succeed(Option.none());
|
|
1444
|
+
}
|
|
1445
|
+
return executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(input.replacement)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.as(Option.some({
|
|
1446
|
+
previous: previous.value,
|
|
1447
|
+
replacement: input.replacement,
|
|
1448
|
+
})));
|
|
1449
|
+
}));
|
|
1450
|
+
})),
|
|
1451
|
+
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
|
|
1452
|
+
? Option.none()
|
|
1453
|
+
: Option.some(decodeRefreshTokenRow(tables, rows[0])))),
|
|
1454
|
+
revoke: (input) => findRefreshTokenById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
1455
|
+
if (Option.isNone(row)) {
|
|
1456
|
+
return Effect.void;
|
|
1457
|
+
}
|
|
1458
|
+
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);
|
|
1459
|
+
})),
|
|
1460
|
+
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 }))),
|
|
1461
|
+
});
|
|
1462
|
+
};
|
|
708
1463
|
export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) => {
|
|
709
1464
|
const tables = options.tables ?? authSqliteTables;
|
|
710
1465
|
return JwtRevocationStore.make({
|
|
@@ -719,6 +1474,57 @@ export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) =>
|
|
|
719
1474
|
find: (jwtId) => findJwtRevocation(executor, tables, jwtId),
|
|
720
1475
|
});
|
|
721
1476
|
};
|
|
1477
|
+
export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
|
|
1478
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1479
|
+
return OAuthAccountStore.make({
|
|
1480
|
+
insert: (row) => executePlan(executor, queryInsert(tables.oauthAccount.table, mutationValues(tables.oauthAccount.insert(row)))).pipe(Effect.mapError((cause) => oauthAccountStoreError("insert", cause)), Effect.asVoid),
|
|
1481
|
+
findById: (id) => findOAuthAccountById(executor, tables, id),
|
|
1482
|
+
findByProviderAccount: (input) => findOAuthAccountByProviderAccount(executor, tables, input),
|
|
1483
|
+
listByUser: (input) => {
|
|
1484
|
+
const base = Query.select(selectOAuthAccountColumns(tables)).pipe(Query.from(tables.oauthAccount.table), Query.where(queryEq(tables.oauthAccount.select.userId, input.userId)));
|
|
1485
|
+
const query = input.includeUnlinked === true
|
|
1486
|
+
? base
|
|
1487
|
+
: base.pipe(Query.where(queryIsNull(tables.oauthAccount.select.unlinkedAt)));
|
|
1488
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => oauthAccountStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeOAuthAccountRow(tables, row))));
|
|
1489
|
+
},
|
|
1490
|
+
unlink: (input) => findOAuthAccountByProviderAccount(executor, tables, {
|
|
1491
|
+
providerId: input.providerId,
|
|
1492
|
+
providerAccountId: input.providerAccountId,
|
|
1493
|
+
}, "unlink").pipe(Effect.flatMap((row) => {
|
|
1494
|
+
if (Option.isNone(row)) {
|
|
1495
|
+
return Effect.succeed(Option.none());
|
|
1496
|
+
}
|
|
1497
|
+
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
|
|
1498
|
+
? Option.none()
|
|
1499
|
+
: Option.some(decodeOAuthAccountRow(tables, rows[0]))));
|
|
1500
|
+
})),
|
|
1501
|
+
});
|
|
1502
|
+
};
|
|
1503
|
+
export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}) => {
|
|
1504
|
+
const tables = options.tables ?? authSqliteTables;
|
|
1505
|
+
return OAuthProviderTokenVault.make({
|
|
1506
|
+
upsert: (record) => findOAuthProviderTokenVaultRecordByAccountId(executor, tables, { accountId: record.accountId }, "upsert", true).pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
1507
|
+
? executePlan(executor, queryInsert(tables.oauthProviderTokenVault.table, mutationValues(tables.oauthProviderTokenVault.insert(record))))
|
|
1508
|
+
: executePlan(executor, queryUpdate(tables.oauthProviderTokenVault.table, mutationValues(tables.oauthProviderTokenVault.update(record))).pipe(Query.where(queryEq(tables.oauthProviderTokenVault.select.accountId, record.accountId))))), Effect.mapError((cause) => cause instanceof OAuthProviderTokenVaultError
|
|
1509
|
+
? cause
|
|
1510
|
+
: oauthProviderTokenVaultError("upsert", cause)), Effect.asVoid),
|
|
1511
|
+
refreshTokenForAccount: (input) => findOAuthProviderTokenVaultRecordByAccountId(executor, tables, input, "refresh-token").pipe(Effect.map((row) => Option.isNone(row) || row.value.refreshTokenCiphertext === undefined
|
|
1512
|
+
? Option.none()
|
|
1513
|
+
: Option.some(row.value.refreshTokenCiphertext))),
|
|
1514
|
+
tokensForAccount: (input) => findOAuthProviderTokenVaultRecordByAccountId(executor, tables, input, "tokens").pipe(Effect.map((row) => Option.isNone(row)
|
|
1515
|
+
? Option.none()
|
|
1516
|
+
: Option.some(providerTokenVaultTokens(row.value)))),
|
|
1517
|
+
markRevoked: (input) => executePlan(executor, queryUpdate(tables.oauthProviderTokenVault.table, mutationValues(tables.oauthProviderTokenVault.markRevoked(input))).pipe(Query.where(queryEq(tables.oauthProviderTokenVault.select.accountId, input.accountId)), Query.returning({
|
|
1518
|
+
accountId: tables.oauthProviderTokenVault.select.accountId,
|
|
1519
|
+
}))).pipe(Effect.mapError((cause) => oauthProviderTokenVaultError("mark-revoked", cause)), Effect.flatMap((rows) => {
|
|
1520
|
+
const [updated] = rows;
|
|
1521
|
+
if (updated === undefined) {
|
|
1522
|
+
return Effect.succeed(Option.none());
|
|
1523
|
+
}
|
|
1524
|
+
return findOAuthProviderTokenVaultRecordByAccountId(executor, tables, { accountId: OAuthAccountId(updated.accountId) }, "mark-revoked", true);
|
|
1525
|
+
})),
|
|
1526
|
+
});
|
|
1527
|
+
};
|
|
722
1528
|
export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
|
|
723
1529
|
userStore: makeEffectQbSqliteUserStore(executor, options),
|
|
724
1530
|
credentialStore: makeEffectQbSqliteCredentialStore(executor, options),
|
|
@@ -726,9 +1532,16 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
|
|
|
726
1532
|
verificationStore: makeEffectQbSqliteVerificationStore(executor, options),
|
|
727
1533
|
loginApprovalReviewStore: makeEffectQbSqliteLoginApprovalReviewStore(executor, options),
|
|
728
1534
|
trustedDeviceStore: makeEffectQbSqliteTrustedDeviceStore(executor, options),
|
|
1535
|
+
passkeyCredentialStore: makeEffectQbSqlitePasskeyCredentialStore(executor, options),
|
|
1536
|
+
totpFactorStore: makeEffectQbSqliteTotpFactorStore(executor, options),
|
|
1537
|
+
recoveryCodeStore: makeEffectQbSqliteRecoveryCodeStore(executor, options),
|
|
1538
|
+
apiKeyStore: makeEffectQbSqliteApiKeyStore(executor, options),
|
|
1539
|
+
refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, options),
|
|
729
1540
|
jwtRevocationStore: makeEffectQbSqliteJwtRevocationStore(executor, options),
|
|
1541
|
+
oauthAccountStore: makeEffectQbSqliteOAuthAccountStore(executor, options),
|
|
1542
|
+
oauthProviderTokenVault: makeEffectQbSqliteOAuthProviderTokenVault(executor, options),
|
|
730
1543
|
});
|
|
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)));
|
|
1544
|
+
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)));
|
|
732
1545
|
export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
|
|
733
1546
|
driver: Executor.driver((query) => Effect.tryPromise({
|
|
734
1547
|
try: () => database
|