mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 06:42:12 +03:00
An already-expired grok-cli token (real expires_at/exp in the past) produced a negative expiresIn, which is truthy in the import-token route and maps to a PAST expiresAt — AutoCombo then reads that as 'already expired' and excludes the connection instead of refreshing it. Clamp with Math.max(1, expiresIn) so an expired token is treated as due-for-refresh. Extends #5775 (thanks @Chewji9875). Regression: 2 new cases in tests/unit/grok-cli-oauth.test.ts (expired JWT exp + expired JSON expires_at), both failing-then-passing.
This commit is contained in:
committed by
GitHub
parent
057ca116db
commit
7d07be9b20
@@ -34,7 +34,7 @@
|
||||
|
||||
- **settings (model aliases — self-heal after restart):** the Settings → Routing page showed "No exact-match aliases configured" after a server restart even though the aliases were persisted in the DB. Aliases are held in a module-local `_customAliases` map in `modelDeprecation.ts` that the boot path hydrates, but Next.js compiles the app-route module graph separately from the startup graph (the same webpack chunk-splitting class as #5312), so the `GET /api/settings/model-aliases` handler read a different, un-hydrated copy. The handler now self-heals: when its in-memory alias map is empty it reads `settings.modelAliases` from the DB (via the existing `getSettings()` db module — no raw SQL in the route) and repopulates the map, so the UI reflects the persisted aliases on the first GET after a restart. Regression guard: `tests/unit/model-aliases-settings-route-selfheal.test.ts`. ([#5777](https://github.com/diegosouzapw/OmniRoute/pull/5777) — thanks [@jleonar2](https://github.com/jleonar2))
|
||||
|
||||
- **providers (grok-cli token auto-refresh):** grok-cli OAuth tokens were never proactively refreshed before their real expiry. `mapTokens` hardcoded `expiresIn: 21600` (6 h) regardless of the token's actual lifetime, so the persisted `expiresAt` was always "now + 6 h" and the proactive `tokenHealthCheck` sweep (refresh when `expiresAt - now < 5 min`) fired 6 h after import instead of shortly before the token really expired. `mapTokens` now computes `expiresIn` from the authoritative `expires_at` field in `~/.grok/auth.json` (ISO → epoch-seconds) with a fallback to the JWT `exp` claim (payload-only decode, no signature trust); the hardcoded `21600` is kept only when neither is present. Regression guards: 3 new cases in `tests/unit/grok-cli-oauth.test.ts` (JWT `exp`, JSON `expires_at`, and the fallback). ([#5775](https://github.com/diegosouzapw/OmniRoute/pull/5775) — thanks [@Chewji9875](https://github.com/Chewji9875))
|
||||
- **providers (grok-cli token auto-refresh):** grok-cli OAuth tokens were never proactively refreshed before their real expiry. `mapTokens` hardcoded `expiresIn: 21600` (6 h) regardless of the token's actual lifetime, so the persisted `expiresAt` was always "now + 6 h" and the proactive `tokenHealthCheck` sweep (refresh when `expiresAt - now < 5 min`) fired 6 h after import instead of shortly before the token really expired. `mapTokens` now computes `expiresIn` from the authoritative `expires_at` field in `~/.grok/auth.json` (ISO → epoch-seconds) with a fallback to the JWT `exp` claim (payload-only decode, no signature trust); the hardcoded `21600` is kept only when neither is present. An already-expired token (real `expires_at`/`exp` in the past) is now clamped to a positive `expiresIn` via `Math.max(1, …)`, so the import route stores a near-future `expiresAt` and AutoCombo refreshes the connection instead of reading a past date and excluding it outright. Regression guards: 5 cases in `tests/unit/grok-cli-oauth.test.ts` (JWT `exp`, JSON `expires_at`, the `21600` fallback, and the two expired-token clamps). ([#5775](https://github.com/diegosouzapw/OmniRoute/pull/5775) — thanks [@Chewji9875](https://github.com/Chewji9875))
|
||||
|
||||
- **compression (CCR retrieve via MCP HTTP):** the `omniroute_ccr_retrieve` MCP tool returned `"CCR block not found"` for blocks stored earlier in the **same** session when called over the MCP HTTP transports (SSE / Streamable HTTP), e.g. from OpenCode in a Docker deployment. Compression stores each block keyed by the API-key principal (`String(apiKeyInfo.id)`), but the tool resolved the caller via `extra.authInfo.clientId` — which the MCP SDK never populates for API-key auth — so it fell back to `"anonymous"` and the compound store-key never matched. The retrieve tool now resolves the caller's API-key id from the MCP HTTP auth context (`httpAuthContext`) using the **same** `getApiKeyMetadata` lookup used at storage time, so retrieval matches storage. Cross-tenant IDOR isolation is preserved: a different key resolves to a different id → miss; no key → the anonymous bucket only. Regression guard: `tests/unit/compression/ccr-mcp-principal-5649.test.ts` (extraction, distinct-principal isolation, fail-closed, end-to-end store→retrieve). ([#5649](https://github.com/diegosouzapw/OmniRoute/issues/5649))
|
||||
|
||||
|
||||
@@ -131,6 +131,13 @@ export const grokCli = {
|
||||
expiresIn = exp - currentSec;
|
||||
}
|
||||
|
||||
// #5775 follow-up: guard against an already-expired token yielding a negative
|
||||
// expiresIn. A negative value is truthy downstream (import-token route) and maps
|
||||
// to a PAST expiresAt, which AutoCombo reads as "already expired" and excludes the
|
||||
// connection instead of refreshing it. Clamp to a tiny positive TTL so the token is
|
||||
// treated as due-for-refresh.
|
||||
expiresIn = Math.max(1, expiresIn);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
|
||||
@@ -152,3 +152,32 @@ test("Grok Build OAuth Provider - mapTokens falls back to 21600 if no exp or exp
|
||||
|
||||
assert.equal(result.expiresIn, 21600);
|
||||
});
|
||||
|
||||
// #5775 follow-up: an already-expired token must NOT produce a negative expiresIn.
|
||||
// A negative value is truthy in the import-token route (route.ts), yielding a PAST
|
||||
// expiresAt that AutoCombo (virtualFactory.ts) reads as "already expired" and excludes
|
||||
// the connection immediately — instead of clamping to a tiny positive TTL so the token
|
||||
// is treated as due-for-refresh. Clamp with Math.max(1, …).
|
||||
test("Grok Build OAuth Provider - mapTokens clamps expired JWT exp to a positive expiresIn", () => {
|
||||
const pastSec = Math.floor(Date.now() / 1000) - 3600; // expired 1h ago
|
||||
const payload = { sub: "12345", email: "test@example.com", exp: pastSec };
|
||||
const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
||||
const mockJwt = `eyJhbGciOiJFUzI1NiJ9.${payloadBase64}.signature`;
|
||||
const result = grokCli.mapTokens(mockJwt, null);
|
||||
|
||||
assert.ok(result.expiresIn >= 1, `expected expiresIn >= 1, got ${result.expiresIn}`);
|
||||
});
|
||||
|
||||
test("Grok Build OAuth Provider - mapTokens clamps expired JSON expires_at to a positive expiresIn", () => {
|
||||
const pastDateStr = new Date(Date.now() - 3600 * 1000).toISOString(); // expired 1h ago
|
||||
const authJson = {
|
||||
"https://auth.x.ai::clientId": {
|
||||
key: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZXhhbXBsZS5jb20ifQ.signature",
|
||||
refresh_token: "test-refresh-token",
|
||||
expires_at: pastDateStr,
|
||||
},
|
||||
};
|
||||
const result = grokCli.mapTokens(authJson, null);
|
||||
|
||||
assert.ok(result.expiresIn >= 1, `expected expiresIn >= 1, got ${result.expiresIn}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user