@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.24
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 +11 -0
- package/dist/Adapter.d.mts +13 -5
- package/dist/Adapter.mjs +9 -2
- package/dist/OAuthPersistence.d.mts +87 -0
- package/dist/OAuthPersistence.mjs +125 -0
- package/dist/OAuthPersistence.mjs.map +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/dist/internal/authority-snapshot.mjs +53 -0
- package/dist/internal/authority-snapshot.mjs.map +1 -0
- package/dist/internal/configuration.d.mts +1 -1
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/models/oauth-connected-model.d.mts +294 -0
- package/dist/internal/models/oauth-connected-model.mjs +22 -0
- package/dist/internal/models/oauth-connected-model.mjs.map +1 -0
- package/dist/internal/models/oauth-model.d.mts +467 -0
- package/dist/internal/models/oauth-model.mjs +49 -0
- package/dist/internal/models/oauth-model.mjs.map +1 -0
- package/dist/internal/models/password-model.d.mts +4 -97
- package/dist/internal/models/password-model.mjs +0 -3
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/native-sql-table.d.mts +34 -0
- package/dist/internal/native-sql-table.mjs +111 -0
- package/dist/internal/native-sql-table.mjs.map +1 -0
- package/dist/internal/oauth/accounts.d.mts +8 -0
- package/dist/internal/oauth/accounts.mjs +379 -0
- package/dist/internal/oauth/accounts.mjs.map +1 -0
- package/dist/internal/oauth/connected-access.d.mts +9 -0
- package/dist/internal/oauth/connected-access.mjs +229 -0
- package/dist/internal/oauth/connected-access.mjs.map +1 -0
- package/dist/internal/oauth/connected-collection.d.mts +7 -0
- package/dist/internal/oauth/connected-collection.mjs +214 -0
- package/dist/internal/oauth/connected-collection.mjs.map +1 -0
- package/dist/internal/oauth/connected-custody.d.mts +6 -0
- package/dist/internal/oauth/connected-custody.mjs +165 -0
- package/dist/internal/oauth/connected-custody.mjs.map +1 -0
- package/dist/internal/oauth/connected-flow.d.mts +7 -0
- package/dist/internal/oauth/connected-flow.mjs +324 -0
- package/dist/internal/oauth/connected-flow.mjs.map +1 -0
- package/dist/internal/oauth/connected-input.mjs +122 -0
- package/dist/internal/oauth/connected-input.mjs.map +1 -0
- package/dist/internal/oauth/connected-keys.d.mts +2 -0
- package/dist/internal/oauth/connected-keys.mjs +22 -0
- package/dist/internal/oauth/connected-keys.mjs.map +1 -0
- package/dist/internal/oauth/connected-maintenance.d.mts +9 -0
- package/dist/internal/oauth/connected-maintenance.mjs +226 -0
- package/dist/internal/oauth/connected-maintenance.mjs.map +1 -0
- package/dist/internal/oauth/connected-management.d.mts +9 -0
- package/dist/internal/oauth/connected-management.mjs +243 -0
- package/dist/internal/oauth/connected-management.mjs.map +1 -0
- package/dist/internal/oauth/connected-reference.mjs +63 -0
- package/dist/internal/oauth/connected-reference.mjs.map +1 -0
- package/dist/internal/oauth/connected-settlement.d.mts +8 -0
- package/dist/internal/oauth/connected-settlement.mjs +141 -0
- package/dist/internal/oauth/connected-settlement.mjs.map +1 -0
- package/dist/internal/oauth/connected-sign-in.d.mts +10 -0
- package/dist/internal/oauth/connected-sign-in.mjs +281 -0
- package/dist/internal/oauth/connected-sign-in.mjs.map +1 -0
- package/dist/internal/oauth/connected-state.d.mts +14 -0
- package/dist/internal/oauth/connected-state.mjs +469 -0
- package/dist/internal/oauth/connected-state.mjs.map +1 -0
- package/dist/internal/oauth/connected-target.d.mts +21 -0
- package/dist/internal/oauth/connected-target.mjs +117 -0
- package/dist/internal/oauth/connected-target.mjs.map +1 -0
- package/dist/internal/oauth/flow.d.mts +5 -0
- package/dist/internal/oauth/flow.mjs +279 -0
- package/dist/internal/oauth/flow.mjs.map +1 -0
- package/dist/internal/oauth/input.mjs +127 -0
- package/dist/internal/oauth/input.mjs.map +1 -0
- package/dist/internal/oauth/owner.d.mts +10 -0
- package/dist/internal/oauth/owner.mjs +10 -0
- package/dist/internal/oauth/owner.mjs.map +1 -0
- package/dist/internal/oauth/query-compiler.d.mts +7 -0
- package/dist/internal/oauth/query-compiler.mjs +9 -0
- package/dist/internal/oauth/query-compiler.mjs.map +1 -0
- package/dist/internal/oauth/registration.d.mts +7 -0
- package/dist/internal/oauth/registration.mjs +393 -0
- package/dist/internal/oauth/registration.mjs.map +1 -0
- package/dist/internal/oauth/state.mjs +107 -0
- package/dist/internal/oauth/state.mjs.map +1 -0
- package/dist/internal/oauth/target.d.mts +31 -0
- package/dist/internal/oauth/target.mjs +273 -0
- package/dist/internal/oauth/target.mjs.map +1 -0
- package/dist/internal/oauth-kernel.d.mts +1423 -0
- package/dist/internal/oauth-kernel.mjs +187 -0
- package/dist/internal/oauth-kernel.mjs.map +1 -0
- package/dist/internal/oauth-proxy.d.mts +31 -0
- package/dist/internal/oauth-proxy.mjs +129 -0
- package/dist/internal/oauth-proxy.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +1 -1
- package/dist/internal/passkey-kernel.d.mts +5 -5
- package/dist/internal/passkeys.mjs +1 -1
- package/dist/internal/password-credentials.d.mts +11 -0
- package/dist/internal/password-credentials.mjs +121 -0
- package/dist/internal/password-credentials.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +15 -28
- package/dist/internal/password-kernel.mjs +35 -236
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +2 -0
- package/dist/internal/persistence.mjs +5 -5
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/query-operations.d.mts +2 -0
- package/dist/internal/session-kernel.d.mts +5 -3
- package/dist/internal/session-kernel.mjs +95 -29
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-oauth-connected-model.d.mts +29 -0
- package/dist/internal/sql-oauth-connected-model.mjs +7 -0
- package/dist/internal/sql-oauth-connected-model.mjs.map +1 -0
- package/dist/internal/sql-oauth-model.d.mts +60 -0
- package/dist/internal/sql-oauth-model.mjs +10 -0
- package/dist/internal/sql-oauth-model.mjs.map +1 -0
- package/dist/internal/sql-persistence.mjs +3 -1
- package/dist/internal/sql-persistence.mjs.map +1 -1
- package/dist/internal/sql-query.d.mts +12 -0
- package/dist/internal/sql-query.mjs +15 -18
- package/dist/internal/sql-query.mjs.map +1 -1
- package/dist/internal/sql-storage-validation.mjs +54 -0
- package/dist/internal/sql-storage-validation.mjs.map +1 -0
- package/dist/internal/storage-mapping.mjs +2 -67
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-plans.d.mts +7 -0
- package/dist/internal/storage-plans.mjs +189 -0
- package/dist/internal/storage-plans.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +0 -99
- package/dist/internal/storage-tables.mjs +0 -43
- package/dist/internal/storage-tables.mjs.map +1 -1
- package/dist/internal/storage-validation.d.mts +6 -1
- package/dist/internal/storage-validation.mjs +42 -1
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/transaction-execution-kernel.d.mts +1 -1
- package/dist/internal/transaction-kernel.d.mts +1 -1
- package/dist/internal/transaction-kernel.mjs +3 -2
- package/dist/internal/transaction-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +97 -8
- package/src/OAuthPersistence.ts +427 -0
- package/src/index.ts +3 -0
- package/src/internal/authority-snapshot.ts +91 -0
- package/src/internal/configuration.ts +1 -8
- package/src/internal/models/oauth-connected-model.ts +396 -0
- package/src/internal/models/oauth-model.ts +708 -0
- package/src/internal/models/password-model.ts +0 -120
- package/src/internal/native-sql-table.ts +178 -0
- package/src/internal/oauth/accounts.ts +838 -0
- package/src/internal/oauth/connected-access.ts +388 -0
- package/src/internal/oauth/connected-collection.ts +345 -0
- package/src/internal/oauth/connected-custody.ts +308 -0
- package/src/internal/oauth/connected-flow.ts +562 -0
- package/src/internal/oauth/connected-input.ts +147 -0
- package/src/internal/oauth/connected-keys.ts +31 -0
- package/src/internal/oauth/connected-maintenance.ts +379 -0
- package/src/internal/oauth/connected-management.ts +391 -0
- package/src/internal/oauth/connected-reference.ts +135 -0
- package/src/internal/oauth/connected-settlement.ts +278 -0
- package/src/internal/oauth/connected-sign-in.ts +472 -0
- package/src/internal/oauth/connected-state.ts +924 -0
- package/src/internal/oauth/connected-target.ts +307 -0
- package/src/internal/oauth/flow.ts +505 -0
- package/src/internal/oauth/input.ts +201 -0
- package/src/internal/oauth/owner.ts +22 -0
- package/src/internal/oauth/query-compiler.ts +15 -0
- package/src/internal/oauth/registration.ts +676 -0
- package/src/internal/oauth/state.ts +218 -0
- package/src/internal/oauth/target.ts +803 -0
- package/src/internal/oauth-kernel.ts +294 -0
- package/src/internal/oauth-proxy.ts +176 -0
- package/src/internal/password-credentials.ts +245 -0
- package/src/internal/password-kernel.ts +98 -712
- package/src/internal/persistence.ts +5 -10
- package/src/internal/query-operations.ts +2 -0
- package/src/internal/session-kernel.ts +231 -81
- package/src/internal/sql-oauth-connected-model.ts +145 -0
- package/src/internal/sql-oauth-model.ts +263 -0
- package/src/internal/sql-persistence.ts +2 -0
- package/src/internal/sql-query.ts +16 -13
- package/src/internal/sql-storage-validation.ts +64 -0
- package/src/internal/storage-mapping.ts +0 -66
- package/src/internal/storage-plans.ts +218 -0
- package/src/internal/storage-tables.ts +0 -35
- package/src/internal/storage-validation.ts +58 -0
- package/src/internal/transaction-kernel.ts +6 -2
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { randomId } from "./crypto";
|
|
30
30
|
import { makeEmailKernel, CurrentEmailSql, type EmailSqlDatabase } from "./email-kernel";
|
|
31
31
|
import { PersistenceMappingError } from "./mapping-error";
|
|
32
|
+
import type { NativeSqlTables } from "./native-sql-table";
|
|
32
33
|
import {
|
|
33
34
|
makePasswordKernel,
|
|
34
35
|
CurrentPasswordSql,
|
|
@@ -69,6 +70,7 @@ export interface Backend<T extends object, R> {
|
|
|
69
70
|
readonly describe: (table: T) => StorageTable;
|
|
70
71
|
readonly acquire: Effect.Effect<object, PersistenceConfigurationError, R | SqlClient.SqlClient>;
|
|
71
72
|
readonly operations: QueryOperations;
|
|
73
|
+
readonly nativeTables: (client: SqlClient.SqlClient, database: object) => NativeSqlTables;
|
|
72
74
|
readonly passkeys: (input: {
|
|
73
75
|
readonly storage: MappingInput;
|
|
74
76
|
readonly namespace: string;
|
|
@@ -112,9 +114,9 @@ export const createPersistence = <T extends object, R>(
|
|
|
112
114
|
backend: Backend<T, R>,
|
|
113
115
|
): PersistenceApi<T, R> => {
|
|
114
116
|
const proofKernel = makeProofKernel(backend.operations);
|
|
115
|
-
const passwordKernel = makePasswordKernel(backend.operations, proofKernel);
|
|
117
|
+
const passwordKernel = makePasswordKernel(backend.operations, proofKernel, backend.nativeTables);
|
|
116
118
|
const emailKernel = makeEmailKernel(backend.operations, proofKernel);
|
|
117
|
-
const sessionKernel = makeSessionKernel(backend.operations);
|
|
119
|
+
const sessionKernel = makeSessionKernel(backend.operations, backend.nativeTables);
|
|
118
120
|
const transactionKernel = makeTransactionKernel(backend.operations);
|
|
119
121
|
const executionKernel = makeTransactionExecutionKernel(transactionKernel);
|
|
120
122
|
const phoneKernel = makePhoneKernel(backend.operations, transactionKernel);
|
|
@@ -139,14 +141,7 @@ export const createPersistence = <T extends object, R>(
|
|
|
139
141
|
|
|
140
142
|
const roles: StorageRole[] = ["identifiers", "credentials", "sessions", "sessionFlows"];
|
|
141
143
|
|
|
142
|
-
if (password)
|
|
143
|
-
roles.push(
|
|
144
|
-
"passwords",
|
|
145
|
-
"passwordAttempts",
|
|
146
|
-
"passwordScopes",
|
|
147
|
-
"passwordCharges",
|
|
148
|
-
"passwordCommands",
|
|
149
|
-
);
|
|
144
|
+
if (password) roles.push("passwords", "passwordCommands");
|
|
150
145
|
if (management) roles.push("passwordRegistrations");
|
|
151
146
|
if (email) roles.push("emailCredentials", "emailCommands");
|
|
152
147
|
if (phone) roles.push("phoneState");
|
|
@@ -60,10 +60,12 @@ export interface QueryOperations<
|
|
|
60
60
|
readonly balancedD1And: Predicate<Fragment>;
|
|
61
61
|
readonly asc: (value: unknown) => Fragment;
|
|
62
62
|
readonly eq: Comparison<Fragment>;
|
|
63
|
+
readonly ne: Comparison<Fragment>;
|
|
63
64
|
readonly gt: Comparison<Fragment>;
|
|
64
65
|
readonly gte: Comparison<Fragment>;
|
|
65
66
|
readonly lte: Comparison<Fragment>;
|
|
66
67
|
readonly inArray: (value: unknown, values: ReadonlyArray<unknown>) => Fragment;
|
|
68
|
+
readonly isNotNull: (value: unknown) => Fragment;
|
|
67
69
|
readonly isNull: (value: unknown) => Fragment;
|
|
68
70
|
readonly notExists: (value: SqlExpression) => Fragment;
|
|
69
71
|
readonly sql: {
|
|
@@ -8,6 +8,7 @@ import { reportPersistenceFailure } from "@yielded/auth/Persistence";
|
|
|
8
8
|
import type { SubjectId, TokenDigest } from "@yielded/auth/Schema";
|
|
9
9
|
import {
|
|
10
10
|
assessAuthentication,
|
|
11
|
+
AuthenticationCapture,
|
|
11
12
|
snapshotSessionAuthenticationProvenance,
|
|
12
13
|
PendingAuthenticationInvalid,
|
|
13
14
|
SessionConflict,
|
|
@@ -36,8 +37,10 @@ import {
|
|
|
36
37
|
type SessionStepUpPersistence,
|
|
37
38
|
} from "@yielded/auth/Sessions";
|
|
38
39
|
import { Cause, Context, DateTime, Effect, Option, Schema } from "effect";
|
|
40
|
+
import type { SqlClient } from "effect/sql/SqlClient";
|
|
39
41
|
import type * as SqlError from "effect/sql/SqlError";
|
|
40
42
|
|
|
43
|
+
import { makeAuthoritySnapshot } from "./authority-snapshot";
|
|
41
44
|
import { PersistenceMappingError, isMappedConstraintConflict } from "./mapping-error";
|
|
42
45
|
import type {
|
|
43
46
|
AuthenticationAuthorityMapping,
|
|
@@ -50,6 +53,7 @@ import type {
|
|
|
50
53
|
StatefulSessionMapping,
|
|
51
54
|
} from "./models/session-model";
|
|
52
55
|
import type { SessionStepUpMapping } from "./models/step-up-model";
|
|
56
|
+
import { makeNativeSqlTables, sqlMapping, type NativeSqlTables } from "./native-sql-table";
|
|
53
57
|
/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */
|
|
54
58
|
import type { QueryFailure } from "./query-operations";
|
|
55
59
|
import type { QueryOperations } from "./query-operations";
|
|
@@ -61,7 +65,21 @@ import {
|
|
|
61
65
|
validateStepUpPlan,
|
|
62
66
|
stepUpRotationMatches,
|
|
63
67
|
} from "./step-up-state";
|
|
64
|
-
import
|
|
68
|
+
import { canJoinTextColumns } from "./storage-validation";
|
|
69
|
+
import { NativeDatabase } from "./transaction-kernel";
|
|
70
|
+
|
|
71
|
+
const decodeRevocationFlag = Schema.decodeUnknownEffect(Schema.Literals([0, 1]));
|
|
72
|
+
|
|
73
|
+
/** Reads need no transaction of their own. A caller-owned transaction still
|
|
74
|
+
* needs a savepoint so catching an SQL failure does not leave it aborted. */
|
|
75
|
+
const withVerificationSavepoint = Effect.fnUntraced(function* <A, E, R>(
|
|
76
|
+
effect: Effect.Effect<A, E, R>,
|
|
77
|
+
): Effect.fn.Return<A, E | SqlError.SqlError, R | NativeDatabase> {
|
|
78
|
+
const { $client } = yield* NativeDatabase;
|
|
79
|
+
const transaction = yield* Effect.serviceOption($client.transactionService);
|
|
80
|
+
|
|
81
|
+
return yield* Option.isSome(transaction) ? $client.withTransaction(effect) : effect;
|
|
82
|
+
});
|
|
65
83
|
|
|
66
84
|
type CommitMode = "interactive" | "synchronous";
|
|
67
85
|
|
|
@@ -139,7 +157,11 @@ type SqlStepUpRejectAbsence = SessionStepUpInvalid | StaleAuthentication;
|
|
|
139
157
|
|
|
140
158
|
type SqlStepUpReadFailure = AdapterFailure | SqlStepUpRejectAbsence | SessionUnavailable;
|
|
141
159
|
|
|
142
|
-
export const makeSessionKernel = (
|
|
160
|
+
export const makeSessionKernel = (
|
|
161
|
+
operations: QueryOperations,
|
|
162
|
+
nativeTables: (client: SqlClient, database: object) => NativeSqlTables = (client) =>
|
|
163
|
+
makeNativeSqlTables(client),
|
|
164
|
+
) => {
|
|
143
165
|
const { and, eq, gt, lte, inArray, or, sql, column, updateValues } = operations;
|
|
144
166
|
const unavailable = () => SessionUnavailable.make({});
|
|
145
167
|
|
|
@@ -396,15 +418,6 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
396
418
|
};
|
|
397
419
|
});
|
|
398
420
|
|
|
399
|
-
const captureIn = Effect.fn("DrizzleSession.capture")(function* (
|
|
400
|
-
mapping: AnyAuthorityMapping,
|
|
401
|
-
subjectId: SubjectId,
|
|
402
|
-
credentialIds: ReadonlyArray<string>,
|
|
403
|
-
locking: boolean,
|
|
404
|
-
) {
|
|
405
|
-
return (yield* captureDetailsIn(mapping, subjectId, credentialIds, locking)).revision;
|
|
406
|
-
});
|
|
407
|
-
|
|
408
421
|
const validateEvidenceIn = Effect.fn("DrizzleSession.validateEvidence")(function* (
|
|
409
422
|
mapping: AnyAuthorityMapping,
|
|
410
423
|
evidence: AuthenticationEvidence,
|
|
@@ -417,6 +430,17 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
417
430
|
locking,
|
|
418
431
|
);
|
|
419
432
|
|
|
433
|
+
return yield* validateCapturedEvidence(mapping, evidence, captured);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const validateCapturedEvidence = Effect.fnUntraced(function* (
|
|
437
|
+
mapping: AnyAuthorityMapping,
|
|
438
|
+
evidence: AuthenticationEvidence,
|
|
439
|
+
captured: {
|
|
440
|
+
readonly subject: Record<string, unknown>;
|
|
441
|
+
readonly revision: AuthenticationRevision;
|
|
442
|
+
},
|
|
443
|
+
) {
|
|
420
444
|
if (
|
|
421
445
|
captured.revision.securityRevision !== evidence.revision.securityRevision ||
|
|
422
446
|
captured.revision.credentials.length !== evidence.revision.credentials.length ||
|
|
@@ -643,18 +667,37 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
643
667
|
);
|
|
644
668
|
const hooks = yield* LifecycleHooks;
|
|
645
669
|
|
|
670
|
+
const root = yield* NativeDatabase;
|
|
671
|
+
const sqlClient = root.$client.withoutTransforms();
|
|
672
|
+
|
|
673
|
+
const tables = yield* sqlMapping(() => nativeTables(sqlClient, root)).pipe(
|
|
674
|
+
Effect.mapError(unavailable),
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
const readSnapshot = makeAuthoritySnapshot(tables, mapping);
|
|
678
|
+
|
|
646
679
|
const service = {
|
|
647
680
|
capture: (subjectId: SubjectId, credentialIds: ReadonlyArray<string>) =>
|
|
648
681
|
safeTransaction(
|
|
649
|
-
|
|
682
|
+
readSnapshot(subjectId, credentialIds).pipe(
|
|
683
|
+
Effect.flatMap((value) =>
|
|
684
|
+
Effect.flatMap(mapping.subject.decodeRequirement(value.subject), (requirement) =>
|
|
685
|
+
Schema.decodeEffect(AuthenticationCapture)({
|
|
686
|
+
revision: value.revision,
|
|
687
|
+
requirement,
|
|
688
|
+
}),
|
|
689
|
+
),
|
|
690
|
+
),
|
|
691
|
+
),
|
|
650
692
|
),
|
|
651
693
|
requirements: (evidence: AuthenticationEvidence) =>
|
|
652
694
|
safeTransaction(
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
),
|
|
695
|
+
readSnapshot(
|
|
696
|
+
evidence.revision.subjectId,
|
|
697
|
+
evidence.revision.credentials.map((item) => item.credentialId),
|
|
698
|
+
).pipe(
|
|
699
|
+
Effect.flatMap((captured) => validateCapturedEvidence(mapping, evidence, captured)),
|
|
700
|
+
Effect.map((value) => value.requirement),
|
|
658
701
|
),
|
|
659
702
|
),
|
|
660
703
|
approve: <A>(
|
|
@@ -667,7 +710,20 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
667
710
|
mapping.isConstraintConflict,
|
|
668
711
|
Effect.gen(function* () {
|
|
669
712
|
const journal = yield* CurrentCommitJournal;
|
|
670
|
-
|
|
713
|
+
|
|
714
|
+
// With row locks, lock the subject before its credentials in separate
|
|
715
|
+
// reads: a joined FOR UPDATE does not fix that order on every table
|
|
716
|
+
// layout, and a coordinated application may already hold the subject
|
|
717
|
+
// lock. Without row locks one native snapshot is already serialized.
|
|
718
|
+
const { requirement } = yield* options.locking
|
|
719
|
+
? validateEvidenceIn(mapping, input.evidence, true)
|
|
720
|
+
: Effect.flatMap(
|
|
721
|
+
readSnapshot(
|
|
722
|
+
input.evidence.revision.subjectId,
|
|
723
|
+
input.evidence.revision.credentials.map((item) => item.credentialId),
|
|
724
|
+
),
|
|
725
|
+
(captured) => validateCapturedEvidence(mapping, input.evidence, captured),
|
|
726
|
+
);
|
|
671
727
|
|
|
672
728
|
if (input.pending === undefined) {
|
|
673
729
|
const assessed = yield* assessAuthentication(input.evidence, requirement).pipe(
|
|
@@ -720,11 +776,24 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
720
776
|
capture: (subjectId, credentialIds) =>
|
|
721
777
|
service
|
|
722
778
|
.capture(subjectId, credentialIds)
|
|
723
|
-
.pipe(
|
|
779
|
+
.pipe(
|
|
780
|
+
Effect.provideService(NativeDatabase, root),
|
|
781
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
782
|
+
),
|
|
724
783
|
requirements: (evidence) =>
|
|
725
|
-
service
|
|
784
|
+
service
|
|
785
|
+
.requirements(evidence)
|
|
786
|
+
.pipe(
|
|
787
|
+
Effect.provideService(NativeDatabase, root),
|
|
788
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
789
|
+
),
|
|
726
790
|
approve: (input, prepare) =>
|
|
727
|
-
service
|
|
791
|
+
service
|
|
792
|
+
.approve(input, prepare)
|
|
793
|
+
.pipe(
|
|
794
|
+
Effect.provideService(NativeDatabase, root),
|
|
795
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
796
|
+
),
|
|
728
797
|
} satisfies AuthenticationAuthority["Service"];
|
|
729
798
|
});
|
|
730
799
|
|
|
@@ -991,8 +1060,33 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
991
1060
|
);
|
|
992
1061
|
const hooks = yield* LifecycleHooks;
|
|
993
1062
|
|
|
1063
|
+
const root = yield* NativeDatabase;
|
|
1064
|
+
|
|
1065
|
+
const tables = yield* sqlMapping(() =>
|
|
1066
|
+
nativeTables(root.$client.withoutTransforms(), root),
|
|
1067
|
+
).pipe(Effect.mapError(unavailable));
|
|
1068
|
+
|
|
1069
|
+
const readSnapshot = makeAuthoritySnapshot(tables, mapping);
|
|
994
1070
|
const c = sessionColumns(mapping);
|
|
995
1071
|
|
|
1072
|
+
const verification = yield* safeTransaction(
|
|
1073
|
+
Effect.gen(function* () {
|
|
1074
|
+
const { session, subject } = yield* sqlMapping(() => ({
|
|
1075
|
+
session: tables(mapping.session.table).as("auth_session"),
|
|
1076
|
+
subject: tables(mapping.subject.table).as("auth_subject"),
|
|
1077
|
+
}));
|
|
1078
|
+
|
|
1079
|
+
const [sessionOwner, subjectOwner] = yield* sqlMapping(() => [
|
|
1080
|
+
session.unencodedTextColumn?.(mapping.session.subjectId),
|
|
1081
|
+
subject.unencodedTextColumn?.(mapping.subject.id),
|
|
1082
|
+
]);
|
|
1083
|
+
|
|
1084
|
+
const canJoinOwner = yield* canJoinTextColumns(sessionOwner, subjectOwner);
|
|
1085
|
+
|
|
1086
|
+
return { session, subject, canJoinOwner };
|
|
1087
|
+
}),
|
|
1088
|
+
);
|
|
1089
|
+
|
|
996
1090
|
const persistence = {
|
|
997
1091
|
establish: <A>(
|
|
998
1092
|
input: Parameters<StatefulSessionPersistence<Claims>["establish"]>[0],
|
|
@@ -1006,23 +1100,29 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
1006
1100
|
const transaction = yield* CurrentSessionSql;
|
|
1007
1101
|
const journal = yield* CurrentCommitJournal;
|
|
1008
1102
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
input.evidence,
|
|
1012
|
-
|
|
1013
|
-
|
|
1103
|
+
// Ordered locks with row locks; one serialized snapshot without (see approve).
|
|
1104
|
+
const { nativeSubjectId, requirement } = yield* options.locking
|
|
1105
|
+
? validateEvidenceIn(mapping, input.evidence, true)
|
|
1106
|
+
: Effect.flatMap(
|
|
1107
|
+
readSnapshot(
|
|
1108
|
+
input.evidence.revision.subjectId,
|
|
1109
|
+
input.evidence.revision.credentials.map((item) => item.credentialId),
|
|
1110
|
+
),
|
|
1111
|
+
(captured) => validateCapturedEvidence(mapping, input.evidence, captured),
|
|
1112
|
+
);
|
|
1014
1113
|
|
|
1015
1114
|
let flowInsert: unknown | undefined;
|
|
1016
1115
|
let pendingExpiresAt: DateTime.Utc | undefined;
|
|
1017
1116
|
|
|
1018
|
-
if (input.pending === undefined)
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1117
|
+
if (input.pending === undefined) {
|
|
1118
|
+
if (input.fresh !== true || input.handoffSourceSessionId !== undefined)
|
|
1119
|
+
flowInsert = yield* ensureDirectFlowAvailable(
|
|
1120
|
+
mapping,
|
|
1121
|
+
input.evidence,
|
|
1122
|
+
nativeSubjectId,
|
|
1123
|
+
input.session.absoluteExpiresAt,
|
|
1124
|
+
);
|
|
1125
|
+
} else {
|
|
1026
1126
|
if (mapping.pending === undefined) return yield* invalidPending();
|
|
1027
1127
|
const pending = yield* validatePending(mapping, input.pending);
|
|
1028
1128
|
|
|
@@ -1080,7 +1180,7 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
1080
1180
|
|
|
1081
1181
|
if (flowInsert !== undefined)
|
|
1082
1182
|
yield* transaction.insert(mapping.flow.table).values(flowInsert);
|
|
1083
|
-
else
|
|
1183
|
+
else if (input.pending !== undefined)
|
|
1084
1184
|
yield* consumePending(
|
|
1085
1185
|
{ ...mapping, pending: mapping.pending! },
|
|
1086
1186
|
input.pending!,
|
|
@@ -1107,32 +1207,55 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
1107
1207
|
return receipt;
|
|
1108
1208
|
}).pipe(normalizeMutation(mapping)),
|
|
1109
1209
|
),
|
|
1110
|
-
verify: (input:
|
|
1210
|
+
verify: (input: Parameters<StatefulSessionPersistence<Claims>["verify"]>[0]) =>
|
|
1111
1211
|
safeTransaction(
|
|
1112
|
-
|
|
1113
|
-
database,
|
|
1212
|
+
withVerificationSavepoint(
|
|
1114
1213
|
Effect.gen(function* () {
|
|
1115
|
-
const
|
|
1214
|
+
const sql = root.$client.withoutTransforms();
|
|
1215
|
+
const { session, subject, canJoinOwner } = verification;
|
|
1116
1216
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1217
|
+
// A digest carries no owner. Custom codecs require decoding that
|
|
1218
|
+
// native ID first; never compare differently encoded ID columns.
|
|
1219
|
+
const owner = canJoinOwner
|
|
1220
|
+
? undefined
|
|
1221
|
+
: (yield* database
|
|
1222
|
+
.select({ subjectId: c.subjectId })
|
|
1223
|
+
.from(mapping.session.table)
|
|
1224
|
+
.where(eq(c.digest, input.digest))
|
|
1225
|
+
.limit(1))[0];
|
|
1226
|
+
|
|
1227
|
+
if (!canJoinOwner && owner === undefined) return yield* invalidSession();
|
|
1228
|
+
|
|
1229
|
+
// The final statement reads both rows even after owner discovery,
|
|
1230
|
+
// so deletion, rotation or owner changes cannot reuse the first read.
|
|
1231
|
+
const rows = yield* sqlMapping(
|
|
1232
|
+
() => sql<
|
|
1233
|
+
Record<string, unknown>
|
|
1234
|
+
>`select ${session.fields("v_")}, ${subject.fields("s_")}
|
|
1235
|
+
from ${session.name} inner join ${subject.name} on
|
|
1236
|
+
${subject.column(mapping.subject.id)} = ${
|
|
1237
|
+
owner === undefined
|
|
1238
|
+
? session.column(mapping.session.subjectId)
|
|
1239
|
+
: subject.value(mapping.subject.id, owner.subjectId)
|
|
1240
|
+
}
|
|
1241
|
+
where ${session.column(mapping.session.digest)} = ${session.value(mapping.session.digest, input.digest)}
|
|
1242
|
+
limit 1`,
|
|
1243
|
+
).pipe(Effect.flatten);
|
|
1122
1244
|
|
|
1123
1245
|
const row = rows[0];
|
|
1124
1246
|
|
|
1125
1247
|
if (row === undefined) return yield* invalidSession();
|
|
1126
|
-
const
|
|
1248
|
+
const sessionRow = yield* sqlMapping(() => session.decode(row, "v_"));
|
|
1249
|
+
const subjectRow = yield* sqlMapping(() => subject.decode(row, "s_"));
|
|
1250
|
+
const record = yield* mapping.session.decode(sessionRow);
|
|
1127
1251
|
const nativeSubjectId = yield* mapping.subjectId.toNative(record.subjectId);
|
|
1128
|
-
const subjects = yield* readSubject(mapping, nativeSubjectId, false);
|
|
1129
|
-
const subject = subjects[0];
|
|
1130
1252
|
const now = yield* freshNow;
|
|
1131
1253
|
|
|
1132
1254
|
if (
|
|
1133
|
-
|
|
1134
|
-
!mapping.
|
|
1135
|
-
subject[mapping.subject.
|
|
1255
|
+
!mapping.subjectId.equals(nativeSubjectId, sessionRow[mapping.session.subjectId]) ||
|
|
1256
|
+
!mapping.subjectId.equals(nativeSubjectId, subjectRow[mapping.subject.id]) ||
|
|
1257
|
+
!mapping.subject.isActiveStatus(subjectRow[mapping.subject.status]) ||
|
|
1258
|
+
subjectRow[mapping.subject.securityRevision] !== record.securityRevision ||
|
|
1136
1259
|
DateTime.toEpochMillis(now) >= DateTime.toEpochMillis(record.expiresAt) ||
|
|
1137
1260
|
DateTime.toEpochMillis(now) >= DateTime.toEpochMillis(record.absoluteExpiresAt)
|
|
1138
1261
|
)
|
|
@@ -1403,9 +1526,19 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
1403
1526
|
return {
|
|
1404
1527
|
statefulSessionPersistence: {
|
|
1405
1528
|
establish: (input, prepare) =>
|
|
1406
|
-
persistence
|
|
1529
|
+
persistence
|
|
1530
|
+
.establish(input, prepare)
|
|
1531
|
+
.pipe(
|
|
1532
|
+
Effect.provideService(NativeDatabase, root),
|
|
1533
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
1534
|
+
),
|
|
1407
1535
|
verify: (input) =>
|
|
1408
|
-
persistence
|
|
1536
|
+
persistence
|
|
1537
|
+
.verify(input)
|
|
1538
|
+
.pipe(
|
|
1539
|
+
Effect.provideService(NativeDatabase, root),
|
|
1540
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
1541
|
+
),
|
|
1409
1542
|
rotate: (input, prepare) =>
|
|
1410
1543
|
persistence.rotate(input, prepare).pipe(Effect.provideService(LifecycleHooks, hooks)),
|
|
1411
1544
|
revokeDigest: (digest, prepare) =>
|
|
@@ -1447,50 +1580,62 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
1447
1580
|
absoluteExpiresAt: column(mapping.tombstone.table, mapping.tombstone.absoluteExpiresAt),
|
|
1448
1581
|
};
|
|
1449
1582
|
|
|
1583
|
+
const root = yield* NativeDatabase;
|
|
1584
|
+
|
|
1585
|
+
const tables = yield* sqlMapping(() =>
|
|
1586
|
+
nativeTables(root.$client.withoutTransforms(), root),
|
|
1587
|
+
).pipe(Effect.mapError(unavailable));
|
|
1588
|
+
|
|
1450
1589
|
const service = {
|
|
1451
1590
|
verify: (session: SessionMetadata, _now: DateTime.Utc) =>
|
|
1452
1591
|
safeTransaction(
|
|
1453
|
-
|
|
1454
|
-
database,
|
|
1592
|
+
withVerificationSavepoint(
|
|
1455
1593
|
Effect.gen(function* () {
|
|
1456
|
-
const
|
|
1594
|
+
const sql = root.$client.withoutTransforms();
|
|
1457
1595
|
const nativeSubjectId = yield* mapping.subjectId.toNative(session.subjectId);
|
|
1458
1596
|
const nativeSessionId = yield* mapping.sessionId.toNative(session.sessionId);
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1597
|
+
const readNow = yield* freshNow;
|
|
1598
|
+
|
|
1599
|
+
// Independent bound IDs preserve each column's physical type and
|
|
1600
|
+
// codec. Both authority and revocation come from this statement.
|
|
1601
|
+
const { subjectTable, rows } = yield* sqlMapping(() => {
|
|
1602
|
+
const subjectTable = tables(mapping.subject.table).as("auth_subject");
|
|
1603
|
+
const revoked = tables(mapping.tombstone.table).as("auth_revocation");
|
|
1604
|
+
|
|
1605
|
+
return {
|
|
1606
|
+
subjectTable,
|
|
1607
|
+
rows: sql<Record<string, unknown>>`
|
|
1608
|
+
select ${subjectTable.fields("s_")},
|
|
1609
|
+
case when exists (
|
|
1610
|
+
select 1 from ${revoked.name}
|
|
1611
|
+
where ${revoked.column(mapping.tombstone.subjectId)} = ${revoked.value(mapping.tombstone.subjectId, nativeSubjectId)}
|
|
1612
|
+
and ${revoked.column(mapping.tombstone.sessionId)} = ${revoked.value(mapping.tombstone.sessionId, nativeSessionId)}
|
|
1613
|
+
and ${revoked.column(mapping.tombstone.absoluteExpiresAt)} > ${revoked.value(mapping.tombstone.absoluteExpiresAt, mapping.tombstone.encodeInstant(readNow))}
|
|
1614
|
+
) then 1 else 0 end as auth_revoked
|
|
1615
|
+
from ${subjectTable.name}
|
|
1616
|
+
where ${subjectTable.column(mapping.subject.id)} = ${subjectTable.value(mapping.subject.id, nativeSubjectId)}
|
|
1617
|
+
limit 1`,
|
|
1618
|
+
};
|
|
1619
|
+
});
|
|
1461
1620
|
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
)
|
|
1467
|
-
return yield* invalidSession();
|
|
1621
|
+
const result = (yield* rows)[0];
|
|
1622
|
+
|
|
1623
|
+
if (result === undefined) return yield* invalidSession();
|
|
1624
|
+
const row = yield* sqlMapping(() => subjectTable.decode(result, "s_"));
|
|
1625
|
+
const revoked = yield* decodeRevocationFlag(result.auth_revoked);
|
|
1468
1626
|
const authoritativeNow = yield* freshNow;
|
|
1469
1627
|
|
|
1470
1628
|
if (
|
|
1629
|
+
!mapping.subjectId.equals(nativeSubjectId, row[mapping.subject.id]) ||
|
|
1630
|
+
!mapping.subject.isActiveStatus(row[mapping.subject.status]) ||
|
|
1631
|
+
row[mapping.subject.securityRevision] !== session.securityRevision ||
|
|
1632
|
+
revoked === 1 ||
|
|
1471
1633
|
DateTime.toEpochMillis(authoritativeNow) >=
|
|
1472
1634
|
DateTime.toEpochMillis(session.expiresAt) ||
|
|
1473
1635
|
DateTime.toEpochMillis(authoritativeNow) >=
|
|
1474
1636
|
DateTime.toEpochMillis(session.absoluteExpiresAt)
|
|
1475
1637
|
)
|
|
1476
1638
|
return yield* invalidSession();
|
|
1477
|
-
|
|
1478
|
-
const revoked = yield* read
|
|
1479
|
-
.select({ sessionId: tombstone.sessionId })
|
|
1480
|
-
.from(mapping.tombstone.table)
|
|
1481
|
-
.where(
|
|
1482
|
-
and(
|
|
1483
|
-
eq(tombstone.subjectId, nativeSubjectId),
|
|
1484
|
-
eq(tombstone.sessionId, nativeSessionId),
|
|
1485
|
-
gt(
|
|
1486
|
-
tombstone.absoluteExpiresAt,
|
|
1487
|
-
mapping.tombstone.encodeInstant(authoritativeNow),
|
|
1488
|
-
),
|
|
1489
|
-
),
|
|
1490
|
-
)
|
|
1491
|
-
.limit(1);
|
|
1492
|
-
|
|
1493
|
-
if (revoked.length > 0) return yield* invalidSession();
|
|
1494
1639
|
}),
|
|
1495
1640
|
),
|
|
1496
1641
|
),
|
|
@@ -1616,7 +1761,12 @@ export const makeSessionKernel = (operations: QueryOperations) => {
|
|
|
1616
1761
|
|
|
1617
1762
|
return {
|
|
1618
1763
|
verify: (session, now) =>
|
|
1619
|
-
service
|
|
1764
|
+
service
|
|
1765
|
+
.verify(session, now)
|
|
1766
|
+
.pipe(
|
|
1767
|
+
Effect.provideService(NativeDatabase, root),
|
|
1768
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
1769
|
+
),
|
|
1620
1770
|
revoke: (input, prepare) =>
|
|
1621
1771
|
service.revoke(input, prepare).pipe(Effect.provideService(LifecycleHooks, hooks)),
|
|
1622
1772
|
revokeAll: (input, prepare) =>
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as Shared from "./models/oauth-connected-model";
|
|
2
|
+
import type { SqlTableModel } from "./sql-oauth-model";
|
|
3
|
+
import type { Fragment as SQL, Table } from "./sql-table";
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
type OAuthConnectedAction,
|
|
7
|
+
type OAuthConnectedOrderCodec,
|
|
8
|
+
type OAuthConnectedPolicyInput,
|
|
9
|
+
requiredOAuthConnectedConstraints,
|
|
10
|
+
requiredOAuthConnectedRevocationConstraints,
|
|
11
|
+
} from "./models/oauth-connected-model";
|
|
12
|
+
|
|
13
|
+
export type OAuthConnectedSubjectTable<S extends Table> = Shared.OAuthConnectedSubjectTable<
|
|
14
|
+
SqlTableModel<S>,
|
|
15
|
+
SQL
|
|
16
|
+
>;
|
|
17
|
+
|
|
18
|
+
export type OAuthConnectedFlowTable<F extends Table, N> = Shared.OAuthConnectedFlowTable<
|
|
19
|
+
SqlTableModel<F>,
|
|
20
|
+
N
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
export type OAuthConnectedGrantTable<G extends Table, N> = Shared.OAuthConnectedGrantTable<
|
|
24
|
+
SqlTableModel<G>,
|
|
25
|
+
N
|
|
26
|
+
>;
|
|
27
|
+
|
|
28
|
+
/** Also stores a permanent provider/issuer scope anchor. clientKey must fit 52
|
|
29
|
+
* ASCII characters. Empty clientRegistrationId is reserved for that anchor;
|
|
30
|
+
* actual profile registrations are nonempty. Scope counter stays zero. */
|
|
31
|
+
export type OAuthConnectedClientRegistrationTable<C extends Table> =
|
|
32
|
+
Shared.OAuthConnectedClientRegistrationTable<SqlTableModel<C>>;
|
|
33
|
+
|
|
34
|
+
/** Remote authority: no local-subject column or subject-scoped key. Retain this
|
|
35
|
+
* anchor/cutoff when a legitimately released tuple gains a different local owner. */
|
|
36
|
+
export type OAuthConnectedCohortTable<C extends Table> = Shared.OAuthConnectedCohortTable<
|
|
37
|
+
SqlTableModel<C>
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
export type OAuthConnectedAdmissionTable<A extends Table, N> = Shared.OAuthConnectedAdmissionTable<
|
|
41
|
+
SqlTableModel<A>,
|
|
42
|
+
N
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
export type OAuthConnectedCommandTable<C extends Table, N> = Shared.OAuthConnectedCommandTable<
|
|
46
|
+
SqlTableModel<C>,
|
|
47
|
+
N
|
|
48
|
+
>;
|
|
49
|
+
|
|
50
|
+
export type OAuthConnectedRevocationJobTable<
|
|
51
|
+
J extends Table,
|
|
52
|
+
N,
|
|
53
|
+
> = Shared.OAuthConnectedRevocationJobTable<SqlTableModel<J>, N>;
|
|
54
|
+
|
|
55
|
+
export type OAuthConnectedPolicyGuardTable<
|
|
56
|
+
T extends Table,
|
|
57
|
+
N,
|
|
58
|
+
> = Shared.OAuthConnectedPolicyGuardTable<SqlTableModel<T>, N, SQL>;
|
|
59
|
+
|
|
60
|
+
export type OAuthConnectedPolicyGuard<N> = Shared.OAuthConnectedPolicyGuard<N, SQL, Table>;
|
|
61
|
+
|
|
62
|
+
export const oauthConnectedPolicyGuard = <T extends Table, N>(
|
|
63
|
+
input: OAuthConnectedPolicyGuardTable<T, N>,
|
|
64
|
+
): OAuthConnectedPolicyGuard<N> =>
|
|
65
|
+
Shared.oauthConnectedPolicyGuard<SqlTableModel<T>, N, SQL>(input);
|
|
66
|
+
|
|
67
|
+
export type OAuthConnectedSqlPolicy<N> = Shared.OAuthConnectedSqlPolicy<N, SQL, Table>;
|
|
68
|
+
|
|
69
|
+
/** Existing ownership only; workers never allocate or acquire a tuple. */
|
|
70
|
+
export type OAuthConnectedOwnershipRead<
|
|
71
|
+
T extends Table,
|
|
72
|
+
O extends Table,
|
|
73
|
+
N,
|
|
74
|
+
> = Shared.OAuthConnectedOwnershipRead<SqlTableModel<T>, SqlTableModel<O>, N, SQL>;
|
|
75
|
+
|
|
76
|
+
export type OAuthConnectedAuthorityMapping<
|
|
77
|
+
T extends Table,
|
|
78
|
+
O extends Table,
|
|
79
|
+
F extends Table,
|
|
80
|
+
G extends Table,
|
|
81
|
+
C extends Table,
|
|
82
|
+
H extends Table,
|
|
83
|
+
N,
|
|
84
|
+
> = Shared.OAuthConnectedAuthorityMapping<
|
|
85
|
+
SqlTableModel<T>,
|
|
86
|
+
SqlTableModel<O>,
|
|
87
|
+
SqlTableModel<F>,
|
|
88
|
+
SqlTableModel<G>,
|
|
89
|
+
SqlTableModel<C>,
|
|
90
|
+
SqlTableModel<H>,
|
|
91
|
+
N,
|
|
92
|
+
SQL
|
|
93
|
+
>;
|
|
94
|
+
|
|
95
|
+
export type OAuthConnectedMapping<
|
|
96
|
+
S extends Table,
|
|
97
|
+
AC extends Table,
|
|
98
|
+
T extends Table,
|
|
99
|
+
O extends Table,
|
|
100
|
+
F extends Table,
|
|
101
|
+
G extends Table,
|
|
102
|
+
C extends Table,
|
|
103
|
+
H extends Table,
|
|
104
|
+
A extends Table,
|
|
105
|
+
D extends Table,
|
|
106
|
+
N,
|
|
107
|
+
J extends Table = never,
|
|
108
|
+
> = Shared.OAuthConnectedMapping<
|
|
109
|
+
SqlTableModel<S>,
|
|
110
|
+
SqlTableModel<AC>,
|
|
111
|
+
SqlTableModel<T>,
|
|
112
|
+
SqlTableModel<O>,
|
|
113
|
+
SqlTableModel<F>,
|
|
114
|
+
SqlTableModel<G>,
|
|
115
|
+
SqlTableModel<C>,
|
|
116
|
+
SqlTableModel<H>,
|
|
117
|
+
SqlTableModel<A>,
|
|
118
|
+
SqlTableModel<D>,
|
|
119
|
+
N,
|
|
120
|
+
SqlTableModel<J>,
|
|
121
|
+
SQL,
|
|
122
|
+
Table,
|
|
123
|
+
SqlTableModel<Table>
|
|
124
|
+
>;
|
|
125
|
+
|
|
126
|
+
export type OAuthConnectedRevocationMapping<
|
|
127
|
+
T extends Table,
|
|
128
|
+
O extends Table,
|
|
129
|
+
F extends Table,
|
|
130
|
+
G extends Table,
|
|
131
|
+
C extends Table,
|
|
132
|
+
H extends Table,
|
|
133
|
+
J extends Table,
|
|
134
|
+
N,
|
|
135
|
+
> = Shared.OAuthConnectedRevocationMapping<
|
|
136
|
+
SqlTableModel<T>,
|
|
137
|
+
SqlTableModel<O>,
|
|
138
|
+
SqlTableModel<F>,
|
|
139
|
+
SqlTableModel<G>,
|
|
140
|
+
SqlTableModel<C>,
|
|
141
|
+
SqlTableModel<H>,
|
|
142
|
+
SqlTableModel<J>,
|
|
143
|
+
N,
|
|
144
|
+
SQL
|
|
145
|
+
>;
|