feat(providers): add proactive cf_clearance/User-Agent hint to grok-web connection dialog (#7567) (#7713)

This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-19 14:37:02 -03:00
committed by GitHub
parent d1668a7c3c
commit e5842c75ec
5 changed files with 129 additions and 0 deletions

View File

@@ -0,0 +1 @@
- feat(providers): grok-web's add-connection dialog now shows a proactive hint explaining that grok.com's `cf_clearance` cookie is pinned to the IP, User-Agent, and TLS fingerprint of the browser that earned it — on a 403, paste the exact browser User-Agent into the existing Custom User-Agent field under Advanced Settings and reuse the same IP/proxy (#7567)

View File

@@ -4620,6 +4620,7 @@
"blackboxWebCookiePlaceholder": "Blackbox Web Cookie Placeholder",
"t3ChatWebCookieHint": "Open t3.chat → DevTools → Application → Local Storage → https://t3.chat, copy 'convex-session-id'. Then open DevTools → Network, copy the full Cookie header from any chat request. Paste both values in the fields below.",
"t3ChatWebCookiePlaceholder": "convex-session-id=abc123...",
"grokWebCookieHint": "grok.com's cf_clearance cookie is pinned to the IP, User-Agent, and TLS fingerprint of the browser where you copied it — pasting it from a different machine/IP causes a 403. Paste sso and sso-rw here, then open Advanced Settings and fill Custom User-Agent with the EXACT User-Agent string of that same browser, and use the same IP/proxy for this connection.",
"blockClaudeExtraUsageDescription": "Hide extra Claude usage rows reported by some providers when they duplicate primary token accounting.",
"blockClaudeExtraUsageLabel": "Block duplicate Claude usage rows",
"disableCoolingDescription": "Skip the transient cooldown so this connection stays eligible even after recoverable errors (terminal states like banned/expired still apply).",

View File

@@ -4618,6 +4618,7 @@
"blackboxWebCookiePlaceholder": "Cole o valor de __Secure-authjs.session-token",
"t3ChatWebCookieHint": "Abra t3.chat → DevTools → Aplicativo → Armazenamento local → https://t3.chat, copie 'convex-session-id'. Em seguida, abra DevTools → Rede, copie o cabeçalho completo do cookie de qualquer solicitação de chat. Cole os dois valores nos campos abaixo.",
"t3ChatWebCookiePlaceholder": "id-de-sessão-convexa=abc123...",
"grokWebCookieHint": "O cookie cf_clearance do grok.com é vinculado ao IP, ao User-Agent e à impressão digital TLS do navegador de onde você o copiou — colá-lo de outra máquina/IP causa um erro 403. Cole sso e sso-rw aqui e depois abra Configurações Avançadas e preencha o Custom User-Agent com o valor EXATO do User-Agent desse mesmo navegador, usando o mesmo IP/proxy para esta conexão.",
"blockClaudeExtraUsageDescription": "Quando habilitado, o OmniRoute marca esta conta do Claude Code como indisponível assim que a API de usage reporta `extra_usage.queued`, para que o fallback troque para outra conta antes de continuar com cobranças extras pay-as-you-go.",
"blockClaudeExtraUsageLabel": "Bloquear Claude Extra Usage",
"disableCoolingDescription": "__MISSING__:Skip the transient cooldown so this connection stays eligible even after recoverable errors (terminal states like banned/expired still apply).",

View File

@@ -45,6 +45,14 @@ export const WEB_SESSION_CREDENTIAL_REQUIREMENTS = {
placeholder: "sso=...; sso-rw=...",
acceptsFullCookieHeader: true,
storageKeys: ["cookie", "sso", "sso-rw"],
// #7567 — grok.com's cf_clearance cookie is pinned to IP + User-Agent + TLS
// fingerprint of the browser that earned it, so pasting it from a different
// machine/IP causes a 403 that is actually correct Cloudflare behavior. Point
// users at the Custom User-Agent field under Advanced Settings + same IP/proxy,
// instead of the generic (and here misleading) single-cookie hint.
hintKey: "grokWebCookieHint",
hintFallback:
"grok.com's cf_clearance cookie is pinned to the IP, User-Agent, and TLS fingerprint of the browser where you copied it — pasting it from a different machine/IP causes a 403. Paste sso and sso-rw here, then open Advanced Settings and fill Custom User-Agent with the EXACT User-Agent string of that same browser, and use the same IP/proxy for this connection.",
},
"gemini-web": {
kind: "cookie",

View File

@@ -0,0 +1,118 @@
import test from "node:test";
import assert from "node:assert/strict";
import type { ProviderMessageTranslator } from "../../src/app/(dashboard)/dashboard/providers/[id]/providerCredentialText.ts";
// #7567 — grok-web's add-connection dialog showed the generic, uninformative
// cookie hint ("Required cookie: sso + sso-rw. Paste the Cookie header
// value…") even though the real failure mode (403 from a pasted cf_clearance
// pinned to a different IP/User-Agent/TLS fingerprint) is only resolvable by
// also filling the Custom User-Agent field under Advanced Settings and reusing
// the same IP/proxy. A dedicated hintKey (grokWebCookieHint) now overrides the
// generic copy on the ADD flow, following the exact #5465 (t3.chat) pattern.
const { getWebSessionCredentialHint } = await import(
"../../src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts"
);
const { WEB_SESSION_CREDENTIAL_REQUIREMENTS } = await import(
"../../src/shared/providers/webSessionCredentials.ts"
);
const GROK_HINT =
"grok.com's cf_clearance cookie is pinned to the IP, User-Agent, and TLS fingerprint of the browser where you copied it — pasting it from a different machine/IP causes a 403. Paste sso and sso-rw here, then open Advanced Settings and fill Custom User-Agent with the EXACT User-Agent string of that same browser, and use the same IP/proxy for this connection.";
const GENERIC_COOKIE_HINT_TEMPLATE =
"Required cookie: {credential}. Paste the Cookie header value from your own signed-in {provider} web session. Do not include the Cookie: prefix.";
/** Minimal translator stub mimicking next-intl's `t` + `t.has`. */
function makeTranslator(messages: Record<string, string>): ProviderMessageTranslator {
const t = ((key: string, values?: Record<string, unknown>) => {
const raw = messages[key];
if (raw === undefined) return key;
return values
? Object.entries(values).reduce(
(acc, [name, value]) => acc.replaceAll(`{${name}}`, String(value)),
raw
)
: raw;
}) as ProviderMessageTranslator;
t.has = (key: string) => key in messages;
return t;
}
test("grok-web requirement carries the grokWebCookieHint override (#7567)", () => {
const req = WEB_SESSION_CREDENTIAL_REQUIREMENTS["grok-web"] as { hintKey?: string };
assert.equal(req.hintKey, "grokWebCookieHint");
});
test("grok-web add-connection hint explains cf_clearance IP/UA/TLS pinning and the Custom User-Agent fix (#7567)", () => {
const t = makeTranslator({
grokWebCookieHint: GROK_HINT,
webCookieCredentialHint: GENERIC_COOKIE_HINT_TEMPLATE,
});
const hint = getWebSessionCredentialHint(
t,
WEB_SESSION_CREDENTIAL_REQUIREMENTS["grok-web"],
"Grok",
false
);
assert.equal(hint, GROK_HINT);
assert.ok(hint && hint.includes("User-Agent"), "must mention User-Agent");
assert.ok(hint && hint.includes("IP"), "must mention IP pinning");
assert.ok(hint && hint.includes("cf_clearance"), "must mention cf_clearance");
});
test("grok-web add-connection hint does not fall back to the generic circular cookie hint (#7567)", () => {
const t = makeTranslator({
grokWebCookieHint: GROK_HINT,
webCookieCredentialHint: GENERIC_COOKIE_HINT_TEMPLATE,
});
const hint = getWebSessionCredentialHint(
t,
WEB_SESSION_CREDENTIAL_REQUIREMENTS["grok-web"],
"Grok",
false
);
assert.ok(
hint && !hint.includes("Required cookie: sso + sso-rw"),
"must not fall back to the generic single-cookie template"
);
});
test("grok-web edit-connection flow is unaffected by the new hintKey (#7567 regression guard)", () => {
const t = makeTranslator({
grokWebCookieHint: GROK_HINT,
webCookieEditHint: "Leave blank to keep the current session cookie. Required cookie: {credential}.",
});
const hint = getWebSessionCredentialHint(
t,
WEB_SESSION_CREDENTIAL_REQUIREMENTS["grok-web"],
"Grok",
true
);
assert.ok(
hint && hint.startsWith("Leave blank to keep the current session cookie."),
"editing=true must keep the generic edit-flow copy, not the new hintKey"
);
assert.ok(hint && !hint.includes("cf_clearance"), "edit flow must not surface the new add-flow hint");
});
test("grok-web hintFallback is used verbatim when the translation key is missing (#7567)", () => {
const t = makeTranslator({
// grokWebCookieHint intentionally absent — t.has() must return false.
webCookieCredentialHint: GENERIC_COOKIE_HINT_TEMPLATE,
});
const requirement = WEB_SESSION_CREDENTIAL_REQUIREMENTS["grok-web"] as {
hintFallback?: string;
};
assert.ok(requirement.hintFallback, "grok-web must define a hintFallback");
const hint = getWebSessionCredentialHint(t, WEB_SESSION_CREDENTIAL_REQUIREMENTS["grok-web"], "Grok", false);
assert.equal(hint, requirement.hintFallback);
});