fix: intercept Grok sign-in routes in iframe

This commit is contained in:
2026-09-14 17:59:00 +08:00
parent 19854a03b2
commit 067ad77b28
5 changed files with 41 additions and 6 deletions
+3 -2
View File
@@ -31,8 +31,9 @@ not inject provider HTML or replace the native iframe view.
Open `chrome://extensions`, enable Developer mode, choose **Load unpacked**, and select this directory.
Grok authentication is intentionally completed in a top-level browser tab because
`accounts.x.ai` does not permit login inside an iframe. Use **登录 ↗** in the Grok
panel, finish signing in, return to the workspace, and reload the Grok panel.
`accounts.x.ai` does not permit login inside an iframe. Grok `/sign-in` links are
redirected to a top-level tab automatically; alternatively use **登录 ↗** in the
Grok panel. Finish signing in, return to the workspace, and reload the Grok panel.
## Security boundary
@@ -1,8 +1,22 @@
(() => {
const { createProviderAdapter } = globalThis.AIParallelProviderCore;
createProviderAdapter({
function isExternalAuthUrl(value) {
let targetUrl;
try {
targetUrl = new URL(value, "https://grok.com/");
} catch {
return false;
}
if (targetUrl.hostname === "accounts.x.ai") return true;
return ["grok.com", "www.grok.com"].includes(targetUrl.hostname)
&& /^\/(?:sign-in|login)(?:\/|$)/.test(targetUrl.pathname);
}
const adapter = createProviderAdapter({
id: "grok",
hosts: ["grok.com"],
isExternalAuthUrl,
editorSelectors: [
"textarea[aria-label='Ask Grok anything']",
"textarea[placeholder*='Ask']",
@@ -33,6 +47,7 @@
});
if (typeof document === "undefined" || typeof chrome === "undefined") return;
if (window.top === window) return;
document.addEventListener("click", (event) => {
const link = event.target?.closest?.("a[href]");
@@ -44,10 +59,15 @@
} catch {
return;
}
if (targetUrl.hostname !== "accounts.x.ai") return;
if (!adapter.isExternalAuthUrl(targetUrl.href)) return;
event.preventDefault();
event.stopImmediatePropagation();
window.parent.postMessage({
context: "ai-parallel-workspace",
providerId: "grok",
type: "AI_PARALLEL_AUTH_REQUIRED"
}, new URL(chrome.runtime.getURL("/")).origin);
chrome.runtime.sendMessage({
type: "OPEN_PROVIDER_AUTH",
providerId: "grok",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "AI Parallel",
"version": "2.1.2",
"version": "2.1.3",
"description": "Compare multiple AI web chats side by side in one live workspace.",
"minimum_chrome_version": "120",
"permissions": [
@@ -221,6 +221,11 @@ window.addEventListener("message", (event) => {
return;
}
if (event.data.type === "AI_PARALLEL_AUTH_REQUIRED") {
setPanelState(providerId, "已在新标签页打开登录;完成后点击 ↻", { ready: false });
return;
}
if (event.data.type === "AI_PARALLEL_SEND_RESULT") {
const requestId = String(event.data.requestId || "");
const pending = pendingRequests.get(requestId);
+10 -1
View File
@@ -7,7 +7,7 @@ const vm = require("node:vm");
const extensionRoot = path.join(__dirname, "..", "apps", "browser-extension");
function loadAdapters() {
const context = { console, setTimeout, clearTimeout };
const context = { console, setTimeout, clearTimeout, URL };
context.globalThis = context;
vm.createContext(context);
for (const file of [
@@ -40,3 +40,12 @@ test("all supported providers expose the adapter contract", () => {
assert.equal(typeof adapters[id].newChat, "function");
}
});
test("Grok recognizes iframe-only authentication routes", () => {
const grok = loadAdapters().grok;
assert.equal(grok.isExternalAuthUrl("https://accounts.x.ai/check-login?redirect=grok-com"), true);
assert.equal(grok.isExternalAuthUrl("https://grok.com/sign-in?return_to=%2F"), true);
assert.equal(grok.isExternalAuthUrl("/login"), true);
assert.equal(grok.isExternalAuthUrl("https://grok.com/"), false);
assert.equal(grok.isExternalAuthUrl("https://example.com/sign-in"), false);
});