@opengeni/core 2.4.0-canary.2 → 2.5.2-canary.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.
@@ -3,6 +3,7 @@ import { type AccountGrant, type AccessContext, type AccessGrant, type Permissio
3
3
  import { type Database } from "@opengeni/db";
4
4
  import type { Context } from "hono";
5
5
  import type { ManagedAuth } from "../managed-auth-type.js";
6
+ import type { ManagedAuthSessionAdapter } from "../managed-auth-session-sets.js";
6
7
  /**
7
8
  * Opaque, request-local proof that the canonical access resolver authorized the
8
9
  * exact account administrator named by the stamp. The random id is audit and
@@ -19,6 +20,7 @@ export type AccessDeps = {
19
20
  db: Database;
20
21
  settings: Settings;
21
22
  managedAuth?: ManagedAuth | null;
23
+ managedAuthSessionAdapter?: ManagedAuthSessionAdapter | null;
22
24
  };
23
25
  export declare function requireAccessContext(c: Context, deps: AccessDeps): Promise<AccessContext>;
24
26
  export declare function requireAccessGrant(c: Context, deps: AccessDeps, workspaceId: string, permission?: Permission): Promise<AccessGrant>;
@@ -0,0 +1,24 @@
1
+ import type { AccessGrant, ResourceRef, SubmitComposerDraftRequest, SubmitComposerDraftResponse } from "@opengeni/contracts";
2
+ import type { AccessGrantAuthorization } from "../access/index.js";
3
+ import type { AcceptSessionUserMessageDependencies } from "../dependencies.js";
4
+ export type SubmitComposerDraftForRequestOptions = {
5
+ authorization?: AccessGrantAuthorization | undefined;
6
+ /**
7
+ * Trusted host-owned resources admitted with this command without writing
8
+ * them into the actor's browser-visible draft first. The saved draft remains
9
+ * the exact content fence for actor-owned text, annotations, resources, and
10
+ * execution policy; these resources participate in the accepted command's
11
+ * idempotency hash, validation, turn, and durable session resource set.
12
+ */
13
+ additionalResources?: ResourceRef[] | undefined;
14
+ };
15
+ /**
16
+ * Atomically accept one established-session composer draft.
17
+ *
18
+ * This is the application boundary shared by the stock HTTP adapter and an
19
+ * in-process embedding host. A host may prepare its own durable business state
20
+ * before calling this function and project the returned receipt afterward, but
21
+ * OpenGeni remains the sole authority for draft validation/rotation, event
22
+ * append, turn creation, routing, and idempotent replay.
23
+ */
24
+ export declare function submitComposerDraftForRequest(deps: AcceptSessionUserMessageDependencies, grant: AccessGrant, workspaceId: string, sessionId: string, input: SubmitComposerDraftRequest, options?: SubmitComposerDraftForRequestOptions): Promise<SubmitComposerDraftResponse>;
@@ -68,8 +68,8 @@ export declare function controlAgentSessionWorkstream(deps: {
68
68
  }): Promise<{
69
69
  receipt: SessionCommandReceiptRow;
70
70
  control: import("@opengeni/db").EffectiveSessionControl;
71
- sessionControlEventId: string;
72
- workspaceControlEventId: string;
71
+ sessionControlEventId: string | null;
72
+ workspaceControlEventId: string | null;
73
73
  interruptionCount: number;
74
74
  backgroundCommandCount: number;
75
75
  wakeCount: number;
@@ -80,6 +80,7 @@ export declare function controlAgentSessionWorkstream(deps: {
80
80
  eventIds: string[];
81
81
  }>;
82
82
  workflowWake: import("@opengeni/db").SessionControlWorkflowWake | null;
83
+ outcome: "changed" | "unchanged" | "replayed";
83
84
  replay: boolean;
84
85
  authorization: ResolvedSessionAuthorization | null;
85
86
  }>;
@@ -111,6 +112,7 @@ export declare function controlHumanSessionWorkstreamWithOutcome(deps: {
111
112
  sessionAuthorization?: SessionAuthorizationPort | null;
112
113
  } & SessionCommandPostCommitDeps, context: HumanSessionCommandContext, input: SessionControlRequest): Promise<{
113
114
  response: SessionControlResponse;
115
+ outcome: "changed" | "unchanged" | "replayed";
114
116
  replay: boolean;
115
117
  }>;
116
118
  /** Backward-compatible response path used by the REST control route. */
@@ -1,12 +1,17 @@
1
1
  import type { Database } from "@opengeni/db";
2
2
  import type { Context } from "hono";
3
3
  import type { ManagedAuth } from "./managed-auth-type.js";
4
+ export { getManagedAuthRequestActorAdmissionStamp, getManagedAuthRequestActorLeaseStamp, markManagedAuthRequestActorTransitionApplied, } from "./managed-session.js";
5
+ import type { ManagedAuthSessionAdapter } from "./managed-auth-session-sets.js";
6
+ import type { ManagedAuthSessionSetMode } from "@opengeni/contracts/managed-auth-session-sets";
4
7
  export type CanonicalHumanRequestIdentity = {
5
8
  authUserId: string;
6
9
  authSessionId: string;
7
10
  };
8
11
  export declare function requireCanonicalHumanRequestIdentity(context: Context, input: {
9
12
  db: Database;
10
- managedAuth?: ManagedAuth | null;
11
- allowRecovery?: boolean;
13
+ managedAuth?: ManagedAuth | null | undefined;
14
+ managedAuthSessionAdapter?: ManagedAuthSessionAdapter | null | undefined;
15
+ managedAuthSessionSetMode?: ManagedAuthSessionSetMode | undefined;
16
+ allowRecovery?: boolean | undefined;
12
17
  }): Promise<CanonicalHumanRequestIdentity>;
@@ -1,23 +1,36 @@
1
1
  import {
2
- getManagedSession
3
- } from "./chunk-IBOEYG6N.js";
2
+ getManagedAuthRequestActorAdmissionStamp,
3
+ getManagedAuthRequestActorLeaseStamp,
4
+ getManagedSession,
5
+ markManagedAuthRequestActorTransitionApplied
6
+ } from "./chunk-ZVZJTMSV.js";
7
+ import "./chunk-YGOMUGYS.js";
4
8
 
