@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.
- package/README.md +10 -52
- package/dist/EffectQbSqliteStorage.d.ts +228 -2
- package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
- package/dist/EffectQbSqliteStorage.js +683 -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/PasskeySimpleWebAuthn.d.ts +11 -0
- package/dist/PasskeySimpleWebAuthn.d.ts.map +1 -0
- package/dist/PasskeySimpleWebAuthn.js +181 -0
- package/dist/PasskeySimpleWebAuthn.js.map +1 -0
- package/dist/cloudflare/RateLimitDurableObject.d.ts +1 -1
- package/package.json +10 -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, } 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
|