From 6daa31de0b9a15ab4730bf9d7e4de393f7b41a62 Mon Sep 17 00:00:00 2001 From: Aman <1402357+Zartharas@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:52:44 -0600 Subject: [PATCH 1/6] feat(providers): add web-session contract builder --- src/lib/providers/webSessionContract.ts | 58 +++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/lib/providers/webSessionContract.ts diff --git a/src/lib/providers/webSessionContract.ts b/src/lib/providers/webSessionContract.ts new file mode 100644 index 0000000000..4824e9adc0 --- /dev/null +++ b/src/lib/providers/webSessionContract.ts @@ -0,0 +1,58 @@ +import { + listExtractionConfigs, + type TokenSource, +} from "../../../open-sse/services/tokenExtractionConfig.ts"; +import { getWebSessionCredentialRequirement } from "../../shared/providers/webSessionCredentials.ts"; + +export const WEB_SESSION_CONTRACT_VERSION = 1; + +export interface WebSessionContractProvider { + providerId: string; + displayName: string; + loginUrl: string; + homeUrl: string; + tokenSources: TokenSource[]; + credential: { + kind: "cookie" | "token"; + storageKeys: string[]; + acceptsFullCookieHeader: boolean; + }; +} + +export interface WebSessionContract { + version: typeof WEB_SESSION_CONTRACT_VERSION; + providers: WebSessionContractProvider[]; +} + +/** + * Publish only the canonical, non-secret metadata needed by external + * credential brokers to capture credentials in the same shape OmniRoute + * accepts. Provider instructions, polling state, and credential values are + * intentionally excluded. + */ +export function buildWebSessionContract(): WebSessionContract { + const providers = listExtractionConfigs().flatMap((config) => { + const requirement = getWebSessionCredentialRequirement(config.providerId); + if (!requirement || requirement.kind === "none") return []; + + return [ + { + providerId: config.providerId, + displayName: config.displayName, + loginUrl: config.loginUrl, + homeUrl: config.homeUrl, + tokenSources: config.tokenSources.map((source) => ({ ...source })), + credential: { + kind: requirement.kind, + storageKeys: [...requirement.storageKeys], + acceptsFullCookieHeader: requirement.acceptsFullCookieHeader, + }, + }, + ]; + }); + + return { + version: WEB_SESSION_CONTRACT_VERSION, + providers, + }; +} From ae0b79ab7c1fdd13c0b9a047238c0c44dfecd587 Mon Sep 17 00:00:00 2001 From: Aman <1402357+Zartharas@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:52:49 -0600 Subject: [PATCH 2/6] feat(api): publish web-session credential contract --- src/app/api/providers/web-session-contract/route.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/app/api/providers/web-session-contract/route.ts diff --git a/src/app/api/providers/web-session-contract/route.ts b/src/app/api/providers/web-session-contract/route.ts new file mode 100644 index 0000000000..aaa0754d51 --- /dev/null +++ b/src/app/api/providers/web-session-contract/route.ts @@ -0,0 +1,10 @@ +import { NextResponse } from "next/server"; +import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; +import { buildWebSessionContract } from "@/lib/providers/webSessionContract"; + +export async function GET(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + + return NextResponse.json(buildWebSessionContract()); +} From a84f0b683ed6eacd2da5d4dfc11851e934d8324e Mon Sep 17 00:00:00 2001 From: Aman <1402357+Zartharas@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:53:01 -0600 Subject: [PATCH 3/6] test(providers): cover web-session contract publication --- tests/unit/web-session-contract.test.ts | 91 +++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/unit/web-session-contract.test.ts diff --git a/tests/unit/web-session-contract.test.ts b/tests/unit/web-session-contract.test.ts new file mode 100644 index 0000000000..8007d27c3b --- /dev/null +++ b/tests/unit/web-session-contract.test.ts @@ -0,0 +1,91 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +import { listExtractionConfigs } from "../../open-sse/services/tokenExtractionConfig.ts"; +import { + buildWebSessionContract, + WEB_SESSION_CONTRACT_VERSION, +} from "../../src/lib/providers/webSessionContract.ts"; +import { getWebSessionCredentialRequirement } from "../../src/shared/providers/webSessionCredentials.ts"; + +test("web-session contract mirrors canonical extraction and credential metadata", () => { + const contract = buildWebSessionContract(); + assert.equal(contract.version, WEB_SESSION_CONTRACT_VERSION); + + const expected = listExtractionConfigs().flatMap((config) => { + const requirement = getWebSessionCredentialRequirement(config.providerId); + return requirement && requirement.kind !== "none" ? [{ config, requirement }] : []; + }); + + assert.equal(contract.providers.length, expected.length); + assert.equal(new Set(contract.providers.map((provider) => provider.providerId)).size, expected.length); + + for (const { config, requirement } of expected) { + const published = contract.providers.find((provider) => provider.providerId === config.providerId); + assert.ok(published, `${config.providerId} must be published`); + assert.equal(published.displayName, config.displayName); + assert.equal(published.loginUrl, config.loginUrl); + assert.equal(published.homeUrl, config.homeUrl); + assert.deepEqual(published.tokenSources, config.tokenSources); + assert.equal(published.credential.kind, requirement.kind); + assert.deepEqual(published.credential.storageKeys, [...requirement.storageKeys]); + assert.equal( + published.credential.acceptsFullCookieHeader, + requirement.acceptsFullCookieHeader + ); + } +}); + +test("web-session contract preserves representative token and cookie semantics", () => { + const providers = new Map( + buildWebSessionContract().providers.map((provider) => [provider.providerId, provider]) + ); + + assert.equal(providers.get("deepseek-web")?.credential.kind, "token"); + assert.equal(providers.get("zai-web")?.credential.kind, "token"); + assert.equal(providers.get("gemini-web")?.credential.kind, "cookie"); + assert.equal(providers.get("qwen-web")?.credential.kind, "cookie"); + + assert.ok( + providers + .get("deepseek-web") + ?.tokenSources.some((source) => source.type === "localStorage" && source.key === "userToken") + ); + assert.ok( + providers + .get("gemini-web") + ?.tokenSources.some( + (source) => + source.type === "cookie" && source.name === "__Secure-1PSID" && source.domain === ".google.com" + ) + ); +}); + +test("web-session contract excludes credential values and operator-only guidance", () => { + const serialized = JSON.stringify(buildWebSessionContract()); + + for (const forbidden of [ + "placeholder", + "instructions", + "pollingConfig", + "credentialName", + "guideSteps", + "guideNote", + ]) { + assert.equal(serialized.includes(`\"${forbidden}\"`), false, `${forbidden} must not be published`); + } +}); + +test("web-session contract route remains management-authenticated", () => { + const source = readFileSync( + new URL("../../src/app/api/providers/web-session-contract/route.ts", import.meta.url), + "utf8" + ); + + const authCall = source.indexOf("requireManagementAuth(request)"); + const responseCall = source.indexOf("NextResponse.json(buildWebSessionContract())"); + + assert.ok(authCall >= 0, "route must require management authentication"); + assert.ok(responseCall > authCall, "authentication must run before contract publication"); +}); From 9e7096a04807800d24a0ed4c8d7c2a0da427b4b6 Mon Sep 17 00:00:00 2001 From: Aman <1402357+Zartharas@users.noreply.github.com> Date: Sun, 23 Aug 2026 21:55:33 -0600 Subject: [PATCH 4/6] refactor(providers): use canonical contract imports --- src/lib/providers/webSessionContract.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/providers/webSessionContract.ts b/src/lib/providers/webSessionContract.ts index 4824e9adc0..58ee726f5b 100644 --- a/src/lib/providers/webSessionContract.ts +++ b/src/lib/providers/webSessionContract.ts @@ -1,8 +1,8 @@ import { listExtractionConfigs, type TokenSource, -} from "../../../open-sse/services/tokenExtractionConfig.ts"; -import { getWebSessionCredentialRequirement } from "../../shared/providers/webSessionCredentials.ts"; +} from "@omniroute/open-sse/services/tokenExtractionConfig.ts"; +import { getWebSessionCredentialRequirement } from "@/shared/providers/webSessionCredentials"; export const WEB_SESSION_CONTRACT_VERSION = 1; From c5e694131a497266edea7b90bba81bd895dbb644 Mon Sep 17 00:00:00 2001 From: Zartharas <1402357+Zartharas@users.noreply.github.com> Date: Mon, 24 Aug 2026 00:07:33 -0500 Subject: [PATCH 5/6] style(test): format web-session contract regression --- tests/unit/web-session-contract.test.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tests/unit/web-session-contract.test.ts b/tests/unit/web-session-contract.test.ts index 8007d27c3b..7c43e441a4 100644 --- a/tests/unit/web-session-contract.test.ts +++ b/tests/unit/web-session-contract.test.ts @@ -19,10 +19,15 @@ test("web-session contract mirrors canonical extraction and credential metadata" }); assert.equal(contract.providers.length, expected.length); - assert.equal(new Set(contract.providers.map((provider) => provider.providerId)).size, expected.length); + assert.equal( + new Set(contract.providers.map((provider) => provider.providerId)).size, + expected.length + ); for (const { config, requirement } of expected) { - const published = contract.providers.find((provider) => provider.providerId === config.providerId); + const published = contract.providers.find( + (provider) => provider.providerId === config.providerId + ); assert.ok(published, `${config.providerId} must be published`); assert.equal(published.displayName, config.displayName); assert.equal(published.loginUrl, config.loginUrl); @@ -30,10 +35,7 @@ test("web-session contract mirrors canonical extraction and credential metadata" assert.deepEqual(published.tokenSources, config.tokenSources); assert.equal(published.credential.kind, requirement.kind); assert.deepEqual(published.credential.storageKeys, [...requirement.storageKeys]); - assert.equal( - published.credential.acceptsFullCookieHeader, - requirement.acceptsFullCookieHeader - ); + assert.equal(published.credential.acceptsFullCookieHeader, requirement.acceptsFullCookieHeader); } }); @@ -57,7 +59,9 @@ test("web-session contract preserves representative token and cookie semantics", .get("gemini-web") ?.tokenSources.some( (source) => - source.type === "cookie" && source.name === "__Secure-1PSID" && source.domain === ".google.com" + source.type === "cookie" && + source.name === "__Secure-1PSID" && + source.domain === ".google.com" ) ); }); @@ -73,7 +77,11 @@ test("web-session contract excludes credential values and operator-only guidance "guideSteps", "guideNote", ]) { - assert.equal(serialized.includes(`\"${forbidden}\"`), false, `${forbidden} must not be published`); + assert.equal( + serialized.includes(`\"${forbidden}\"`), + false, + `${forbidden} must not be published` + ); } }); From c89bdb21a2de7f71ad0307767978d32de757cf9f Mon Sep 17 00:00:00 2001 From: Zartharas <1402357+Zartharas@users.noreply.github.com> Date: Mon, 24 Aug 2026 00:07:51 -0500 Subject: [PATCH 6/6] chore(changelog): document web-session contract --- changelog.d/features/11340-web-session-contract.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/features/11340-web-session-contract.md diff --git a/changelog.d/features/11340-web-session-contract.md b/changelog.d/features/11340-web-session-contract.md new file mode 100644 index 0000000000..33ee58cf9b --- /dev/null +++ b/changelog.d/features/11340-web-session-contract.md @@ -0,0 +1 @@ +- **feat(providers):** publish a management-authenticated versioned web-session credential contract from OmniRoute's canonical browser credential metadata ([#11340](https://github.com/diegosouzapw/OmniRoute/pull/11340)) — thanks @Zartharas