5
9
  // src/canonical-human-identities.ts
6
10
  import { HTTPException } from "hono/http-exception";
7
11
  async function requireCanonicalHumanRequestIdentity(context, input) {
8
12
  if (!input.managedAuth) {
9
- throw new HTTPException(404, { message: "Canonical human identity is unavailable" });
13
+ throw new HTTPException(404, {
14
+ message: "Canonical human identity is unavailable"
15
+ });
10
16
  }
11
17
  const session = await getManagedSession(context, input.managedAuth, {
12
18
  db: input.db,
19
+ sessionAdapter: input.managedAuthSessionAdapter,
20
+ sessionSetMode: input.managedAuthSessionSetMode,
13
21
  ...input.allowRecovery === void 0 ? {} : { allowIdentityRecovery: input.allowRecovery }
14
22
  });
15
23
  if (!session?.user || typeof session.session?.id !== "string") {
16
- throw new HTTPException(401, { message: "Managed human authentication required" });
24
+ throw new HTTPException(401, {
25
+ message: "Managed human authentication required"
26
+ });
17
27
  }
18
28
  return { authUserId: session.user.id, authSessionId: session.session.id };
19
29
  }
20
30
  export {
31
+ getManagedAuthRequestActorAdmissionStamp,
32
+ getManagedAuthRequestActorLeaseStamp,
33
+ markManagedAuthRequestActorTransitionApplied,
21
34
  requireCanonicalHumanRequestIdentity
22
35
  };
23
36
  //# sourceMappingURL=canonical-human-identities.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/canonical-human-identities.ts"],"sourcesContent":["import type { Database } from \"@opengeni/db\";\nimport type { Context } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport type { ManagedAuth } from \"./managed-auth-type\";\nimport { getManagedSession } from \"./managed-session\";\n\nexport type CanonicalHumanRequestIdentity = {\n authUserId: string;\n authSessionId: string;\n};\n\nexport async function requireCanonicalHumanRequestIdentity(\n context: Context,\n input: { db: Database; managedAuth?: ManagedAuth | null; allowRecovery?: boolean },\n): Promise<CanonicalHumanRequestIdentity> {\n if (!input.managedAuth) {\n throw new HTTPException(404, { message: \"Canonical human identity is unavailable\" });\n }\n const session = await getManagedSession(context, input.managedAuth, {\n db: input.db,\n ...(input.allowRecovery === undefined ? {} : { allowIdentityRecovery: input.allowRecovery }),\n });\n if (!session?.user || typeof session.session?.id !== \"string\") {\n throw new HTTPException(401, { message: \"Managed human authentication required\" });\n }\n return { authUserId: session.user.id, authSessionId: session.session.id };\n}\n"],"mappings":";;;;;AAEA,SAAS,qBAAqB;AAS9B,eAAsB,qCACpB,SACA,OACwC;AACxC,MAAI,CAAC,MAAM,aAAa;AACtB,UAAM,IAAI,cAAc,KAAK,EAAE,SAAS,0CAA0C,CAAC;AAAA,EACrF;AACA,QAAM,UAAU,MAAM,kBAAkB,SAAS,MAAM,aAAa;AAAA,IAClE,IAAI,MAAM;AAAA,IACV,GAAI,MAAM,kBAAkB,SAAY,CAAC,IAAI,EAAE,uBAAuB,MAAM,cAAc;AAAA,EAC5F,CAAC;AACD,MAAI,CAAC,SAAS,QAAQ,OAAO,QAAQ,SAAS,OAAO,UAAU;AAC7D,UAAM,IAAI,cAAc,KAAK,EAAE,SAAS,wCAAwC,CAAC;AAAA,EACnF;AACA,SAAO,EAAE,YAAY,QAAQ,KAAK,IAAI,eAAe,QAAQ,QAAQ,GAAG;AAC1E;","names":[]}
