@opengeni/db 3.4.0-canary.2 → 3.6.0
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/dist/canonical-human-identities.d.ts +20 -0
- package/dist/canonical-human-identities.js +7 -3
- package/dist/{chunk-ZWRIPCTR.js → chunk-2NT476HD.js} +2 -2
- package/dist/{chunk-PYMY3AJW.js → chunk-744DFC65.js} +202 -7
- package/dist/chunk-744DFC65.js.map +1 -0
- package/dist/{chunk-O6FTELKH.js → chunk-CDMMSUWJ.js} +3 -3
- package/dist/chunk-G3732M3T.js +299 -0
- package/dist/chunk-G3732M3T.js.map +1 -0
- package/dist/{chunk-TRBOWUG3.js → chunk-IW6O6G7R.js} +2 -2
- package/dist/{chunk-HY3F5Q4E.js → chunk-Q5YUGQVK.js} +3 -3
- package/dist/{chunk-V3TRUXFJ.js → chunk-UGDVSQ6U.js} +72 -19
- package/dist/chunk-UGDVSQ6U.js.map +1 -0
- package/dist/{chunk-C2AFP7SB.js → chunk-UJ2EW3QI.js} +6664 -6051
- package/dist/chunk-UJ2EW3QI.js.map +1 -0
- package/dist/{chunk-H7MXYG7R.js → chunk-WW7AJ5IC.js} +2 -2
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +44 -6
- package/dist/index.js +2914 -1535
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-session-set-schema.d.ts +1399 -0
- package/dist/managed-auth-session-sets.d.ts +145 -0
- package/dist/managed-auth-session-sets.js +50 -0
- package/dist/managed-auth-session-sets.js.map +1 -0
- package/dist/organization-recovery-schema.d.ts +2144 -0
- package/dist/organization-recovery.d.ts +186 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +7 -4
- package/dist/schema.d.ts +9 -6
- package/dist/schema.js +43 -1
- package/dist/session-background-commands.js +3 -3
- package/dist/session-control.d.ts +5 -3
- package/dist/session-queue-commands.d.ts +2 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/video-generation.js +3 -3
- package/dist/work-claims-schema.d.ts +975 -0
- package/dist/work-claims.d.ts +83 -0
- package/drizzle/0362_managed_auth_session_sets.sql +2347 -0
- package/drizzle/0363_organization_recovery_custody.sql +2418 -0
- package/drizzle/0364_workspace_learning_policy_snapshot_lock_order.sql +221 -0
- package/drizzle/0365_permission_scoped_work_claims.sql +1306 -0
- package/drizzle/0366_session_discovery_title_search_index.sql +5 -0
- package/drizzle/0367_session_discovery_goal_search_index.sql +5 -0
- package/drizzle/0368_session_discovery_claim_search_index.sql +6 -0
- package/package.json +10 -6
- package/src/canonical-human-identities.ts +105 -27
- package/src/index.ts +1471 -433
- package/src/managed-auth-session-set-schema.ts +176 -0
- package/src/managed-auth-session-sets.ts +430 -0
- package/src/organization-recovery-schema.ts +277 -0
- package/src/organization-recovery.ts +424 -0
- package/src/provision-roles.ts +67 -0
- package/src/runtime-posture.ts +144 -6
- package/src/schema.ts +3 -0
- package/src/session-control.ts +255 -16
- package/src/session-queue-commands.ts +10 -1
- package/src/session-tool-call-settlement.ts +6 -1
- package/src/work-claims-schema.ts +148 -0
- package/src/work-claims.ts +200 -0
- package/dist/chunk-C2AFP7SB.js.map +0 -1
- package/dist/chunk-PYMY3AJW.js.map +0 -1
- package/dist/chunk-V3TRUXFJ.js.map +0 -1
- /package/dist/{chunk-ZWRIPCTR.js.map → chunk-2NT476HD.js.map} +0 -0
- /package/dist/{chunk-O6FTELKH.js.map → chunk-CDMMSUWJ.js.map} +0 -0
- /package/dist/{chunk-TRBOWUG3.js.map → chunk-IW6O6G7R.js.map} +0 -0
- /package/dist/{chunk-HY3F5Q4E.js.map → chunk-Q5YUGQVK.js.map} +0 -0
- /package/dist/{chunk-H7MXYG7R.js.map → chunk-WW7AJ5IC.js.map} +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bigint,
|
|
3
|
+
index,
|
|
4
|
+
integer,
|
|
5
|
+
jsonb,
|
|
6
|
+
pgTable,
|
|
7
|
+
primaryKey,
|
|
8
|
+
text,
|
|
9
|
+
timestamp,
|
|
10
|
+
unique,
|
|
11
|
+
uniqueIndex,
|
|
12
|
+
uuid,
|
|
13
|
+
} from "drizzle-orm/pg-core";
|
|
14
|
+
import { sql } from "drizzle-orm";
|
|
15
|
+
|
|
16
|
+
export const managedAuthBrowserInstallations = pgTable("managed_auth_browser_installations", {
|
|
17
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
18
|
+
authorityHash: text("authority_hash").notNull().unique(),
|
|
19
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
20
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
21
|
+
idleExpiresAt: timestamp("idle_expires_at", { withTimezone: true }).notNull(),
|
|
22
|
+
absoluteExpiresAt: timestamp("absolute_expires_at", { withTimezone: true }).notNull(),
|
|
23
|
+
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const managedAuthSessionSets = pgTable("managed_auth_session_sets", {
|
|
27
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
28
|
+
installationId: uuid("installation_id").notNull().unique(),
|
|
29
|
+
authorityHash: text("authority_hash").notNull().unique(),
|
|
30
|
+
csrfHash: text("csrf_hash").notNull(),
|
|
31
|
+
generation: bigint("generation", { mode: "number" }).notNull().default(1),
|
|
32
|
+
actorEpoch: bigint("actor_epoch", { mode: "number" }).notNull().default(1),
|
|
33
|
+
selectedSlotId: uuid("selected_slot_id"),
|
|
34
|
+
state: text("state").notNull().default("ready"),
|
|
35
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
36
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
37
|
+
idleExpiresAt: timestamp("idle_expires_at", { withTimezone: true }).notNull(),
|
|
38
|
+
absoluteExpiresAt: timestamp("absolute_expires_at", { withTimezone: true }).notNull(),
|
|
39
|
+
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const managedAuthLoginSlots = pgTable(
|
|
43
|
+
"managed_auth_login_slots",
|
|
44
|
+
{
|
|
45
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
46
|
+
sessionSetId: uuid("session_set_id").notNull(),
|
|
47
|
+
authSessionId: text("auth_session_id"),
|
|
48
|
+
authUserId: text("auth_user_id").notNull(),
|
|
49
|
+
identityId: uuid("identity_id").notNull(),
|
|
50
|
+
loginBindingId: uuid("login_binding_id").notNull(),
|
|
51
|
+
identityRevision: bigint("identity_revision", { mode: "number" }).notNull(),
|
|
52
|
+
authRevision: bigint("auth_revision", { mode: "number" }).notNull(),
|
|
53
|
+
loginBindingRevision: bigint("login_binding_revision", { mode: "number" }).notNull(),
|
|
54
|
+
displayName: text("display_name").notNull(),
|
|
55
|
+
verifiedEmail: text("verified_email").notNull(),
|
|
56
|
+
status: text("status").notNull().default("active"),
|
|
57
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
58
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
59
|
+
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
60
|
+
},
|
|
61
|
+
(table) => ({
|
|
62
|
+
idSet: unique("managed_auth_login_slots_id_set_unique").on(table.id, table.sessionSetId),
|
|
63
|
+
liveBinding: uniqueIndex("managed_auth_login_slots_live_binding_idx")
|
|
64
|
+
.on(table.sessionSetId, table.loginBindingId)
|
|
65
|
+
.where(sql`${table.status} <> 'revoked'`),
|
|
66
|
+
liveSession: uniqueIndex("managed_auth_login_slots_live_session_idx")
|
|
67
|
+
.on(table.authSessionId)
|
|
68
|
+
.where(sql`${table.authSessionId} is not null and ${table.status} <> 'revoked'`),
|
|
69
|
+
setStatus: index("managed_auth_login_slots_set_status_idx").on(
|
|
70
|
+
table.sessionSetId,
|
|
71
|
+
table.status,
|
|
72
|
+
table.createdAt,
|
|
73
|
+
table.id,
|
|
74
|
+
),
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export const managedAuthLoginReturnIntents = pgTable("managed_auth_login_return_intents", {
|
|
79
|
+
id: uuid("id").primaryKey(),
|
|
80
|
+
sessionSetId: uuid("session_set_id").notNull(),
|
|
81
|
+
path: text("path").notNull(),
|
|
82
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
83
|
+
consumedAt: timestamp("consumed_at", { withTimezone: true }),
|
|
84
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export const managedAuthLoginTransactions = pgTable(
|
|
88
|
+
"managed_auth_login_transactions",
|
|
89
|
+
{
|
|
90
|
+
id: uuid("id").primaryKey(),
|
|
91
|
+
sessionSetId: uuid("session_set_id").notNull(),
|
|
92
|
+
secretHash: text("secret_hash").notNull(),
|
|
93
|
+
kind: text("kind").notNull(),
|
|
94
|
+
targetSlotId: uuid("target_slot_id"),
|
|
95
|
+
expectedIdentityId: uuid("expected_identity_id"),
|
|
96
|
+
expectedLoginBindingId: uuid("expected_login_binding_id"),
|
|
97
|
+
expectedIdentityRevision: bigint("expected_identity_revision", { mode: "number" }),
|
|
98
|
+
expectedAuthRevision: bigint("expected_auth_revision", { mode: "number" }),
|
|
99
|
+
expectedLoginBindingRevision: bigint("expected_login_binding_revision", { mode: "number" }),
|
|
100
|
+
expectedGeneration: bigint("expected_generation", { mode: "number" }).notNull(),
|
|
101
|
+
returnIntentId: uuid("return_intent_id"),
|
|
102
|
+
status: text("status").notNull().default("pending"),
|
|
103
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
104
|
+
consumedAt: timestamp("consumed_at", { withTimezone: true }),
|
|
105
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
106
|
+
},
|
|
107
|
+
(table) => ({
|
|
108
|
+
setStatus: index("managed_auth_login_transactions_set_status_idx").on(
|
|
109
|
+
table.sessionSetId,
|
|
110
|
+
table.status,
|
|
111
|
+
table.expiresAt,
|
|
112
|
+
),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
export const managedAuthSessionSetOperations = pgTable(
|
|
117
|
+
"managed_auth_session_set_operations",
|
|
118
|
+
{
|
|
119
|
+
operationId: uuid("operation_id").primaryKey(),
|
|
120
|
+
sessionSetId: uuid("session_set_id").notNull(),
|
|
121
|
+
operationType: text("operation_type").notNull(),
|
|
122
|
+
requestDigest: text("request_digest").notNull(),
|
|
123
|
+
expectedGeneration: bigint("expected_generation", { mode: "number" }).notNull(),
|
|
124
|
+
resultGeneration: bigint("result_generation", { mode: "number" }).notNull(),
|
|
125
|
+
resultActorEpoch: bigint("result_actor_epoch", { mode: "number" }).notNull(),
|
|
126
|
+
targetSlotId: uuid("target_slot_id"),
|
|
127
|
+
replacementSlotId: uuid("replacement_slot_id"),
|
|
128
|
+
outcome: text("outcome").notNull(),
|
|
129
|
+
result: jsonb("result").notNull(),
|
|
130
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
131
|
+
},
|
|
132
|
+
(table) => ({
|
|
133
|
+
setCreated: index("managed_auth_session_set_operations_set_created_idx").on(
|
|
134
|
+
table.sessionSetId,
|
|
135
|
+
table.createdAt,
|
|
136
|
+
table.operationId,
|
|
137
|
+
),
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
export const managedAuthActorMutationLeases = pgTable(
|
|
142
|
+
"managed_auth_actor_mutation_leases",
|
|
143
|
+
{
|
|
144
|
+
sessionSetId: uuid("session_set_id").notNull(),
|
|
145
|
+
requestId: uuid("request_id").notNull(),
|
|
146
|
+
actorEpoch: bigint("actor_epoch", { mode: "number" }).notNull(),
|
|
147
|
+
acquiredAt: timestamp("acquired_at", { withTimezone: true }).notNull().defaultNow(),
|
|
148
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
149
|
+
},
|
|
150
|
+
(table) => ({
|
|
151
|
+
primaryKey: primaryKey({ columns: [table.sessionSetId, table.requestId] }),
|
|
152
|
+
expiry: index("managed_auth_actor_mutation_leases_expiry_idx").on(
|
|
153
|
+
table.expiresAt,
|
|
154
|
+
table.sessionSetId,
|
|
155
|
+
),
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
export const managedAuthLoginTransactionRateLimits = pgTable(
|
|
160
|
+
"managed_auth_login_transaction_rate_limits",
|
|
161
|
+
{
|
|
162
|
+
scopeKind: text("scope_kind").notNull(),
|
|
163
|
+
scopeHash: text("scope_hash").notNull(),
|
|
164
|
+
windowStartedAt: timestamp("window_started_at", { withTimezone: true }).notNull(),
|
|
165
|
+
attemptCount: integer("attempt_count").notNull(),
|
|
166
|
+
expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
|
|
167
|
+
},
|
|
168
|
+
(table) => ({
|
|
169
|
+
primaryKey: primaryKey({ columns: [table.scopeKind, table.scopeHash] }),
|
|
170
|
+
expiry: index("managed_auth_login_transaction_rate_limits_expiry_idx").on(
|
|
171
|
+
table.expiresAt,
|
|
172
|
+
table.scopeKind,
|
|
173
|
+
table.scopeHash,
|
|
174
|
+
),
|
|
175
|
+
}),
|
|
176
|
+
);
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ManagedAuthLoginTransaction,
|
|
3
|
+
ManagedAuthLogoutAllReceipt,
|
|
4
|
+
ManagedAuthSessionSetProjection,
|
|
5
|
+
type ManagedAuthLoginTransaction as ManagedAuthLoginTransactionType,
|
|
6
|
+
type ManagedAuthLogoutAllReceipt as ManagedAuthLogoutAllReceiptType,
|
|
7
|
+
type ManagedAuthSessionSetMode,
|
|
8
|
+
type ManagedAuthSessionSetProjection as ManagedAuthSessionSetProjectionType,
|
|
9
|
+
} from "@opengeni/contracts/managed-auth-session-sets";
|
|
10
|
+
import { sql, type SQL } from "drizzle-orm";
|
|
11
|
+
import type { Database } from "./database";
|
|
12
|
+
import { rawRows } from "./database";
|
|
13
|
+
import { nestedPostgresSqlState } from "./persistence-errors";
|
|
14
|
+
|
|
15
|
+
const DatabaseProjection = ManagedAuthSessionSetProjection.omit({ csrfToken: true });
|
|
16
|
+
export type ManagedAuthDatabaseProjection = Omit<ManagedAuthSessionSetProjectionType, "csrfToken">;
|
|
17
|
+
|
|
18
|
+
export type ManagedAuthSelectedSession = {
|
|
19
|
+
slotId: string;
|
|
20
|
+
authSessionId: string;
|
|
21
|
+
authUserId: string;
|
|
22
|
+
token: string;
|
|
23
|
+
email: string;
|
|
24
|
+
name: string;
|
|
25
|
+
emailVerified: boolean;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ManagedAuthInternalSlot = ManagedAuthSelectedSession;
|
|
29
|
+
|
|
30
|
+
export type ManagedAuthAdoptedSessionSnapshot = {
|
|
31
|
+
authorityHash: string;
|
|
32
|
+
actorEpoch: string;
|
|
33
|
+
selected: ManagedAuthSelectedSession | null;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export class ManagedAuthSessionSetGenerationConflictError extends Error {
|
|
37
|
+
readonly name = "ManagedAuthSessionSetGenerationConflictError";
|
|
38
|
+
readonly code = "generation_conflict";
|
|
39
|
+
constructor() {
|
|
40
|
+
super("The browser session set changed in another request");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class ManagedAuthSessionSetOperationReuseError extends Error {
|
|
45
|
+
readonly name = "ManagedAuthSessionSetOperationReuseError";
|
|
46
|
+
constructor() {
|
|
47
|
+
super("The browser session-set operation id was reused for different input");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class ManagedAuthSessionSetAuthorityError extends Error {
|
|
52
|
+
readonly name = "ManagedAuthSessionSetAuthorityError";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class ManagedAuthLoginSlotUnavailableError extends Error {
|
|
56
|
+
readonly name = "ManagedAuthLoginSlotUnavailableError";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class ManagedAuthLoginSlotLimitError extends Error {
|
|
60
|
+
readonly name = "ManagedAuthLoginSlotLimitError";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class ManagedAuthLoginSlotAlreadyExistsError extends Error {
|
|
64
|
+
readonly name = "ManagedAuthLoginSlotAlreadyExistsError";
|
|
65
|
+
readonly code = "slot_already_exists";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class ManagedAuthActorMutationInFlightError extends Error {
|
|
69
|
+
readonly name = "ManagedAuthActorMutationInFlightError";
|
|
70
|
+
readonly code = "actor_mutation_in_flight";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class ManagedAuthLoginTransactionRateLimitError extends Error {
|
|
74
|
+
readonly name = "ManagedAuthLoginTransactionRateLimitError";
|
|
75
|
+
readonly code = "login_transaction_rate_limited";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function mapSqlError(error: unknown): never {
|
|
79
|
+
const state = nestedPostgresSqlState(error);
|
|
80
|
+
if (state === "40001") throw new ManagedAuthSessionSetGenerationConflictError();
|
|
81
|
+
if (state === "23505") throw new ManagedAuthSessionSetOperationReuseError();
|
|
82
|
+
if (state === "P0002") throw new ManagedAuthLoginSlotUnavailableError();
|
|
83
|
+
if (state === "P0003") throw new ManagedAuthLoginSlotAlreadyExistsError();
|
|
84
|
+
if (state === "54000") throw new ManagedAuthLoginSlotLimitError();
|
|
85
|
+
if (state === "55P03") throw new ManagedAuthActorMutationInFlightError();
|
|
86
|
+
if (state === "P0004") throw new ManagedAuthLoginTransactionRateLimitError();
|
|
87
|
+
if (state === "42501") {
|
|
88
|
+
throw new ManagedAuthSessionSetAuthorityError(
|
|
89
|
+
error instanceof Error ? error.message : "Browser session-set authority denied",
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function jsonValue(value: unknown): unknown {
|
|
96
|
+
return typeof value === "string" ? JSON.parse(value) : value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function oneJson(db: Database, query: SQL): Promise<unknown> {
|
|
100
|
+
const [row] = await rawRows<{ result: unknown }>(db, query);
|
|
101
|
+
return row ? jsonValue(row.result) : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function projectionFromResult(value: unknown): ManagedAuthDatabaseProjection {
|
|
105
|
+
const projection = (value as { projection?: unknown } | null)?.projection;
|
|
106
|
+
return DatabaseProjection.parse(projection);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function getManagedAuthSessionSetAuthorityState(
|
|
110
|
+
db: Database,
|
|
111
|
+
authorityHash: string,
|
|
112
|
+
): Promise<"absent" | "active" | "retired"> {
|
|
113
|
+
try {
|
|
114
|
+
const [row] = await rawRows<{ state: string }>(
|
|
115
|
+
db,
|
|
116
|
+
sql`select managed_auth_session_set_authority_state(${authorityHash}) as state`,
|
|
117
|
+
);
|
|
118
|
+
const value = row?.state;
|
|
119
|
+
if (value === "absent" || value === "active" || value === "retired") return value;
|
|
120
|
+
throw new ManagedAuthSessionSetAuthorityError("Invalid browser session-set authority state");
|
|
121
|
+
} catch (error) {
|
|
122
|
+
mapSqlError(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function getManagedAuthSessionSetSnapshot(
|
|
127
|
+
db: Database,
|
|
128
|
+
input: {
|
|
129
|
+
authorityHash: string;
|
|
130
|
+
mode: ManagedAuthSessionSetMode;
|
|
131
|
+
includeInternal?: boolean;
|
|
132
|
+
allowRecovery?: boolean;
|
|
133
|
+
readOnly?: boolean;
|
|
134
|
+
},
|
|
135
|
+
): Promise<{
|
|
136
|
+
projection: ManagedAuthDatabaseProjection;
|
|
137
|
+
selected: ManagedAuthSelectedSession | null;
|
|
138
|
+
internalSlots: ManagedAuthInternalSlot[];
|
|
139
|
+
} | null> {
|
|
140
|
+
try {
|
|
141
|
+
const value = await oneJson(
|
|
142
|
+
db,
|
|
143
|
+
sql`select managed_auth_session_set_snapshot(
|
|
144
|
+
${input.authorityHash}, ${input.mode}, ${input.includeInternal ?? false},
|
|
145
|
+
${input.allowRecovery ?? false}, ${input.readOnly ?? false}
|
|
146
|
+
) as result`,
|
|
147
|
+
);
|
|
148
|
+
if (value === null) return null;
|
|
149
|
+
const snapshot = value as {
|
|
150
|
+
projection: unknown;
|
|
151
|
+
selected?: ManagedAuthSelectedSession | null;
|
|
152
|
+
internalSlots?: ManagedAuthInternalSlot[];
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
projection: DatabaseProjection.parse(snapshot.projection),
|
|
156
|
+
selected: snapshot.selected ?? null,
|
|
157
|
+
internalSlots: snapshot.internalSlots ?? [],
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
mapSqlError(error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export async function bootstrapManagedAuthSessionSet(
|
|
165
|
+
db: Database,
|
|
166
|
+
input: {
|
|
167
|
+
authorityHash: string;
|
|
168
|
+
csrfHash: string;
|
|
169
|
+
authSessionId: string | null;
|
|
170
|
+
mode: ManagedAuthSessionSetMode;
|
|
171
|
+
operationId: string;
|
|
172
|
+
requestDigest: string;
|
|
173
|
+
expectedGeneration: string;
|
|
174
|
+
expectedActorEpoch: string;
|
|
175
|
+
},
|
|
176
|
+
): Promise<ManagedAuthDatabaseProjection> {
|
|
177
|
+
try {
|
|
178
|
+
return projectionFromResult(
|
|
179
|
+
await oneJson(
|
|
180
|
+
db,
|
|
181
|
+
sql`select managed_auth_session_set_bootstrap(
|
|
182
|
+
${input.authorityHash}, ${input.csrfHash}, ${input.authSessionId}, ${input.mode},
|
|
183
|
+
${input.operationId}::uuid, ${input.requestDigest},
|
|
184
|
+
${input.expectedGeneration}::bigint, ${input.expectedActorEpoch}::bigint
|
|
185
|
+
) as result`,
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
mapSqlError(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function beginManagedAuthLoginTransaction(
|
|
194
|
+
db: Database,
|
|
195
|
+
input: {
|
|
196
|
+
authorityHash: string;
|
|
197
|
+
csrfHash: string;
|
|
198
|
+
rateScopeHash?: string;
|
|
199
|
+
operationId: string;
|
|
200
|
+
requestDigest: string;
|
|
201
|
+
expectedGeneration: string;
|
|
202
|
+
expectedActorEpoch: string;
|
|
203
|
+
transactionId: string;
|
|
204
|
+
transactionSecretHash: string;
|
|
205
|
+
kind: "add" | "reauth";
|
|
206
|
+
targetSlotId: string | null;
|
|
207
|
+
returnIntentId: string | null;
|
|
208
|
+
returnPath: string | null;
|
|
209
|
+
expiresAt: Date;
|
|
210
|
+
},
|
|
211
|
+
): Promise<ManagedAuthLoginTransactionType> {
|
|
212
|
+
try {
|
|
213
|
+
const value = (await oneJson(
|
|
214
|
+
db,
|
|
215
|
+
sql`select managed_auth_session_set_begin_transaction(
|
|
216
|
+
${input.authorityHash}, ${input.csrfHash},
|
|
217
|
+
${input.rateScopeHash ?? input.authorityHash}, ${input.operationId}::uuid,
|
|
218
|
+
${input.requestDigest}, ${input.expectedGeneration}::bigint,
|
|
219
|
+
${input.transactionId}::uuid, ${input.expectedActorEpoch}::bigint,
|
|
220
|
+
${input.transactionSecretHash}, ${input.kind},
|
|
221
|
+
${input.targetSlotId}::uuid, ${input.returnIntentId}::uuid,
|
|
222
|
+
${input.returnPath}, ${input.expiresAt.toISOString()}::timestamptz
|
|
223
|
+
) as result`,
|
|
224
|
+
)) as { expiresAt?: unknown } | null;
|
|
225
|
+
return ManagedAuthLoginTransaction.parse({
|
|
226
|
+
...value,
|
|
227
|
+
expiresAt:
|
|
228
|
+
typeof value?.expiresAt === "string" || value?.expiresAt instanceof Date
|
|
229
|
+
? new Date(value.expiresAt).toISOString()
|
|
230
|
+
: value?.expiresAt,
|
|
231
|
+
});
|
|
232
|
+
} catch (error) {
|
|
233
|
+
mapSqlError(error);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function completeManagedAuthLoginTransaction(
|
|
238
|
+
db: Database,
|
|
239
|
+
input: {
|
|
240
|
+
authorityHash: string;
|
|
241
|
+
csrfHash: string;
|
|
242
|
+
operationId: string;
|
|
243
|
+
requestDigest: string;
|
|
244
|
+
expectedGeneration: string;
|
|
245
|
+
expectedActorEpoch: string;
|
|
246
|
+
transactionId: string;
|
|
247
|
+
transactionSecretHash: string;
|
|
248
|
+
authSessionId: string;
|
|
249
|
+
mode: ManagedAuthSessionSetMode;
|
|
250
|
+
},
|
|
251
|
+
): Promise<{ projection: ManagedAuthDatabaseProjection; returnIntent: string | null }> {
|
|
252
|
+
try {
|
|
253
|
+
const result = (await oneJson(
|
|
254
|
+
db,
|
|
255
|
+
sql`select managed_auth_session_set_complete_transaction(
|
|
256
|
+
${input.authorityHash}, ${input.csrfHash}, ${input.operationId}::uuid,
|
|
257
|
+
${input.requestDigest}, ${input.expectedGeneration}::bigint,
|
|
258
|
+
${input.expectedActorEpoch}::bigint,
|
|
259
|
+
${input.transactionId}::uuid, ${input.transactionSecretHash},
|
|
260
|
+
${input.authSessionId}, ${input.mode}
|
|
261
|
+
) as result`,
|
|
262
|
+
)) as { projection: unknown; returnIntent?: string | null };
|
|
263
|
+
return {
|
|
264
|
+
projection: DatabaseProjection.parse(result.projection),
|
|
265
|
+
returnIntent: result.returnIntent ?? null,
|
|
266
|
+
};
|
|
267
|
+
} catch (error) {
|
|
268
|
+
mapSqlError(error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export async function mutateManagedAuthSessionSet(
|
|
273
|
+
db: Database,
|
|
274
|
+
input: {
|
|
275
|
+
authorityHash: string;
|
|
276
|
+
csrfHash: string;
|
|
277
|
+
operationId: string;
|
|
278
|
+
requestDigest: string;
|
|
279
|
+
expectedGeneration: string;
|
|
280
|
+
expectedActorEpoch: string;
|
|
281
|
+
operationType: "cancel_transaction" | "select" | "logout_one" | "logout_all";
|
|
282
|
+
targetSlotId?: string | null;
|
|
283
|
+
replacementSlotId?: string | null;
|
|
284
|
+
transactionId?: string | null;
|
|
285
|
+
transactionSecretHash?: string | null;
|
|
286
|
+
mode: ManagedAuthSessionSetMode;
|
|
287
|
+
},
|
|
288
|
+
): Promise<ManagedAuthDatabaseProjection | ManagedAuthLogoutAllReceiptType> {
|
|
289
|
+
try {
|
|
290
|
+
const result = await oneJson(
|
|
291
|
+
db,
|
|
292
|
+
sql`select managed_auth_session_set_mutate(
|
|
293
|
+
${input.authorityHash}, ${input.csrfHash}, ${input.operationId}::uuid,
|
|
294
|
+
${input.requestDigest}, ${input.expectedGeneration}::bigint,
|
|
295
|
+
${input.expectedActorEpoch}::bigint, ${input.operationType},
|
|
296
|
+
${input.targetSlotId ?? null}::uuid, ${input.replacementSlotId ?? null}::uuid,
|
|
297
|
+
${input.transactionId ?? null}::uuid, ${input.transactionSecretHash ?? null}, ${input.mode}
|
|
298
|
+
) as result`,
|
|
299
|
+
);
|
|
300
|
+
if (input.operationType === "logout_all") return ManagedAuthLogoutAllReceipt.parse(result);
|
|
301
|
+
return projectionFromResult(result);
|
|
302
|
+
} catch (error) {
|
|
303
|
+
mapSqlError(error);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export async function getManagedAuthSessionSetOperationReceipt(
|
|
308
|
+
db: Database,
|
|
309
|
+
input: { authorityHash: string; operationId: string; requestDigest: string },
|
|
310
|
+
): Promise<{ projection: ManagedAuthDatabaseProjection; returnIntent: string | null } | null> {
|
|
311
|
+
const result = (await oneJson(
|
|
312
|
+
db,
|
|
313
|
+
sql`select managed_auth_session_set_operation_receipt(
|
|
314
|
+
${input.authorityHash}, ${input.operationId}::uuid, ${input.requestDigest}, 'complete'
|
|
315
|
+
) as result`,
|
|
316
|
+
)) as { projection?: unknown; returnIntent?: string | null } | null;
|
|
317
|
+
if (!result?.projection) return null;
|
|
318
|
+
return {
|
|
319
|
+
projection: DatabaseProjection.parse(result.projection),
|
|
320
|
+
returnIntent: result.returnIntent ?? null,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export async function acquireManagedAuthActorMutationLease(
|
|
325
|
+
db: Database,
|
|
326
|
+
input: {
|
|
327
|
+
authorityHash: string;
|
|
328
|
+
actorEpoch: string;
|
|
329
|
+
requestId: string;
|
|
330
|
+
leaseSeconds: number;
|
|
331
|
+
},
|
|
332
|
+
): Promise<Date> {
|
|
333
|
+
try {
|
|
334
|
+
const [row] = await rawRows<{ expiresAt: Date | string }>(
|
|
335
|
+
db,
|
|
336
|
+
sql`select managed_auth_actor_mutation_lease_acquire(
|
|
337
|
+
${input.authorityHash}, ${input.actorEpoch}::bigint,
|
|
338
|
+
${input.requestId}::uuid, ${input.leaseSeconds}::integer
|
|
339
|
+
) as "expiresAt"`,
|
|
340
|
+
);
|
|
341
|
+
if (!row) throw new ManagedAuthSessionSetAuthorityError("Mutation lease was not acquired");
|
|
342
|
+
return row.expiresAt instanceof Date ? row.expiresAt : new Date(row.expiresAt);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
mapSqlError(error);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export async function releaseManagedAuthActorMutationLease(
|
|
349
|
+
db: Database,
|
|
350
|
+
input: { authorityHash: string; requestId: string },
|
|
351
|
+
): Promise<boolean> {
|
|
352
|
+
try {
|
|
353
|
+
const [row] = await rawRows<{ released: boolean }>(
|
|
354
|
+
db,
|
|
355
|
+
sql`select managed_auth_actor_mutation_lease_release(
|
|
356
|
+
${input.authorityHash}, ${input.requestId}::uuid
|
|
357
|
+
) as released`,
|
|
358
|
+
);
|
|
359
|
+
return row?.released === true;
|
|
360
|
+
} catch (error) {
|
|
361
|
+
mapSqlError(error);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Classify a legacy ambient session once it has entered session-set authority. */
|
|
366
|
+
export async function getManagedAuthAdoptedSessionSnapshot(
|
|
367
|
+
db: Database,
|
|
368
|
+
authSessionId: string,
|
|
369
|
+
): Promise<ManagedAuthAdoptedSessionSnapshot | null> {
|
|
370
|
+
try {
|
|
371
|
+
const value = (await oneJson(
|
|
372
|
+
db,
|
|
373
|
+
sql`select managed_auth_adopted_session_snapshot(${authSessionId}) as result`,
|
|
374
|
+
)) as ManagedAuthAdoptedSessionSnapshot | null;
|
|
375
|
+
if (value === null) return null;
|
|
376
|
+
if (!/^[0-9a-f]{64}$/.test(value.authorityHash) || !/^[1-9][0-9]*$/.test(value.actorEpoch)) {
|
|
377
|
+
throw new ManagedAuthSessionSetAuthorityError("Invalid adopted-session snapshot");
|
|
378
|
+
}
|
|
379
|
+
return value;
|
|
380
|
+
} catch (error) {
|
|
381
|
+
mapSqlError(error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** Exact post-handler proof that the request-owned actor lease is still live. */
|
|
386
|
+
export async function validateManagedAuthActorMutationLease(
|
|
387
|
+
db: Database,
|
|
388
|
+
input: { authorityHash: string; actorEpoch: string; requestId: string },
|
|
389
|
+
): Promise<boolean> {
|
|
390
|
+
try {
|
|
391
|
+
const [row] = await rawRows<{ valid: boolean }>(
|
|
392
|
+
db,
|
|
393
|
+
sql`select managed_auth_actor_mutation_lease_validate(
|
|
394
|
+
${input.authorityHash}, ${input.actorEpoch}::bigint, ${input.requestId}::uuid
|
|
395
|
+
) as valid`,
|
|
396
|
+
);
|
|
397
|
+
return row?.valid === true;
|
|
398
|
+
} catch (error) {
|
|
399
|
+
mapSqlError(error);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** Bounded cleanup for provider sessions left by a process death before adoption. */
|
|
404
|
+
export async function reapManagedAuthIsolatedSessions(db: Database, limit = 100): Promise<number> {
|
|
405
|
+
try {
|
|
406
|
+
const [row] = await rawRows<{ reaped: number }>(
|
|
407
|
+
db,
|
|
408
|
+
sql`select managed_auth_isolated_session_reap(${limit}::integer) as reaped`,
|
|
409
|
+
);
|
|
410
|
+
return row?.reaped ?? 0;
|
|
411
|
+
} catch (error) {
|
|
412
|
+
mapSqlError(error);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Bounded, lease-aware retirement of expired installation/session-set authority. */
|
|
417
|
+
export async function reapExpiredManagedAuthSessionSets(
|
|
418
|
+
db: Database,
|
|
419
|
+
limit = 100,
|
|
420
|
+
): Promise<number> {
|
|
421
|
+
try {
|
|
422
|
+
const [row] = await rawRows<{ retired: number }>(
|
|
423
|
+
db,
|
|
424
|
+
sql`select managed_auth_expired_session_set_reap(${limit}::integer) as retired`,
|
|
425
|
+
);
|
|
426
|
+
return row?.retired ?? 0;
|
|
427
|
+
} catch (error) {
|
|
428
|
+
mapSqlError(error);
|
|
429
|
+
}
|
|
430
|
+
}
|