@yielded/auth-persistence 0.1.0-beta.23 → 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 +12 -6
- package/dist/Adapter.mjs +8 -3
- 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/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 -47
- package/dist/internal/models/password-model.mjs +0 -1
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/native-sql-table.d.mts +4 -0
- package/dist/internal/native-sql-table.mjs +7 -0
- package/dist/internal/native-sql-table.mjs.map +1 -1
- 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-kernel.d.mts +5 -5
- package/dist/internal/{password-attempts.d.mts → password-credentials.d.mts} +3 -4
- package/dist/internal/password-credentials.mjs +121 -0
- package/dist/internal/password-credentials.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +8 -24
- package/dist/internal/password-kernel.mjs +4 -42
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +1 -1
- package/dist/internal/persistence.mjs +2 -2
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/query-operations.d.mts +2 -0
- package/dist/internal/session-kernel.d.mts +2 -2
- package/dist/internal/session-kernel.mjs +74 -18
- 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-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 +1 -37
- 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 -56
- package/dist/internal/storage-tables.mjs +0 -17
- 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-kernel.mjs +3 -2
- package/dist/internal/transaction-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +89 -5
- package/src/OAuthPersistence.ts +427 -0
- package/src/index.ts +3 -0
- package/src/internal/configuration.ts +1 -3
- 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 -56
- package/src/internal/native-sql-table.ts +14 -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 +5 -98
- package/src/internal/persistence.ts +1 -1
- package/src/internal/query-operations.ts +2 -0
- package/src/internal/session-kernel.ts +143 -55
- package/src/internal/sql-oauth-connected-model.ts +145 -0
- package/src/internal/sql-oauth-model.ts +263 -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 -37
- package/src/internal/storage-plans.ts +218 -0
- package/src/internal/storage-tables.ts +0 -20
- package/src/internal/storage-validation.ts +58 -0
- package/src/internal/transaction-kernel.ts +6 -2
- package/dist/internal/password-attempts.mjs +0 -197
- package/dist/internal/password-attempts.mjs.map +0 -1
- package/src/internal/password-attempts.ts +0 -466
|
@@ -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,
|
|
@@ -64,8 +65,22 @@ import {
|
|
|
64
65
|
validateStepUpPlan,
|
|
65
66
|
stepUpRotationMatches,
|
|
66
67
|
} from "./step-up-state";
|
|
68
|
+
import { canJoinTextColumns } from "./storage-validation";
|
|
67
69
|
import { NativeDatabase } from "./transaction-kernel";
|
|
68
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
|
+
});
|
|
83
|
+
|
|
69
84
|
type CommitMode = "interactive" | "synchronous";
|
|
70
85
|
|
|
71
86
|
type AnySubjectMapping = SessionSubjectTables<any, any>;
|
|
@@ -664,7 +679,16 @@ export const makeSessionKernel = (
|
|
|
664
679
|
const service = {
|
|
665
680
|
capture: (subjectId: SubjectId, credentialIds: ReadonlyArray<string>) =>
|
|
666
681
|
safeTransaction(
|
|
667
|
-
readSnapshot(subjectId, credentialIds).pipe(
|
|
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
|
+
),
|
|
668
692
|
),
|
|
669
693
|
requirements: (evidence: AuthenticationEvidence) =>
|
|
670
694
|
safeTransaction(
|
|
@@ -1045,6 +1069,24 @@ export const makeSessionKernel = (
|
|
|
1045
1069
|
const readSnapshot = makeAuthoritySnapshot(tables, mapping);
|
|
1046
1070
|
const c = sessionColumns(mapping);
|
|
1047
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
|
+
|
|
1048
1090
|
const persistence = {
|
|
1049
1091
|
establish: <A>(
|
|
1050
1092
|
input: Parameters<StatefulSessionPersistence<Claims>["establish"]>[0],
|
|
@@ -1072,14 +1114,15 @@ export const makeSessionKernel = (
|
|
|
1072
1114
|
let flowInsert: unknown | undefined;
|
|
1073
1115
|
let pendingExpiresAt: DateTime.Utc | undefined;
|
|
1074
1116
|
|
|
1075
|
-
if (input.pending === undefined)
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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 {
|
|
1083
1126
|
if (mapping.pending === undefined) return yield* invalidPending();
|
|
1084
1127
|
const pending = yield* validatePending(mapping, input.pending);
|
|
1085
1128
|
|
|
@@ -1137,7 +1180,7 @@ export const makeSessionKernel = (
|
|
|
1137
1180
|
|
|
1138
1181
|
if (flowInsert !== undefined)
|
|
1139
1182
|
yield* transaction.insert(mapping.flow.table).values(flowInsert);
|
|
1140
|
-
else
|
|
1183
|
+
else if (input.pending !== undefined)
|
|
1141
1184
|
yield* consumePending(
|
|
1142
1185
|
{ ...mapping, pending: mapping.pending! },
|
|
1143
1186
|
input.pending!,
|
|
@@ -1164,32 +1207,55 @@ export const makeSessionKernel = (
|
|
|
1164
1207
|
return receipt;
|
|
1165
1208
|
}).pipe(normalizeMutation(mapping)),
|
|
1166
1209
|
),
|
|
1167
|
-
verify: (input:
|
|
1210
|
+
verify: (input: Parameters<StatefulSessionPersistence<Claims>["verify"]>[0]) =>
|
|
1168
1211
|
safeTransaction(
|
|
1169
|
-
|
|
1170
|
-
database,
|
|
1212
|
+
withVerificationSavepoint(
|
|
1171
1213
|
Effect.gen(function* () {
|
|
1172
|
-
const
|
|
1214
|
+
const sql = root.$client.withoutTransforms();
|
|
1215
|
+
const { session, subject, canJoinOwner } = verification;
|
|
1173
1216
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
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);
|
|
1179
1244
|
|
|
1180
1245
|
const row = rows[0];
|
|
1181
1246
|
|
|
1182
1247
|
if (row === undefined) return yield* invalidSession();
|
|
1183
|
-
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);
|
|
1184
1251
|
const nativeSubjectId = yield* mapping.subjectId.toNative(record.subjectId);
|
|
1185
|
-
const subjects = yield* readSubject(mapping, nativeSubjectId, false);
|
|
1186
|
-
const subject = subjects[0];
|
|
1187
1252
|
const now = yield* freshNow;
|
|
1188
1253
|
|
|
1189
1254
|
if (
|
|
1190
|
-
|
|
1191
|
-
!mapping.
|
|
1192
|
-
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 ||
|
|
1193
1259
|
DateTime.toEpochMillis(now) >= DateTime.toEpochMillis(record.expiresAt) ||
|
|
1194
1260
|
DateTime.toEpochMillis(now) >= DateTime.toEpochMillis(record.absoluteExpiresAt)
|
|
1195
1261
|
)
|
|
@@ -1467,7 +1533,12 @@ export const makeSessionKernel = (
|
|
|
1467
1533
|
Effect.provideService(LifecycleHooks, hooks),
|
|
1468
1534
|
),
|
|
1469
1535
|
verify: (input) =>
|
|
1470
|
-
persistence
|
|
1536
|
+
persistence
|
|
1537
|
+
.verify(input)
|
|
1538
|
+
.pipe(
|
|
1539
|
+
Effect.provideService(NativeDatabase, root),
|
|
1540
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
1541
|
+
),
|
|
1471
1542
|
rotate: (input, prepare) =>
|
|
1472
1543
|
persistence.rotate(input, prepare).pipe(Effect.provideService(LifecycleHooks, hooks)),
|
|
1473
1544
|
revokeDigest: (digest, prepare) =>
|
|
@@ -1509,50 +1580,62 @@ export const makeSessionKernel = (
|
|
|
1509
1580
|
absoluteExpiresAt: column(mapping.tombstone.table, mapping.tombstone.absoluteExpiresAt),
|
|
1510
1581
|
};
|
|
1511
1582
|
|
|
1583
|
+
const root = yield* NativeDatabase;
|
|
1584
|
+
|
|
1585
|
+
const tables = yield* sqlMapping(() =>
|
|
1586
|
+
nativeTables(root.$client.withoutTransforms(), root),
|
|
1587
|
+
).pipe(Effect.mapError(unavailable));
|
|
1588
|
+
|
|
1512
1589
|
const service = {
|
|
1513
1590
|
verify: (session: SessionMetadata, _now: DateTime.Utc) =>
|
|
1514
1591
|
safeTransaction(
|
|
1515
|
-
|
|
1516
|
-
database,
|
|
1592
|
+
withVerificationSavepoint(
|
|
1517
1593
|
Effect.gen(function* () {
|
|
1518
|
-
const
|
|
1594
|
+
const sql = root.$client.withoutTransforms();
|
|
1519
1595
|
const nativeSubjectId = yield* mapping.subjectId.toNative(session.subjectId);
|
|
1520
1596
|
const nativeSessionId = yield* mapping.sessionId.toNative(session.sessionId);
|
|
1521
|
-
const
|
|
1522
|
-
|
|
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
|
+
});
|
|
1523
1620
|
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
)
|
|
1529
|
-
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);
|
|
1530
1626
|
const authoritativeNow = yield* freshNow;
|
|
1531
1627
|
|
|
1532
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 ||
|
|
1533
1633
|
DateTime.toEpochMillis(authoritativeNow) >=
|
|
1534
1634
|
DateTime.toEpochMillis(session.expiresAt) ||
|
|
1535
1635
|
DateTime.toEpochMillis(authoritativeNow) >=
|
|
1536
1636
|
DateTime.toEpochMillis(session.absoluteExpiresAt)
|
|
1537
1637
|
)
|
|
1538
1638
|
return yield* invalidSession();
|
|
1539
|
-
|
|
1540
|
-
const revoked = yield* read
|
|
1541
|
-
.select({ sessionId: tombstone.sessionId })
|
|
1542
|
-
.from(mapping.tombstone.table)
|
|
1543
|
-
.where(
|
|
1544
|
-
and(
|
|
1545
|
-
eq(tombstone.subjectId, nativeSubjectId),
|
|
1546
|
-
eq(tombstone.sessionId, nativeSessionId),
|
|
1547
|
-
gt(
|
|
1548
|
-
tombstone.absoluteExpiresAt,
|
|
1549
|
-
mapping.tombstone.encodeInstant(authoritativeNow),
|
|
1550
|
-
),
|
|
1551
|
-
),
|
|
1552
|
-
)
|
|
1553
|
-
.limit(1);
|
|
1554
|
-
|
|
1555
|
-
if (revoked.length > 0) return yield* invalidSession();
|
|
1556
1639
|
}),
|
|
1557
1640
|
),
|
|
1558
1641
|
),
|
|
@@ -1678,7 +1761,12 @@ export const makeSessionKernel = (
|
|
|
1678
1761
|
|
|
1679
1762
|
return {
|
|
1680
1763
|
verify: (session, now) =>
|
|
1681
|
-
service
|
|
1764
|
+
service
|
|
1765
|
+
.verify(session, now)
|
|
1766
|
+
.pipe(
|
|
1767
|
+
Effect.provideService(NativeDatabase, root),
|
|
1768
|
+
Effect.provideService(LifecycleHooks, hooks),
|
|
1769
|
+
),
|
|
1682
1770
|
revoke: (input, prepare) =>
|
|
1683
1771
|
service.revoke(input, prepare).pipe(Effect.provideService(LifecycleHooks, hooks)),
|
|
1684
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
|
+
>;
|