1
+ {"version":3,"sources":["../src/canonical-human-identities.ts"],"sourcesContent":["import type { Database } from \"@opengeni/db\";\nimport type { Context } from \"hono\";\nimport { HTTPException } from \"hono/http-exception\";\nimport type { ManagedAuth } from \"./managed-auth-type\";\nimport { getManagedSession } from \"./managed-session\";\nexport {\n getManagedAuthRequestActorAdmissionStamp,\n getManagedAuthRequestActorLeaseStamp,\n markManagedAuthRequestActorTransitionApplied,\n} from \"./managed-session\";\nimport type { ManagedAuthSessionAdapter } from \"./managed-auth-session-sets\";\nimport type { ManagedAuthSessionSetMode } from \"@opengeni/contracts/managed-auth-session-sets\";\n\nexport type CanonicalHumanRequestIdentity = {\n authUserId: string;\n authSessionId: string;\n};\n\nexport async function requireCanonicalHumanRequestIdentity(\n context: Context,\n input: {\n db: Database;\n managedAuth?: ManagedAuth | null | undefined;\n managedAuthSessionAdapter?: ManagedAuthSessionAdapter | null | undefined;\n managedAuthSessionSetMode?: ManagedAuthSessionSetMode | undefined;\n allowRecovery?: boolean | undefined;\n },\n): Promise<CanonicalHumanRequestIdentity> {\n if (!input.managedAuth) {\n throw new HTTPException(404, {\n message: \"Canonical human identity is unavailable\",\n });\n }\n const session = await getManagedSession(context, input.managedAuth, {\n db: input.db,\n sessionAdapter: input.managedAuthSessionAdapter,\n sessionSetMode: input.managedAuthSessionSetMode,\n ...(input.allowRecovery === undefined ? {} : { allowIdentityRecovery: input.allowRecovery }),\n });\n if (!session?.user || typeof session.session?.id !== \"string\") {\n throw new HTTPException(401, {\n message: \"Managed human authentication required\",\n });\n }\n return { authUserId: session.user.id, authSessionId: session.session.id };\n}\n"],"mappings":";;;;;;;;;AAEA,SAAS,qBAAqB;AAgB9B,eAAsB,qCACpB,SACA,OAOwC;AACxC,MAAI,CAAC,MAAM,aAAa;AACtB,UAAM,IAAI,cAAc,KAAK;AAAA,MAC3B,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,QAAM,UAAU,MAAM,kBAAkB,SAAS,MAAM,aAAa;AAAA,IAClE,IAAI,MAAM;AAAA,IACV,gBAAgB,MAAM;AAAA,IACtB,gBAAgB,MAAM;AAAA,IACtB,GAAI,MAAM,kBAAkB,SAAY,CAAC,IAAI,EAAE,uBAAuB,MAAM,cAAc;AAAA,EAC5F,CAAC;AACD,MAAI,CAAC,SAAS,QAAQ,OAAO,QAAQ,SAAS,OAAO,UAAU;AAC7D,UAAM,IAAI,cAAc,KAAK;AAAA,MAC3B,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,SAAO,EAAE,YAAY,QAAQ,KAAK,IAAI,eAAe,QAAQ,QAAQ,GAAG;AAC1E;","names":[]}
@@ -0,0 +1,226 @@
1
+ // src/managed-auth-session-sets.ts
2
+ import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
3
+ import {
4
+ completeManagedAuthLoginTransaction,
5
+ getManagedAuthSessionSetOperationReceipt,
6
+ getManagedAuthSessionSetSnapshot
7
+ } from "@opengeni/db";
8
+ var MANAGED_AUTH_SESSION_SET_COOKIE = "opengeni.session_set";
9
+ var MANAGED_AUTH_LOGIN_TRANSACTION_COOKIE = "opengeni.login_transaction";
10
+ var MANAGED_AUTH_CSRF_HEADER = "x-opengeni-session-csrf";
11
+ var MANAGED_AUTH_ACTOR_EPOCH_HEADER = "x-opengeni-actor-epoch";
12
+ var MANAGED_AUTH_LOGIN_TRANSACTION_COOKIE_PATH = "/v1/auth/session-set/transactions";
13
+ var ManagedAuthActorChangeError = class extends Error {
14
+ name = "ManagedAuthActorChangeError";
15
+ code = "actor_change_required";
16
+ constructor() {
17
+ super("The selected browser actor changed");
18
+ }
19
+ };
20
+ var ManagedAuthRequestAdmissionError = class extends Error {
21
+ name = "ManagedAuthRequestAdmissionError";
22
+ code = "origin_rejected";
23
+ };
24
+ var ManagedAuthCompletionOutcomeUnknownError = class extends Error {
25
+ name = "ManagedAuthCompletionOutcomeUnknownError";
26
+ code = "operation_outcome_unknown";
27
+ constructor(options) {
28
+ super("The managed authentication completion outcome is unknown", options);
29
+ }
30
+ };
31
+ function requireManagedAuthActorFence(input) {
32
+ if (input.expectedActorEpoch !== null && input.expectedActorEpoch !== input.actorEpoch || input.expectedActorEpoch === null && (input.mode === "broker" || input.actorEpoch !== "1" || input.selectedAuthSessionId !== null && input.legacyAmbientSessionId !== input.selectedAuthSessionId)) {
33
+ throw new ManagedAuthActorChangeError();
34
+ }
35
+ }
36
+ function managedAuthRandomAuthority() {
37
+ return randomBytes(32).toString("base64url");
38
+ }
39
+ function managedAuthSha256(value) {
40
+ return createHash("sha256").update(value, "utf8").digest("hex");
41
+ }
42
+ function managedAuthCsrfHash(authority) {
43
+ return managedAuthSha256(`opengeni:managed-auth:csrf-authority:v1
44
+ ${authority}`);
45
+ }
46
+ function managedAuthCsrfToken(signingSecret, authority, generation) {
47
+ return createHmac("sha256", signingSecret).update(`opengeni:managed-auth:csrf:v1
48
+ ${authority}
49
+ ${generation}`, "utf8").digest("base64url");
50
+ }
51
+ function managedAuthTransactionSecret(signingSecret, authority, operationId) {
52
+ return createHmac("sha256", signingSecret).update(`opengeni:managed-auth:transaction:v1
53
+ ${authority}
54
+ ${operationId}`, "utf8").digest("base64url");
55
+ }
56
+ function managedAuthDerivedUuid(namespace, value) {
57
+ const bytes = createHash("sha256").update(`${namespace}
58
+ ${value}`, "utf8").digest().subarray(0, 16);
59
+ bytes[6] = bytes[6] & 15 | 80;
60
+ bytes[8] = bytes[8] & 63 | 128;
61
+ const hex = bytes.toString("hex");
62
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
63
+ }
64
+ function canonicalJson(value) {
65
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
66
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
67
+ return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`).join(",")}}`;
68
+ }
69
+ function managedAuthRequestDigest(value) {
70
+ return managedAuthSha256(canonicalJson(value));
71
+ }
72
+ function managedAuthSecretRequestDigest(signingSecret, value) {
73
+ return createHmac("sha256", signingSecret).update(`opengeni:managed-auth:request:v1
74
+ ${canonicalJson(value)}`, "utf8").digest("hex");
75
+ }
76
+ function withManagedAuthCsrfToken(projection, signingSecret, authority) {
77
+ return {
78
+ ...projection,
79
+ csrfToken: managedAuthCsrfToken(signingSecret, authority, projection.generation)
80
+ };
81
+ }
82
+ function equalSecret(left, right) {
83
+ const leftBytes = Buffer.from(left, "utf8");
84
+ const rightBytes = Buffer.from(right, "utf8");
85
+ return leftBytes.length === rightBytes.length && timingSafeEqual(leftBytes, rightBytes);
86
+ }
87
+ function requireManagedAuthMutationAdmission(input) {
88
+ const origin = input.request.headers.get("origin");
89
+ const fetchSite = input.request.headers.get("sec-fetch-site");
90
+ const contentType = input.request.headers.get("content-type")?.split(";", 1)[0]?.trim();
91
+ const csrf = input.request.headers.get(MANAGED_AUTH_CSRF_HEADER);
92
+ const allowed = new Set(input.allowedOrigins.map((candidate) => new URL(candidate).origin));
93
+ const expectedCsrf = managedAuthCsrfToken(
94
+ input.signingSecret,
95
+ input.authority,
96
+ input.expectedGeneration
97
+ );
98
+ if (!origin || !allowed.has(origin) || fetchSite !== "same-origin" || contentType !== "application/json" || !csrf || !equalSecret(csrf, expectedCsrf)) {
99
+ throw new ManagedAuthRequestAdmissionError("Browser session-set mutation admission failed");
100
+ }
101
+ }
102
+ async function resolveManagedAuthSelectedSession(input) {
103
+ const snapshot = await getManagedAuthSessionSetSnapshot(input.db, {
104
+ authorityHash: managedAuthSha256(input.authority),
105
+ mode: input.mode,
106
+ includeInternal: true,
107
+ allowRecovery: input.allowRecovery ?? false,
108
+ readOnly: true
109
+ });
110
+ if (!snapshot) return null;
111
+ if (snapshot.projection.state === "actor_change_required") {
112
+ throw new ManagedAuthActorChangeError();
113
+ }
114
+ requireManagedAuthActorFence({
115
+ mode: input.mode,
116
+ actorEpoch: snapshot.projection.actorEpoch,
117
+ expectedActorEpoch: input.expectedActorEpoch,
118
+ selectedAuthSessionId: snapshot.selected?.authSessionId ?? null,
119
+ legacyAmbientSessionId: input.legacyAmbientSessionId ?? null
120
+ });
121
+ if (!snapshot.selected) return { session: null, projection: snapshot.projection };
122
+ const resolved = await input.adapter.resolveSelectedSession(snapshot.selected);
123
+ if (!resolved || resolved.session.id !== snapshot.selected.authSessionId || resolved.user.id !== snapshot.selected.authUserId) {
124
+ return { session: null, projection: snapshot.projection };
125
+ }
126
+ return { session: resolved, projection: snapshot.projection };
127
+ }
128
+ async function authenticateAndAdoptManagedAuthSession(input) {
129
+ try {
130
+ const existing = await getManagedAuthSessionSetOperationReceipt(input.db, {
131
+ authorityHash: managedAuthSha256(input.authority),
132
+ operationId: input.operationId,
133
+ requestDigest: input.requestDigest
134
+ });
135
+ if (existing) return existing;
136
+ } catch (error) {
137
+ throw new ManagedAuthCompletionOutcomeUnknownError({ cause: error });
138
+ }
139
+ const created = await input.adapter.authenticate({
140
+ provider: "email_password",
141
+ transactionId: input.transactionId,
142
+ credentials: { email: input.email, password: input.password },
143
+ headers: input.isolatedHeaders
144
+ });
145
+ let completed;
146
+ try {
147
+ completed = await completeManagedAuthLoginTransaction(input.db, {
148
+ authorityHash: managedAuthSha256(input.authority),
149
+ csrfHash: input.csrfHash,
150
+ operationId: input.operationId,
151
+ requestDigest: input.requestDigest,
152
+ expectedGeneration: input.expectedGeneration,
153
+ expectedActorEpoch: input.expectedActorEpoch,
154
+ transactionId: input.transactionId,
155
+ transactionSecretHash: managedAuthSha256(input.transactionSecret),
156
+ authSessionId: created.authSessionId,
157
+ mode: input.mode
158
+ });
159
+ } catch (error) {
160
+ let receipt;
161
+ try {
162
+ receipt = await getManagedAuthSessionSetOperationReceipt(input.db, {
163
+ authorityHash: managedAuthSha256(input.authority),
164
+ operationId: input.operationId,
165
+ requestDigest: input.requestDigest
166
+ });
167
+ } catch (receiptError) {
168
+ throw new ManagedAuthCompletionOutcomeUnknownError({ cause: receiptError });
169
+ }
170
+ if (receipt) {
171
+ await reconcileCreatedManagedAuthSession(input, created.authSessionId);
172
+ return receipt;
173
+ }
174
+ await input.adapter.revokeSession(created).catch(() => void 0);
175
+ throw error;
176
+ }
177
+ try {
178
+ await reconcileCreatedManagedAuthSession(input, created.authSessionId);
179
+ } catch (error) {
180
+ throw new ManagedAuthCompletionOutcomeUnknownError({ cause: error });
181
+ }
182
+ return completed;
183
+ }
184
+ async function reconcileCreatedManagedAuthSession(input, authSessionId) {
185
+ const snapshot = await getManagedAuthSessionSetSnapshot(input.db, {
186
+ authorityHash: managedAuthSha256(input.authority),
187
+ mode: input.mode,
188
+ includeInternal: true,
189
+ readOnly: true
190
+ });
191
+ if (snapshot?.internalSlots.some((slot) => slot.authSessionId === authSessionId)) return;
192
+ await input.adapter.revokeSession({ authSessionId });
193
+ }
194
+ function isolatedManagedAuthHeaders(request) {
195
+ const headers = new Headers(request.headers);
196
+ headers.delete("cookie");
197
+ headers.delete("authorization");
198
+ headers.delete("x-forwarded-user");
199
+ return headers;
200
+ }
201
+
202
+ export {
203
+ MANAGED_AUTH_SESSION_SET_COOKIE,
204
+ MANAGED_AUTH_LOGIN_TRANSACTION_COOKIE,
205
+ MANAGED_AUTH_CSRF_HEADER,
206
+ MANAGED_AUTH_ACTOR_EPOCH_HEADER,
207
+ MANAGED_AUTH_LOGIN_TRANSACTION_COOKIE_PATH,
208
+ ManagedAuthActorChangeError,
209
+ ManagedAuthRequestAdmissionError,
210
+ ManagedAuthCompletionOutcomeUnknownError,
211
+ requireManagedAuthActorFence,
212
+ managedAuthRandomAuthority,
213
+ managedAuthSha256,
214
+ managedAuthCsrfHash,
215
+ managedAuthCsrfToken,
216
+ managedAuthTransactionSecret,
217
+ managedAuthDerivedUuid,
218
+ managedAuthRequestDigest,
219
+ managedAuthSecretRequestDigest,
220
+ withManagedAuthCsrfToken,
221
+ requireManagedAuthMutationAdmission,
222
+ resolveManagedAuthSelectedSession,
223
+ authenticateAndAdoptManagedAuthSession,
224
+ isolatedManagedAuthHeaders
225
+ };
226
+ //# sourceMappingURL=chunk-YGOMUGYS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/managed-auth-session-sets.ts"],"sourcesContent":["import { createHash, createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\nimport type {\n ManagedAuthSessionSetMode,\n ManagedAuthSessionSetProjection,\n} from \"@opengeni/contracts/managed-auth-session-sets\";\nimport {\n completeManagedAuthLoginTransaction,\n getManagedAuthSessionSetOperationReceipt,\n getManagedAuthSessionSetSnapshot,\n type Database,\n type ManagedAuthDatabaseProjection,\n type ManagedAuthSelectedSession,\n} from \"@opengeni/db\";\n\nexport const MANAGED_AUTH_SESSION_SET_COOKIE = \"opengeni.session_set\" as const;\nexport const MANAGED_AUTH_LOGIN_TRANSACTION_COOKIE = \"opengeni.login_transaction\" as const;\nexport const MANAGED_AUTH_CSRF_HEADER = \"x-opengeni-session-csrf\" as const;\nexport const MANAGED_AUTH_ACTOR_EPOCH_HEADER = \"x-opengeni-actor-epoch\" as const;\nexport const MANAGED_AUTH_LOGIN_TRANSACTION_COOKIE_PATH =\n \"/v1/auth/session-set/transactions\" as const;\n\nexport type ManagedAuthResolvedSession = {\n session: { id: string; userId: string; [key: string]: unknown };\n user: {\n id: string;\n email: string;\n name: string;\n emailVerified: boolean;\n [key: string]: unknown;\n };\n};\n\n/** Provider-neutral boundary; provider credentials and tokens never cross its output. */\nexport interface ManagedAuthSessionAdapter {\n authenticate(input: {\n provider: \"email_password\";\n transactionId: string;\n credentials: { email: string; password: string };\n headers: Headers;\n }): Promise<{ authSessionId: string }>;\n /** Verify an ambient provider cookie without sliding expiry or emitting cookies. */\n resolveAmbientSession(headers: Headers): Promise<ManagedAuthResolvedSession | null>;\n resolveSelectedSession(\n input: ManagedAuthSelectedSession,\n ): Promise<ManagedAuthResolvedSession | null>;\n refreshSelectedSession(\n input: ManagedAuthSelectedSession,\n ): Promise<ManagedAuthResolvedSession | null>;\n revokeSession(input: { authSessionId: string }): Promise<void>;\n /** Dual-mode exact selected-session cookie plus stale provider-cache invalidations. */\n createLegacySelectedSessionCookies(\n input: ManagedAuthSelectedSession | null,\n currentCookieHeader?: string | null,\n ): Promise<string[]>;\n}\n\nexport class ManagedAuthActorChangeError extends Error {\n readonly name = \"ManagedAuthActorChangeError\";\n readonly code = \"actor_change_required\";\n constructor() {\n super(\"The selected browser actor changed\");\n }\n}\n\nexport class ManagedAuthRequestAdmissionError extends Error {\n readonly name = \"ManagedAuthRequestAdmissionError\";\n readonly code = \"origin_rejected\";\n}\n\nexport class ManagedAuthCompletionOutcomeUnknownError extends Error {\n readonly name = \"ManagedAuthCompletionOutcomeUnknownError\";\n readonly code = \"operation_outcome_unknown\";\n constructor(options?: ErrorOptions) {\n super(\"The managed authentication completion outcome is unknown\", options);\n }\n}\n\nexport function requireManagedAuthActorFence(input: {\n mode: ManagedAuthSessionSetMode;\n actorEpoch: string;\n expectedActorEpoch: string | null;\n selectedAuthSessionId: string | null;\n legacyAmbientSessionId?: string | null;\n}): void {\n if (\n (input.expectedActorEpoch !== null && input.expectedActorEpoch !== input.actorEpoch) ||\n (input.expectedActorEpoch === null &&\n (input.mode === \"broker\" ||\n input.actorEpoch !== \"1\" ||\n (input.selectedAuthSessionId !== null &&\n input.legacyAmbientSessionId !== input.selectedAuthSessionId)))\n ) {\n throw new ManagedAuthActorChangeError();\n }\n}\n\nexport function managedAuthRandomAuthority(): string {\n return randomBytes(32).toString(\"base64url\");\n}\n\nexport function managedAuthSha256(value: string): string {\n return createHash(\"sha256\").update(value, \"utf8\").digest(\"hex\");\n}\n\nexport function managedAuthCsrfHash(authority: string): string {\n return managedAuthSha256(`opengeni:managed-auth:csrf-authority:v1\\n${authority}`);\n}\n\nexport function managedAuthCsrfToken(\n signingSecret: string,\n authority: string,\n generation: string,\n): string {\n return createHmac(\"sha256\", signingSecret)\n .update(`opengeni:managed-auth:csrf:v1\\n${authority}\\n${generation}`, \"utf8\")\n .digest(\"base64url\");\n}\n\nexport function managedAuthTransactionSecret(\n signingSecret: string,\n authority: string,\n operationId: string,\n): string {\n return createHmac(\"sha256\", signingSecret)\n .update(`opengeni:managed-auth:transaction:v1\\n${authority}\\n${operationId}`, \"utf8\")\n .digest(\"base64url\");\n}\n\nexport function managedAuthDerivedUuid(namespace: string, value: string): string {\n const bytes = createHash(\"sha256\")\n .update(`${namespace}\\n${value}`, \"utf8\")\n .digest()\n .subarray(0, 16);\n bytes[6] = (bytes[6]! & 0x0f) | 0x50;\n bytes[8] = (bytes[8]! & 0x3f) | 0x80;\n const hex = bytes.toString(\"hex\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n\nfunction canonicalJson(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value);\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n return `{${Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, child]) => `${JSON.stringify(key)}:${canonicalJson(child)}`)\n .join(\",\")}}`;\n}\n\nexport function managedAuthRequestDigest(value: unknown): string {\n return managedAuthSha256(canonicalJson(value));\n}\n\nexport function managedAuthSecretRequestDigest(signingSecret: string, value: unknown): string {\n return createHmac(\"sha256\", signingSecret)\n .update(`opengeni:managed-auth:request:v1\\n${canonicalJson(value)}`, \"utf8\")\n .digest(\"hex\");\n}\n\nexport function withManagedAuthCsrfToken(\n projection: ManagedAuthDatabaseProjection,\n signingSecret: string,\n authority: string,\n): ManagedAuthSessionSetProjection {\n return {\n ...projection,\n csrfToken: managedAuthCsrfToken(signingSecret, authority, projection.generation),\n };\n}\n\nfunction equalSecret(left: string, right: string): boolean {\n const leftBytes = Buffer.from(left, \"utf8\");\n const rightBytes = Buffer.from(right, \"utf8\");\n return leftBytes.length === rightBytes.length && timingSafeEqual(leftBytes, rightBytes);\n}\n\nexport function requireManagedAuthMutationAdmission(input: {\n request: Request;\n allowedOrigins: readonly string[];\n authority: string;\n signingSecret: string;\n expectedGeneration: string;\n}): void {\n const origin = input.request.headers.get(\"origin\");\n const fetchSite = input.request.headers.get(\"sec-fetch-site\");\n const contentType = input.request.headers.get(\"content-type\")?.split(\";\", 1)[0]?.trim();\n const csrf = input.request.headers.get(MANAGED_AUTH_CSRF_HEADER);\n const allowed = new Set(input.allowedOrigins.map((candidate) => new URL(candidate).origin));\n const expectedCsrf = managedAuthCsrfToken(\n input.signingSecret,\n input.authority,\n input.expectedGeneration,\n );\n if (\n !origin ||\n !allowed.has(origin) ||\n fetchSite !== \"same-origin\" ||\n contentType !== \"application/json\" ||\n !csrf ||\n !equalSecret(csrf, expectedCsrf)\n ) {\n throw new ManagedAuthRequestAdmissionError(\"Browser session-set mutation admission failed\");\n }\n}\n\nexport async function resolveManagedAuthSelectedSession(input: {\n db: Database;\n adapter: ManagedAuthSessionAdapter;\n authority: string;\n mode: ManagedAuthSessionSetMode;\n expectedActorEpoch: string | null;\n legacyAmbientSessionId?: string | null;\n allowRecovery?: boolean;\n}): Promise<{\n session: ManagedAuthResolvedSession | null;\n projection: ManagedAuthDatabaseProjection;\n} | null> {\n const snapshot = await getManagedAuthSessionSetSnapshot(input.db, {\n authorityHash: managedAuthSha256(input.authority),\n mode: input.mode,\n includeInternal: true,\n allowRecovery: input.allowRecovery ?? false,\n readOnly: true,\n });\n if (!snapshot) return null;\n if (snapshot.projection.state === \"actor_change_required\") {\n throw new ManagedAuthActorChangeError();\n }\n requireManagedAuthActorFence({\n mode: input.mode,\n actorEpoch: snapshot.projection.actorEpoch,\n expectedActorEpoch: input.expectedActorEpoch,\n selectedAuthSessionId: snapshot.selected?.authSessionId ?? null,\n legacyAmbientSessionId: input.legacyAmbientSessionId ?? null,\n });\n if (!snapshot.selected) return { session: null, projection: snapshot.projection };\n const resolved = await input.adapter.resolveSelectedSession(snapshot.selected);\n if (\n !resolved ||\n resolved.session.id !== snapshot.selected.authSessionId ||\n resolved.user.id !== snapshot.selected.authUserId\n ) {\n return { session: null, projection: snapshot.projection };\n }\n return { session: resolved, projection: snapshot.projection };\n}\n\nexport async function authenticateAndAdoptManagedAuthSession(input: {\n db: Database;\n adapter: ManagedAuthSessionAdapter;\n isolatedHeaders: Headers;\n authority: string;\n csrfHash: string;\n operationId: string;\n requestDigest: string;\n expectedGeneration: string;\n expectedActorEpoch: string;\n transactionId: string;\n transactionSecret: string;\n email: string;\n password: string;\n mode: ManagedAuthSessionSetMode;\n}): Promise<{ projection: ManagedAuthDatabaseProjection; returnIntent: string | null }> {\n try {\n const existing = await getManagedAuthSessionSetOperationReceipt(input.db, {\n authorityHash: managedAuthSha256(input.authority),\n operationId: input.operationId,\n requestDigest: input.requestDigest,\n });\n if (existing) return existing;\n } catch (error) {\n // Provider authentication creates a durable session. Do not perform it\n // while exact-replay reconciliation is unavailable.\n throw new ManagedAuthCompletionOutcomeUnknownError({ cause: error });\n }\n const created = await input.adapter.authenticate({\n provider: \"email_password\",\n transactionId: input.transactionId,\n credentials: { email: input.email, password: input.password },\n headers: input.isolatedHeaders,\n });\n let completed: { projection: ManagedAuthDatabaseProjection; returnIntent: string | null };\n try {\n completed = await completeManagedAuthLoginTransaction(input.db, {\n authorityHash: managedAuthSha256(input.authority),\n csrfHash: input.csrfHash,\n operationId: input.operationId,\n requestDigest: input.requestDigest,\n expectedGeneration: input.expectedGeneration,\n expectedActorEpoch: input.expectedActorEpoch,\n transactionId: input.transactionId,\n transactionSecretHash: managedAuthSha256(input.transactionSecret),\n authSessionId: created.authSessionId,\n mode: input.mode,\n });\n } catch (error) {\n let receipt: Awaited<ReturnType<typeof getManagedAuthSessionSetOperationReceipt>>;\n try {\n receipt = await getManagedAuthSessionSetOperationReceipt(input.db, {\n authorityHash: managedAuthSha256(input.authority),\n operationId: input.operationId,\n requestDigest: input.requestDigest,\n });\n } catch (receiptError) {\n throw new ManagedAuthCompletionOutcomeUnknownError({ cause: receiptError });\n }\n if (receipt) {\n await reconcileCreatedManagedAuthSession(input, created.authSessionId);\n return receipt;\n }\n await input.adapter.revokeSession(created).catch(() => undefined);\n throw error;\n }\n try {\n await reconcileCreatedManagedAuthSession(input, created.authSessionId);\n } catch (error) {\n throw new ManagedAuthCompletionOutcomeUnknownError({ cause: error });\n }\n return completed;\n}\n\nasync function reconcileCreatedManagedAuthSession(\n input: Pick<\n Parameters<typeof authenticateAndAdoptManagedAuthSession>[0],\n \"db\" | \"adapter\" | \"authority\" | \"mode\"\n >,\n authSessionId: string,\n): Promise<void> {\n const snapshot = await getManagedAuthSessionSetSnapshot(input.db, {\n authorityHash: managedAuthSha256(input.authority),\n mode: input.mode,\n includeInternal: true,\n readOnly: true,\n });\n if (snapshot?.internalSlots.some((slot) => slot.authSessionId === authSessionId)) return;\n await input.adapter.revokeSession({ authSessionId });\n}\n\nexport function isolatedManagedAuthHeaders(request: Request): Headers {\n const headers = new Headers(request.headers);\n headers.delete(\"cookie\");\n headers.delete(\"authorization\");\n headers.delete(\"x-forwarded-user\");\n return headers;\n}\n"],"mappings":"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","names":[]}