mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-18 21:02:50 +03:00
Merged as part of the owner batch of 2026-09-11. This PR had a live worktree in another session, so it sat outside the main 39. Merged on your explicit call, validated first rather than taken on trust: boarded with the other 10 worktree-held PRs into a consolidated worktree off `release/v3.8.51`. - ESLint over every changed file: no errors - `typecheck:core` clean; `check:dashboard-typecheck` OK; `check:changelog-integrity` OK - complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437 - 203 of 208 assertions green. The 5 remaining (`guide-settings-route` ×4, `hard-session-lease-bypass-inventory` ×1) reproduce on the pure tip with nothing from this batch applied. - `imageGeneration.ts` rebaselined 3259 → 3293 for #12945's image-only-model guard, landed separately in #13392 so nothing was pushed onto a live branch. ⚠️ base-red inherited: #12732 — provider count 356 vs 358 and `open-sse/utils/stream.ts` 3115 > frozen 3098, both reproducing on the pure tip.
43 lines
1.8 KiB
TypeScript
43 lines
1.8 KiB
TypeScript
/**
|
|
* Dashboard session token — the ONE verifier for the `auth_token` cookie.
|
|
*
|
|
* A dashboard session is a JWT that verifies against JWT_SECRET AND carries
|
|
* `authenticated: true` — the claim every session minter emits
|
|
* (`api/auth/login`, `api/auth/oidc/callback`, the authz pipeline refresh).
|
|
* Other tokens signed with the same secret exist (the Cursor CLI passthrough
|
|
* mints `iss "omniroute" / aud "cursor-cli"` tokens for any key holder) and
|
|
* MUST NOT verify as a session: before #13298 any such token forged the
|
|
* cookie and reached instance-wide operations. Every place that trusts the
|
|
* cookie goes through `verifyDashboardSessionToken`; a bare `jwtVerify` on
|
|
* `auth_token` is a regression (guarded by
|
|
* tests/unit/dashboard-session-verifier-source-guard.test.ts).
|
|
*/
|
|
import { jwtVerify, type JWTPayload } from "jose";
|
|
|
|
export const DASHBOARD_SESSION_COOKIE = "auth_token";
|
|
export const DASHBOARD_SESSION_CLAIM = "authenticated";
|
|
|
|
export function getDashboardJwtSecret(): Uint8Array | null {
|
|
const secret = process.env.JWT_SECRET?.trim();
|
|
return secret ? new TextEncoder().encode(secret) : null;
|
|
}
|
|
|
|
/**
|
|
* Returns the verified payload when `token` is a dashboard session, else null.
|
|
* Never throws: a malformed, expired, foreign-secret or claim-less token is
|
|
* simply "not a session". Pass `secret` explicitly when the caller already
|
|
* resolved it (the authz pipeline does); `null` means "no secret → no session".
|
|
*/
|
|
export async function verifyDashboardSessionToken(
|
|
token: string | null | undefined,
|
|
secret: Uint8Array | null = getDashboardJwtSecret()
|
|
): Promise<JWTPayload | null> {
|
|
if (!token || typeof token !== "string" || !secret) return null;
|
|
try {
|
|
const { payload } = await jwtVerify(token, secret);
|
|
return payload[DASHBOARD_SESSION_CLAIM] === true ? payload : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|