@effect-auth/core 0.1.0-alpha.18 → 0.1.0-alpha.19
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 +4 -5
- package/dist/DevEmail.d.ts +44 -0
- package/dist/DevEmail.d.ts.map +1 -0
- package/dist/DevEmail.js +115 -0
- package/dist/DevEmail.js.map +1 -0
- package/dist/DevelopmentSeed.d.ts +47 -0
- package/dist/DevelopmentSeed.d.ts.map +1 -0
- package/dist/DevelopmentSeed.js +337 -0
- package/dist/DevelopmentSeed.js.map +1 -0
- package/dist/DrizzleBunSqliteStorage.d.ts +1 -1
- package/dist/DrizzleBunSqliteStorage.d.ts.map +1 -1
- package/dist/DrizzleD1SqliteStorage.d.ts +1 -1
- package/dist/DrizzleD1SqliteStorage.d.ts.map +1 -1
- package/dist/DrizzleEffectSqliteStorage.d.ts +1 -1
- package/dist/DrizzleEffectSqliteStorage.d.ts.map +1 -1
- package/dist/DrizzleNodeSqliteStorage.d.ts +1 -1
- package/dist/DrizzleNodeSqliteStorage.d.ts.map +1 -1
- package/dist/DrizzlePostgresStorage.d.ts +26 -0
- package/dist/DrizzlePostgresStorage.d.ts.map +1 -0
- package/dist/DrizzlePostgresStorage.js +14 -0
- package/dist/DrizzlePostgresStorage.js.map +1 -0
- package/dist/EffectQbPostgresStorage.d.ts +8 -1
- package/dist/EffectQbPostgresStorage.d.ts.map +1 -1
- package/dist/EffectQbPostgresStorage.js +359 -2
- package/dist/EffectQbPostgresStorage.js.map +1 -1
- package/dist/EffectQbSqliteStorage.d.ts +42 -2
- package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
- package/dist/EffectQbSqliteStorage.js +337 -1
- package/dist/EffectQbSqliteStorage.js.map +1 -1
- package/dist/HttpApi/Api.d.ts +151 -98
- package/dist/HttpApi/Api.d.ts.map +1 -1
- package/dist/HttpApi/Api.js +122 -72
- package/dist/HttpApi/Api.js.map +1 -1
- package/dist/HttpApi/ClientApi.d.ts +136 -136
- package/dist/HttpApi/Endpoints.d.ts +33 -1
- package/dist/HttpApi/Endpoints.d.ts.map +1 -1
- package/dist/HttpApi/Endpoints.js +31 -2
- package/dist/HttpApi/Endpoints.js.map +1 -1
- package/dist/HttpApi/Errors.d.ts +11 -3
- package/dist/HttpApi/Errors.d.ts.map +1 -1
- package/dist/HttpApi/Errors.js +16 -0
- package/dist/HttpApi/Errors.js.map +1 -1
- package/dist/HttpApi/Middleware.d.ts +12 -0
- package/dist/HttpApi/Middleware.d.ts.map +1 -1
- package/dist/HttpApi/Middleware.js +8 -0
- package/dist/HttpApi/Middleware.js.map +1 -1
- package/dist/HttpApi/OAuthDeviceAuthorization.d.ts +106 -0
- package/dist/HttpApi/OAuthDeviceAuthorization.d.ts.map +1 -0
- package/dist/HttpApi/OAuthDeviceAuthorization.js +415 -0
- package/dist/HttpApi/OAuthDeviceAuthorization.js.map +1 -0
- package/dist/HttpApi/OAuthToken.d.ts +1 -1
- package/dist/HttpApi/OAuthToken.d.ts.map +1 -1
- package/dist/HttpApi/OAuthToken.js +1 -1
- package/dist/HttpApi/OAuthToken.js.map +1 -1
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.d.ts +23 -0
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.d.ts.map +1 -0
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.js +7 -0
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.js.map +1 -0
- package/dist/HttpApi/Operations/OAuthToken.d.ts +10 -0
- package/dist/HttpApi/Operations/OAuthToken.d.ts.map +1 -1
- package/dist/HttpApi/Operations/OAuthToken.js +4 -0
- package/dist/HttpApi/Operations/OAuthToken.js.map +1 -1
- package/dist/HttpApi/Operations/index.d.ts +1 -0
- package/dist/HttpApi/Operations/index.d.ts.map +1 -1
- package/dist/HttpApi/Operations/index.js +1 -0
- package/dist/HttpApi/Operations/index.js.map +1 -1
- package/dist/HttpApi/PasswordContract.d.ts +9 -9
- package/dist/HttpApi/Schemas.d.ts +38 -0
- package/dist/HttpApi/Schemas.d.ts.map +1 -1
- package/dist/HttpApi/Schemas.js +36 -1
- package/dist/HttpApi/Schemas.js.map +1 -1
- package/dist/HttpApi/index.d.ts +3 -1
- package/dist/HttpApi/index.d.ts.map +1 -1
- package/dist/HttpApi/index.js +3 -1
- package/dist/HttpApi/index.js.map +1 -1
- package/dist/OAuth.d.ts +46 -1
- package/dist/OAuth.d.ts.map +1 -1
- package/dist/OAuth.js +118 -4
- package/dist/OAuth.js.map +1 -1
- package/dist/OAuthDeviceAuthorization.d.ts +378 -0
- package/dist/OAuthDeviceAuthorization.d.ts.map +1 -0
- package/dist/OAuthDeviceAuthorization.js +860 -0
- package/dist/OAuthDeviceAuthorization.js.map +1 -0
- package/dist/OAuthDeviceClient.d.ts +76 -0
- package/dist/OAuthDeviceClient.d.ts.map +1 -0
- package/dist/OAuthDeviceClient.js +362 -0
- package/dist/OAuthDeviceClient.js.map +1 -0
- package/dist/PostgresStorageMigrations.d.ts +2 -1
- package/dist/PostgresStorageMigrations.d.ts.map +1 -1
- package/dist/PostgresStorageMigrations.js +54 -0
- package/dist/PostgresStorageMigrations.js.map +1 -1
- package/dist/Retention.d.ts +6 -1
- package/dist/Retention.d.ts.map +1 -1
- package/dist/Retention.js +49 -12
- package/dist/Retention.js.map +1 -1
- package/dist/SqliteStorageSchemaData.d.ts +2 -0
- package/dist/SqliteStorageSchemaData.d.ts.map +1 -0
- package/dist/SqliteStorageSchemaData.js +508 -0
- package/dist/SqliteStorageSchemaData.js.map +1 -0
- package/dist/StorageMigrations.d.ts +2 -1
- package/dist/StorageMigrations.d.ts.map +1 -1
- package/dist/StorageMigrations.js +32 -0
- package/dist/StorageMigrations.js.map +1 -1
- package/dist/StorageSchemaGenerator.d.ts +70 -12
- package/dist/StorageSchemaGenerator.d.ts.map +1 -1
- package/dist/StorageSchemaGenerator.js +478 -82
- package/dist/StorageSchemaGenerator.js.map +1 -1
- package/migrations/0029_auth_oauth_device_authorization.sql +28 -0
- package/migrations/postgres/0010_auth_oauth_device_authorization.sql +50 -0
- package/package.json +24 -16
- package/dist/DrizzleDurableObjectSqliteStorage.d.ts +0 -19
- package/dist/DrizzleDurableObjectSqliteStorage.d.ts.map +0 -1
- package/dist/DrizzleDurableObjectSqliteStorage.js +0 -14
- package/dist/DrizzleDurableObjectSqliteStorage.js.map +0 -1
- package/dist/DrizzleWasmSqliteStorage.d.ts +0 -23
- package/dist/DrizzleWasmSqliteStorage.d.ts.map +0 -1
- package/dist/DrizzleWasmSqliteStorage.js +0 -15
- package/dist/DrizzleWasmSqliteStorage.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// oxlint-disable complexity, typescript/no-explicit-any
|
|
1
|
+
// oxlint-disable complexity, promise/prefer-await-to-then, typescript/no-explicit-any
|
|
2
2
|
import { Cause, Data, Effect, Layer, Option, Redacted, Schema } from "effect";
|
|
3
3
|
import { Column, Executor as CoreExecutor, Query, RowSet, Scalar, Table, } from "effect-qb";
|
|
4
4
|
import { Column as PostgresColumn, Errors as PostgresErrors, Executor, Jsonb, Query as PostgresQuery, Renderer, } from "effect-qb/postgres";
|
|
@@ -14,6 +14,7 @@ import { JwtError, JwtRevocationStore } from "./Jwt.js";
|
|
|
14
14
|
import { LoginApprovalReviewError, LoginApprovalReviewStore, } from "./LoginApproval.js";
|
|
15
15
|
import { LoginRiskContext, LoginRiskHistory, LoginRiskHistoryError, LoginRiskSignal, maxLoginRiskLevel, } from "./LoginRisk.js";
|
|
16
16
|
import { OAuthAuthorizationCodeStore, OAuthAuthorizationCodeStoreError, OAuthAccountStore, OAuthAccountStoreError, OAuthClientSecretHash, OAuthClientSecretPrefix, OAuthClientSecretStore, OAuthClientSecretStoreError, OAuthClientStore, OAuthClientStoreError, OAuthCodeChallenge, OAuthConsentStore, OAuthConsentStoreError, OAuthIdentityBridgeConflictError, OAuthIdentityBridgeStore, OAuthIdentityBridgeStoreError, OAuthProviderModeTokenStore, OAuthProviderModeTokenStoreError, OAuthProviderTokenVault, OAuthProviderTokenVaultError, } from "./OAuth.js";
|
|
17
|
+
import { OAuthDeviceAuthorizationId, OAuthDeviceAuthorizationStore, OAuthDeviceAuthorizationStoreError, OAuthDeviceCodeTokenCommitStore, OAuthDeviceCodeTokenCommitStoreError, } from "./OAuthDeviceAuthorization.js";
|
|
17
18
|
import { PasskeyCredentialId, PasskeyCredentialStore, PasskeyCredentialStoreError, } from "./Passkey.js";
|
|
18
19
|
import { canonicalPermissionScope, normalizePermissionDefinitionListLimit, PermissionStore, PermissionStoreError, } from "./Permission.js";
|
|
19
20
|
import { RecoveryCodeHash, RecoveryCodeLoginCommitStore, RecoveryCodeLoginCommitStoreError, RecoveryCodeSessionRotateStore, RecoveryCodeSessionRotateStoreError, RecoveryCodeStore, RecoveryCodeStoreError, } from "./RecoveryCode.js";
|
|
@@ -210,6 +211,25 @@ const authPostgresOAuthAuthorizationCode = Table.make("auth_oauth_authorization_
|
|
|
210
211
|
consumed_at: nullableInt8(),
|
|
211
212
|
metadata: jsonb(),
|
|
212
213
|
});
|
|
214
|
+
const authPostgresOAuthDeviceAuthorization = Table.make("auth_oauth_device_authorization", {
|
|
215
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
216
|
+
device_code_hash: Column.text().pipe(Column.unique),
|
|
217
|
+
user_code_hash: Column.text().pipe(Column.unique),
|
|
218
|
+
client_id: Column.text(),
|
|
219
|
+
requested_scopes: PostgresColumn.jsonb(Schema.Unknown),
|
|
220
|
+
granted_scopes: jsonb(),
|
|
221
|
+
subject: Column.text().pipe(Column.nullable),
|
|
222
|
+
status: Column.text(),
|
|
223
|
+
issued_at: int8(),
|
|
224
|
+
expires_at: int8(),
|
|
225
|
+
poll_interval_seconds: Column.int(),
|
|
226
|
+
next_poll_at: int8(),
|
|
227
|
+
last_polled_at: nullableInt8(),
|
|
228
|
+
approved_at: nullableInt8(),
|
|
229
|
+
denied_at: nullableInt8(),
|
|
230
|
+
consumed_at: nullableInt8(),
|
|
231
|
+
metadata: jsonb(),
|
|
232
|
+
});
|
|
213
233
|
const authPostgresOAuthClientSecret = Table.make("auth_oauth_client_secret", {
|
|
214
234
|
prefix: Column.text(),
|
|
215
235
|
client_id: Column.text(),
|
|
@@ -415,6 +435,7 @@ export const effectQbPostgresTables = {
|
|
|
415
435
|
oauthClient: authPostgresOAuthClient,
|
|
416
436
|
oauthConsent: authPostgresOAuthConsent,
|
|
417
437
|
oauthAuthorizationCode: authPostgresOAuthAuthorizationCode,
|
|
438
|
+
oauthDeviceAuthorization: authPostgresOAuthDeviceAuthorization,
|
|
418
439
|
oauthClientSecret: authPostgresOAuthClientSecret,
|
|
419
440
|
oauthProviderModeToken: authPostgresOAuthProviderModeToken,
|
|
420
441
|
oauthProviderTokenVault: authPostgresOAuthProviderTokenVault,
|
|
@@ -607,6 +628,25 @@ const oauthAuthorizationCodeSelection = {
|
|
|
607
628
|
consumedAt: authPostgresOAuthAuthorizationCode.consumed_at,
|
|
608
629
|
metadata: authPostgresOAuthAuthorizationCode.metadata,
|
|
609
630
|
};
|
|
631
|
+
const oauthDeviceAuthorizationSelection = {
|
|
632
|
+
id: authPostgresOAuthDeviceAuthorization.id,
|
|
633
|
+
deviceCodeHash: authPostgresOAuthDeviceAuthorization.device_code_hash,
|
|
634
|
+
userCodeHash: authPostgresOAuthDeviceAuthorization.user_code_hash,
|
|
635
|
+
clientId: authPostgresOAuthDeviceAuthorization.client_id,
|
|
636
|
+
requestedScopes: authPostgresOAuthDeviceAuthorization.requested_scopes,
|
|
637
|
+
grantedScopes: authPostgresOAuthDeviceAuthorization.granted_scopes,
|
|
638
|
+
subject: authPostgresOAuthDeviceAuthorization.subject,
|
|
639
|
+
status: authPostgresOAuthDeviceAuthorization.status,
|
|
640
|
+
issuedAt: authPostgresOAuthDeviceAuthorization.issued_at,
|
|
641
|
+
expiresAt: authPostgresOAuthDeviceAuthorization.expires_at,
|
|
642
|
+
pollIntervalSeconds: authPostgresOAuthDeviceAuthorization.poll_interval_seconds,
|
|
643
|
+
nextPollAt: authPostgresOAuthDeviceAuthorization.next_poll_at,
|
|
644
|
+
lastPolledAt: authPostgresOAuthDeviceAuthorization.last_polled_at,
|
|
645
|
+
approvedAt: authPostgresOAuthDeviceAuthorization.approved_at,
|
|
646
|
+
deniedAt: authPostgresOAuthDeviceAuthorization.denied_at,
|
|
647
|
+
consumedAt: authPostgresOAuthDeviceAuthorization.consumed_at,
|
|
648
|
+
metadata: authPostgresOAuthDeviceAuthorization.metadata,
|
|
649
|
+
};
|
|
610
650
|
const oauthClientSecretSelection = {
|
|
611
651
|
prefix: authPostgresOAuthClientSecret.prefix,
|
|
612
652
|
clientId: authPostgresOAuthClientSecret.client_id,
|
|
@@ -1155,6 +1195,48 @@ const decodeOAuthAuthorizationCode = (row) => ({
|
|
|
1155
1195
|
? {}
|
|
1156
1196
|
: { metadata: optionalRecord(row.metadata) }),
|
|
1157
1197
|
});
|
|
1198
|
+
const decodeOAuthDeviceAuthorization = (row) => {
|
|
1199
|
+
const base = {
|
|
1200
|
+
id: OAuthDeviceAuthorizationId(String(row.id)),
|
|
1201
|
+
deviceCodeHash: String(row.deviceCodeHash ?? row.device_code_hash),
|
|
1202
|
+
userCodeHash: String(row.userCodeHash ?? row.user_code_hash),
|
|
1203
|
+
clientId: OAuthClientId(String(row.clientId ?? row.client_id)),
|
|
1204
|
+
requestedScopes: stringArray(row.requestedScopes ?? row.requested_scopes),
|
|
1205
|
+
issuedAt: postgresBigintToUnixMillis(row.issuedAt ?? row.issued_at),
|
|
1206
|
+
expiresAt: postgresBigintToUnixMillis(row.expiresAt ?? row.expires_at),
|
|
1207
|
+
pollIntervalSeconds: Number(row.pollIntervalSeconds ?? row.poll_interval_seconds),
|
|
1208
|
+
nextPollAt: postgresBigintToUnixMillis(row.nextPollAt ?? row.next_poll_at),
|
|
1209
|
+
...(optionalMillis(row.lastPolledAt ?? row.last_polled_at) === undefined
|
|
1210
|
+
? {}
|
|
1211
|
+
: {
|
|
1212
|
+
lastPolledAt: optionalMillis(row.lastPolledAt ?? row.last_polled_at),
|
|
1213
|
+
}),
|
|
1214
|
+
...(optionalRecord(row.metadata) === undefined
|
|
1215
|
+
? {}
|
|
1216
|
+
: { metadata: optionalRecord(row.metadata) }),
|
|
1217
|
+
};
|
|
1218
|
+
const status = String(row.status);
|
|
1219
|
+
if (status === "approved") {
|
|
1220
|
+
return {
|
|
1221
|
+
...base,
|
|
1222
|
+
status: "approved",
|
|
1223
|
+
subject: String(row.subject),
|
|
1224
|
+
grantedScopes: stringArray(row.grantedScopes ?? row.granted_scopes),
|
|
1225
|
+
approvedAt: postgresBigintToUnixMillis(row.approvedAt ?? row.approved_at),
|
|
1226
|
+
...(optionalMillis(row.consumedAt ?? row.consumed_at) === undefined
|
|
1227
|
+
? {}
|
|
1228
|
+
: { consumedAt: optionalMillis(row.consumedAt ?? row.consumed_at) }),
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
if (status === "denied") {
|
|
1232
|
+
return {
|
|
1233
|
+
...base,
|
|
1234
|
+
status: "denied",
|
|
1235
|
+
deniedAt: postgresBigintToUnixMillis(row.deniedAt ?? row.denied_at),
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
return { ...base, status: "pending" };
|
|
1239
|
+
};
|
|
1158
1240
|
const decodeOAuthClientSecret = (row) => ({
|
|
1159
1241
|
prefix: OAuthClientSecretPrefix(String(row.prefix)),
|
|
1160
1242
|
clientId: OAuthClientId(String(row.clientId ?? row.client_id)),
|
|
@@ -1726,6 +1808,25 @@ const oauthAuthorizationCodeValues = (row) => ({
|
|
|
1726
1808
|
consumed_at: row.consumedAt === undefined ? null : millisParam(row.consumedAt),
|
|
1727
1809
|
metadata: row.metadata ?? null,
|
|
1728
1810
|
});
|
|
1811
|
+
const oauthDeviceAuthorizationValues = (row) => ({
|
|
1812
|
+
id: row.id,
|
|
1813
|
+
device_code_hash: row.deviceCodeHash,
|
|
1814
|
+
user_code_hash: row.userCodeHash,
|
|
1815
|
+
client_id: row.clientId,
|
|
1816
|
+
requested_scopes: row.requestedScopes,
|
|
1817
|
+
granted_scopes: null,
|
|
1818
|
+
subject: null,
|
|
1819
|
+
status: "pending",
|
|
1820
|
+
issued_at: millisParam(row.issuedAt),
|
|
1821
|
+
expires_at: millisParam(row.expiresAt),
|
|
1822
|
+
poll_interval_seconds: row.pollIntervalSeconds,
|
|
1823
|
+
next_poll_at: millisParam(row.nextPollAt),
|
|
1824
|
+
last_polled_at: row.lastPolledAt === undefined ? null : millisParam(row.lastPolledAt),
|
|
1825
|
+
approved_at: null,
|
|
1826
|
+
denied_at: null,
|
|
1827
|
+
consumed_at: null,
|
|
1828
|
+
metadata: row.metadata ?? null,
|
|
1829
|
+
});
|
|
1729
1830
|
const oauthClientSecretValues = (row) => ({
|
|
1730
1831
|
prefix: row.prefix,
|
|
1731
1832
|
client_id: row.clientId,
|
|
@@ -2098,6 +2199,16 @@ const oauthAuthorizationCodeStoreError = (operation, cause) => new OAuthAuthoriz
|
|
|
2098
2199
|
message: `OAuth authorization code ${operation} failed`,
|
|
2099
2200
|
cause,
|
|
2100
2201
|
});
|
|
2202
|
+
const oauthDeviceAuthorizationStoreError = (operation, cause) => new OAuthDeviceAuthorizationStoreError({
|
|
2203
|
+
operation,
|
|
2204
|
+
message: `OAuth device authorization ${operation} failed`,
|
|
2205
|
+
cause,
|
|
2206
|
+
});
|
|
2207
|
+
const oauthDeviceCodeTokenCommitStoreError = (cause) => new OAuthDeviceCodeTokenCommitStoreError({
|
|
2208
|
+
operation: "commit",
|
|
2209
|
+
message: "OAuth device token commit failed",
|
|
2210
|
+
cause,
|
|
2211
|
+
});
|
|
2101
2212
|
const oauthClientSecretStoreError = (operation, cause) => new OAuthClientSecretStoreError({
|
|
2102
2213
|
operation,
|
|
2103
2214
|
message: `OAuth client secret ${operation} failed`,
|
|
@@ -3694,6 +3805,250 @@ export const makeEffectQbPostgresOAuthAuthorizationCodeStore = (executor, sql) =
|
|
|
3694
3805
|
},
|
|
3695
3806
|
});
|
|
3696
3807
|
};
|
|
3808
|
+
export const makeEffectQbPostgresOAuthDeviceAuthorizationStore = (executor, sql) => {
|
|
3809
|
+
const find = (column, value, operation) => {
|
|
3810
|
+
const mapError = (cause) => oauthDeviceAuthorizationStoreError(operation, cause);
|
|
3811
|
+
return executeMapped(executor, () => Query.select(oauthDeviceAuthorizationSelection).pipe(Query.from(authPostgresOAuthDeviceAuthorization), Query.where(eq(column, value))), mapError).pipe(Effect.flatMap((rows) => decodeMapped(() => firstOption(rows, decodeOAuthDeviceAuthorization), mapError)));
|
|
3812
|
+
};
|
|
3813
|
+
const selectedColumns = `
|
|
3814
|
+
case when u.id is null then c.id else u.id end as id,
|
|
3815
|
+
case when u.id is null then c.device_code_hash else u.device_code_hash end as device_code_hash,
|
|
3816
|
+
case when u.id is null then c.user_code_hash else u.user_code_hash end as user_code_hash,
|
|
3817
|
+
case when u.id is null then c.client_id else u.client_id end as client_id,
|
|
3818
|
+
case when u.id is null then c.requested_scopes else u.requested_scopes end as requested_scopes,
|
|
3819
|
+
case when u.id is null then c.granted_scopes else u.granted_scopes end as granted_scopes,
|
|
3820
|
+
case when u.id is null then c.subject else u.subject end as subject,
|
|
3821
|
+
case when u.id is null then c.status else u.status end as status,
|
|
3822
|
+
case when u.id is null then c.issued_at else u.issued_at end as issued_at,
|
|
3823
|
+
case when u.id is null then c.expires_at else u.expires_at end as expires_at,
|
|
3824
|
+
case when u.id is null then c.poll_interval_seconds else u.poll_interval_seconds end as poll_interval_seconds,
|
|
3825
|
+
case when u.id is null then c.next_poll_at else u.next_poll_at end as next_poll_at,
|
|
3826
|
+
case when u.id is null then c.last_polled_at else u.last_polled_at end as last_polled_at,
|
|
3827
|
+
case when u.id is null then c.approved_at else u.approved_at end as approved_at,
|
|
3828
|
+
case when u.id is null then c.denied_at else u.denied_at end as denied_at,
|
|
3829
|
+
case when u.id is null then c.consumed_at else u.consumed_at end as consumed_at,
|
|
3830
|
+
case when u.id is null then c.metadata else u.metadata end as metadata`;
|
|
3831
|
+
return OAuthDeviceAuthorizationStore.of({
|
|
3832
|
+
insert: (record) => Effect.suspend(() => execute(executor, insert(authPostgresOAuthDeviceAuthorization, oauthDeviceAuthorizationValues(record)).pipe(Query.returning({ id: authPostgresOAuthDeviceAuthorization.id })))).pipe(Effect.as(true), Effect.catch((error) => postgresErrorDetails(error).code === "23505"
|
|
3833
|
+
? Effect.succeed(false)
|
|
3834
|
+
: Effect.fail(oauthDeviceAuthorizationStoreError("insert", error)))),
|
|
3835
|
+
findByDeviceCodeHash: (input) => find(authPostgresOAuthDeviceAuthorization.device_code_hash, input.deviceCodeHash, "find-by-device-code-hash"),
|
|
3836
|
+
findByUserCodeHash: (input) => find(authPostgresOAuthDeviceAuthorization.user_code_hash, input.userCodeHash, "find-by-user-code-hash"),
|
|
3837
|
+
findById: (id) => find(authPostgresOAuthDeviceAuthorization.id, id, "find-by-id"),
|
|
3838
|
+
decide: (input) => {
|
|
3839
|
+
const mapError = (cause) => oauthDeviceAuthorizationStoreError("decide", cause);
|
|
3840
|
+
const status = input.decision === "approve" ? "approved" : "denied";
|
|
3841
|
+
const subject = input.decision === "approve" ? input.subject : null;
|
|
3842
|
+
const grantedScopes = input.decision === "approve"
|
|
3843
|
+
? JSON.stringify(input.grantedScopes)
|
|
3844
|
+
: null;
|
|
3845
|
+
return sql
|
|
3846
|
+
.unsafe(`with candidate as materialized (
|
|
3847
|
+
select * from auth_oauth_device_authorization where id = $1 for update
|
|
3848
|
+
), updated as (
|
|
3849
|
+
update auth_oauth_device_authorization d set
|
|
3850
|
+
status = $3,
|
|
3851
|
+
subject = $4,
|
|
3852
|
+
granted_scopes = $5::jsonb,
|
|
3853
|
+
approved_at = case when $3 = 'approved' then $2::bigint else null end,
|
|
3854
|
+
denied_at = case when $3 = 'denied' then $2::bigint else null end,
|
|
3855
|
+
metadata = coalesce($6::jsonb, c.metadata)
|
|
3856
|
+
from candidate c
|
|
3857
|
+
where d.id = c.id and c.status = 'pending' and c.expires_at > $2::bigint
|
|
3858
|
+
returning d.*
|
|
3859
|
+
)
|
|
3860
|
+
select
|
|
3861
|
+
case when u.id is not null then $3 when c.expires_at <= $2::bigint then 'expired' else 'already_decided' end as action,
|
|
3862
|
+
${selectedColumns}
|
|
3863
|
+
from candidate c left join updated u on true`, [
|
|
3864
|
+
input.id,
|
|
3865
|
+
millisParam(input.decidedAt),
|
|
3866
|
+
status,
|
|
3867
|
+
subject,
|
|
3868
|
+
grantedScopes,
|
|
3869
|
+
input.metadata === undefined
|
|
3870
|
+
? null
|
|
3871
|
+
: JSON.stringify(input.metadata),
|
|
3872
|
+
])
|
|
3873
|
+
.pipe(Effect.mapError(mapError), Effect.flatMap((rows) => decodeMapped(() => {
|
|
3874
|
+
const [row] = rows;
|
|
3875
|
+
if (row === undefined) {
|
|
3876
|
+
return { status: "missing" };
|
|
3877
|
+
}
|
|
3878
|
+
const record = decodeOAuthDeviceAuthorization(row);
|
|
3879
|
+
if (row.action === "expired") {
|
|
3880
|
+
return { status: "expired", record };
|
|
3881
|
+
}
|
|
3882
|
+
if (row.action === "already_decided") {
|
|
3883
|
+
return {
|
|
3884
|
+
status: "already_decided",
|
|
3885
|
+
record: record,
|
|
3886
|
+
};
|
|
3887
|
+
}
|
|
3888
|
+
return record.status === "approved"
|
|
3889
|
+
? { status: "approved", record }
|
|
3890
|
+
: {
|
|
3891
|
+
status: "denied",
|
|
3892
|
+
record: record,
|
|
3893
|
+
};
|
|
3894
|
+
}, mapError)));
|
|
3895
|
+
},
|
|
3896
|
+
poll: (input) => {
|
|
3897
|
+
const mapError = (cause) => oauthDeviceAuthorizationStoreError("poll", cause);
|
|
3898
|
+
return sql
|
|
3899
|
+
.unsafe(`with candidate as materialized (
|
|
3900
|
+
select d.*,
|
|
3901
|
+
case
|
|
3902
|
+
when d.consumed_at is not null then 'already_consumed'
|
|
3903
|
+
when d.expires_at <= $3::bigint then 'expired'
|
|
3904
|
+
when d.status = 'denied' then 'denied'
|
|
3905
|
+
when d.status = 'approved' then 'approved'
|
|
3906
|
+
when d.next_poll_at > $3::bigint then 'slow_down'
|
|
3907
|
+
when d.status = 'pending' then 'pending'
|
|
3908
|
+
else 'pending'
|
|
3909
|
+
end as action
|
|
3910
|
+
from auth_oauth_device_authorization d
|
|
3911
|
+
where d.device_code_hash = $1 and d.client_id = $2
|
|
3912
|
+
for update
|
|
3913
|
+
), updated as (
|
|
3914
|
+
update auth_oauth_device_authorization d set
|
|
3915
|
+
poll_interval_seconds = case when c.action = 'slow_down' then c.poll_interval_seconds + 5 else c.poll_interval_seconds end,
|
|
3916
|
+
next_poll_at = $3::bigint + (case when c.action = 'slow_down' then c.poll_interval_seconds + 5 else c.poll_interval_seconds end) * 1000,
|
|
3917
|
+
last_polled_at = $3::bigint
|
|
3918
|
+
from candidate c
|
|
3919
|
+
where d.id = c.id and c.action in ('slow_down', 'pending')
|
|
3920
|
+
returning d.*
|
|
3921
|
+
)
|
|
3922
|
+
select c.action, ${selectedColumns}
|
|
3923
|
+
from candidate c left join updated u on true`, [input.deviceCodeHash, input.clientId, millisParam(input.now)])
|
|
3924
|
+
.pipe(Effect.mapError(mapError), Effect.flatMap((rows) => decodeMapped(() => {
|
|
3925
|
+
const [row] = rows;
|
|
3926
|
+
if (row === undefined) {
|
|
3927
|
+
return { status: "missing" };
|
|
3928
|
+
}
|
|
3929
|
+
const record = decodeOAuthDeviceAuthorization(row);
|
|
3930
|
+
switch (row.action) {
|
|
3931
|
+
case "expired": {
|
|
3932
|
+
return { status: "expired", record };
|
|
3933
|
+
}
|
|
3934
|
+
case "denied": {
|
|
3935
|
+
return {
|
|
3936
|
+
status: "denied",
|
|
3937
|
+
record: record,
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3940
|
+
case "already_consumed": {
|
|
3941
|
+
return {
|
|
3942
|
+
status: "already_consumed",
|
|
3943
|
+
record: record,
|
|
3944
|
+
};
|
|
3945
|
+
}
|
|
3946
|
+
case "slow_down": {
|
|
3947
|
+
return {
|
|
3948
|
+
status: "slow_down",
|
|
3949
|
+
record,
|
|
3950
|
+
interval: record.pollIntervalSeconds,
|
|
3951
|
+
};
|
|
3952
|
+
}
|
|
3953
|
+
case "pending": {
|
|
3954
|
+
return {
|
|
3955
|
+
status: "pending",
|
|
3956
|
+
record: record,
|
|
3957
|
+
};
|
|
3958
|
+
}
|
|
3959
|
+
default: {
|
|
3960
|
+
return {
|
|
3961
|
+
status: "approved",
|
|
3962
|
+
record: record,
|
|
3963
|
+
};
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
}, mapError)));
|
|
3967
|
+
},
|
|
3968
|
+
deleteExpired: (input) => {
|
|
3969
|
+
const mapError = (cause) => oauthDeviceAuthorizationStoreError("delete-expired", cause);
|
|
3970
|
+
return executeMapped(executor, () => Query.delete(authPostgresOAuthDeviceAuthorization).pipe(Query.where(lte(authPostgresOAuthDeviceAuthorization.expires_at, millisParam(input.beforeExpiresAt))), Query.returning({ id: authPostgresOAuthDeviceAuthorization.id })), mapError).pipe(Effect.map((rows) => rows.length));
|
|
3971
|
+
},
|
|
3972
|
+
});
|
|
3973
|
+
};
|
|
3974
|
+
export const makeEffectQbPostgresOAuthDeviceCodeTokenCommitStore = (sql) => {
|
|
3975
|
+
const executor = makeEffectQbPostgresExecutor(sql);
|
|
3976
|
+
const mapError = oauthDeviceCodeTokenCommitStoreError;
|
|
3977
|
+
return OAuthDeviceCodeTokenCommitStore.of({
|
|
3978
|
+
commit: (input) => sql
|
|
3979
|
+
.withTransaction(Effect.gen(function* () {
|
|
3980
|
+
const selected = yield* executeMapped(executor, () => selectForUpdate(oauthDeviceAuthorizationSelection, authPostgresOAuthDeviceAuthorization, eq(authPostgresOAuthDeviceAuthorization.id, input.authorization.id)), mapError);
|
|
3981
|
+
const existing = yield* decodeMapped(() => selected[0] === undefined
|
|
3982
|
+
? undefined
|
|
3983
|
+
: decodeOAuthDeviceAuthorization(selected[0]), mapError);
|
|
3984
|
+
if (existing === undefined) {
|
|
3985
|
+
return { status: "missing" };
|
|
3986
|
+
}
|
|
3987
|
+
if (existing.status !== "approved") {
|
|
3988
|
+
return { status: "missing" };
|
|
3989
|
+
}
|
|
3990
|
+
if (existing.consumedAt !== undefined) {
|
|
3991
|
+
return {
|
|
3992
|
+
status: "already_consumed",
|
|
3993
|
+
record: existing,
|
|
3994
|
+
};
|
|
3995
|
+
}
|
|
3996
|
+
const expected = input.authorization;
|
|
3997
|
+
const sameArray = (left, right) => left.length === right.length &&
|
|
3998
|
+
left.every((value, index) => value === right[index]);
|
|
3999
|
+
const matchesApprovedSnapshot = existing.id === expected.id &&
|
|
4000
|
+
existing.deviceCodeHash === expected.deviceCodeHash &&
|
|
4001
|
+
existing.userCodeHash === expected.userCodeHash &&
|
|
4002
|
+
existing.clientId === expected.clientId &&
|
|
4003
|
+
sameArray(existing.requestedScopes, expected.requestedScopes) &&
|
|
4004
|
+
existing.subject === expected.subject &&
|
|
4005
|
+
sameArray(existing.grantedScopes, expected.grantedScopes) &&
|
|
4006
|
+
Number(existing.issuedAt) === Number(expected.issuedAt) &&
|
|
4007
|
+
Number(existing.expiresAt) === Number(expected.expiresAt) &&
|
|
4008
|
+
existing.pollIntervalSeconds === expected.pollIntervalSeconds &&
|
|
4009
|
+
Number(existing.nextPollAt) === Number(expected.nextPollAt) &&
|
|
4010
|
+
(existing.lastPolledAt === undefined
|
|
4011
|
+
? expected.lastPolledAt === undefined
|
|
4012
|
+
: Number(existing.lastPolledAt) ===
|
|
4013
|
+
Number(expected.lastPolledAt)) &&
|
|
4014
|
+
Number(existing.approvedAt) === Number(expected.approvedAt) &&
|
|
4015
|
+
JSON.stringify(existing.metadata ?? null) ===
|
|
4016
|
+
JSON.stringify(expected.metadata ?? null);
|
|
4017
|
+
if (!matchesApprovedSnapshot) {
|
|
4018
|
+
return { status: "missing" };
|
|
4019
|
+
}
|
|
4020
|
+
if (Number(existing.expiresAt) <= Number(input.consumedAt)) {
|
|
4021
|
+
return {
|
|
4022
|
+
status: "expired",
|
|
4023
|
+
record: existing,
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
yield* Effect.forEach(input.tokenRecords, (record) => executeMapped(executor, () => insert(authPostgresOAuthProviderModeToken, oauthProviderModeTokenValues(record)), mapError), { discard: true });
|
|
4027
|
+
const updated = yield* executeMapped(executor, () => update(authPostgresOAuthDeviceAuthorization, {
|
|
4028
|
+
consumed_at: millisParam(input.consumedAt),
|
|
4029
|
+
last_polled_at: millisParam(input.consumedAt),
|
|
4030
|
+
next_poll_at: millisParam(UnixMillis(Number(input.consumedAt) +
|
|
4031
|
+
existing.pollIntervalSeconds * 1000)),
|
|
4032
|
+
}).pipe(Query.where(eq(authPostgresOAuthDeviceAuthorization.id, input.authorization.id)), Query.where(eq(authPostgresOAuthDeviceAuthorization.status, "approved")), Query.where(isNull(authPostgresOAuthDeviceAuthorization.consumed_at)), Query.where(gt(authPostgresOAuthDeviceAuthorization.expires_at, millisParam(input.consumedAt))), Query.returning({
|
|
4033
|
+
...oauthDeviceAuthorizationSelection,
|
|
4034
|
+
consumedAt: withoutNullRefinement(authPostgresOAuthDeviceAuthorization.consumed_at),
|
|
4035
|
+
})), mapError);
|
|
4036
|
+
return yield* decodeMapped(() => {
|
|
4037
|
+
const [row] = updated;
|
|
4038
|
+
if (row === undefined) {
|
|
4039
|
+
throw new Error("OAuth device authorization consume CAS failed");
|
|
4040
|
+
}
|
|
4041
|
+
return {
|
|
4042
|
+
status: "committed",
|
|
4043
|
+
record: decodeOAuthDeviceAuthorization(row),
|
|
4044
|
+
};
|
|
4045
|
+
}, mapError);
|
|
4046
|
+
}))
|
|
4047
|
+
.pipe(Effect.mapError((cause) => cause instanceof OAuthDeviceCodeTokenCommitStoreError
|
|
4048
|
+
? cause
|
|
4049
|
+
: mapError(cause))),
|
|
4050
|
+
});
|
|
4051
|
+
};
|
|
3697
4052
|
export const makeEffectQbPostgresOAuthClientSecretStore = (executor) => {
|
|
3698
4053
|
const find = (input, operation = "find") => {
|
|
3699
4054
|
const mapError = (cause) => oauthClientSecretStoreError(operation, cause);
|
|
@@ -4267,6 +4622,8 @@ export const makeEffectQbPostgresCoreStores = (sql, options = {}) => {
|
|
|
4267
4622
|
oauthClientStore: makeEffectQbPostgresOAuthClientStore(executor),
|
|
4268
4623
|
oauthConsentStore: makeEffectQbPostgresOAuthConsentStore(executor),
|
|
4269
4624
|
oauthAuthorizationCodeStore: makeEffectQbPostgresOAuthAuthorizationCodeStore(executor, sql),
|
|
4625
|
+
oauthDeviceAuthorizationStore: makeEffectQbPostgresOAuthDeviceAuthorizationStore(executor, sql),
|
|
4626
|
+
oauthDeviceCodeTokenCommitStore: makeEffectQbPostgresOAuthDeviceCodeTokenCommitStore(sql),
|
|
4270
4627
|
oauthClientSecretStore: makeEffectQbPostgresOAuthClientSecretStore(executor),
|
|
4271
4628
|
oauthProviderModeTokenStore: makeEffectQbPostgresOAuthProviderModeTokenStore(executor),
|
|
4272
4629
|
oauthProviderTokenVault: makeEffectQbPostgresOAuthProviderTokenVault(executor),
|
|
@@ -4275,6 +4632,6 @@ export const makeEffectQbPostgresCoreStores = (sql, options = {}) => {
|
|
|
4275
4632
|
};
|
|
4276
4633
|
export const EffectQbPostgresCoreStoresLive = (sql, options = {}) => {
|
|
4277
4634
|
const stores = makeEffectQbPostgresCoreStores(sql, options);
|
|
4278
|
-
return Layer.mergeAll(Layer.succeed(UserStore)(stores.userStore), Layer.succeed(IdentityStore)(stores.identityStore), Layer.succeed(RegistrationStore)(stores.registrationStore), Layer.succeed(CredentialStore)(stores.credentialStore), Layer.succeed(PasskeyCredentialStore)(stores.passkeyCredentialStore), Layer.succeed(SessionStore)(stores.sessionStore), Layer.succeed(VerificationStore)(stores.verificationStore), Layer.succeed(AuditLogStore)(stores.auditLogStore), Layer.succeed(LoginApprovalReviewStore)(stores.loginApprovalReviewStore), Layer.succeed(LoginRiskHistory)(stores.loginRiskHistoryStore), Layer.succeed(TrustedDeviceStore)(stores.trustedDeviceStore), Layer.succeed(DomainVerificationStore)(stores.domainVerificationStore), Layer.succeed(SecurityTimelineStore)(stores.securityTimelineStore), Layer.succeed(WebhookOutboxStore)(stores.webhookOutboxStore), Layer.succeed(WebhookReplayStore)(stores.webhookReplayStore), Layer.succeed(TotpFactorStore)(stores.totpFactorStore), Layer.succeed(TotpSessionRotateStore)(stores.totpSessionRotateStore), Layer.succeed(TotpLoginCommitStore)(stores.totpLoginCommitStore), Layer.succeed(RecoveryCodeStore)(stores.recoveryCodeStore), Layer.succeed(RecoveryCodeSessionRotateStore)(stores.recoveryCodeSessionRotateStore), Layer.succeed(RecoveryCodeLoginCommitStore)(stores.recoveryCodeLoginCommitStore), Layer.succeed(ApiKeyStore)(stores.apiKeyStore), Layer.succeed(RefreshTokenStore)(stores.refreshTokenStore), Layer.succeed(JwtRevocationStore)(stores.jwtRevocationStore), Layer.succeed(OAuthAccountStore)(stores.oauthAccountStore), Layer.succeed(OAuthIdentityBridgeStore)(stores.oauthIdentityBridgeStore), Layer.succeed(OAuthClientStore)(stores.oauthClientStore), Layer.succeed(OAuthConsentStore)(stores.oauthConsentStore), Layer.succeed(OAuthAuthorizationCodeStore)(stores.oauthAuthorizationCodeStore), Layer.succeed(OAuthClientSecretStore)(stores.oauthClientSecretStore), Layer.succeed(OAuthProviderModeTokenStore)(stores.oauthProviderModeTokenStore), Layer.succeed(OAuthProviderTokenVault)(stores.oauthProviderTokenVault), Layer.succeed(PermissionStore)(stores.permissionStore));
|
|
4635
|
+
return Layer.mergeAll(Layer.succeed(UserStore)(stores.userStore), Layer.succeed(IdentityStore)(stores.identityStore), Layer.succeed(RegistrationStore)(stores.registrationStore), Layer.succeed(CredentialStore)(stores.credentialStore), Layer.succeed(PasskeyCredentialStore)(stores.passkeyCredentialStore), Layer.succeed(SessionStore)(stores.sessionStore), Layer.succeed(VerificationStore)(stores.verificationStore), Layer.succeed(AuditLogStore)(stores.auditLogStore), Layer.succeed(LoginApprovalReviewStore)(stores.loginApprovalReviewStore), Layer.succeed(LoginRiskHistory)(stores.loginRiskHistoryStore), Layer.succeed(TrustedDeviceStore)(stores.trustedDeviceStore), Layer.succeed(DomainVerificationStore)(stores.domainVerificationStore), Layer.succeed(SecurityTimelineStore)(stores.securityTimelineStore), Layer.succeed(WebhookOutboxStore)(stores.webhookOutboxStore), Layer.succeed(WebhookReplayStore)(stores.webhookReplayStore), Layer.succeed(TotpFactorStore)(stores.totpFactorStore), Layer.succeed(TotpSessionRotateStore)(stores.totpSessionRotateStore), Layer.succeed(TotpLoginCommitStore)(stores.totpLoginCommitStore), Layer.succeed(RecoveryCodeStore)(stores.recoveryCodeStore), Layer.succeed(RecoveryCodeSessionRotateStore)(stores.recoveryCodeSessionRotateStore), Layer.succeed(RecoveryCodeLoginCommitStore)(stores.recoveryCodeLoginCommitStore), Layer.succeed(ApiKeyStore)(stores.apiKeyStore), Layer.succeed(RefreshTokenStore)(stores.refreshTokenStore), Layer.succeed(JwtRevocationStore)(stores.jwtRevocationStore), Layer.succeed(OAuthAccountStore)(stores.oauthAccountStore), Layer.succeed(OAuthIdentityBridgeStore)(stores.oauthIdentityBridgeStore), Layer.succeed(OAuthClientStore)(stores.oauthClientStore), Layer.succeed(OAuthConsentStore)(stores.oauthConsentStore), Layer.succeed(OAuthAuthorizationCodeStore)(stores.oauthAuthorizationCodeStore), Layer.succeed(OAuthDeviceAuthorizationStore)(stores.oauthDeviceAuthorizationStore), Layer.succeed(OAuthDeviceCodeTokenCommitStore)(stores.oauthDeviceCodeTokenCommitStore), Layer.succeed(OAuthClientSecretStore)(stores.oauthClientSecretStore), Layer.succeed(OAuthProviderModeTokenStore)(stores.oauthProviderModeTokenStore), Layer.succeed(OAuthProviderTokenVault)(stores.oauthProviderTokenVault), Layer.succeed(PermissionStore)(stores.permissionStore));
|
|
4279
4636
|
};
|
|
4280
4637
|
//# sourceMappingURL=EffectQbPostgresStorage.js.map
|