You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SPA与Chrome扩展间共享认证会话(无需Cookie)

无Cookie实现SPA到Chrome扩展的无缝认证

问题背景

我有一个React单页应用(SPA)和Chrome扩展,目前认证功能仅在SPA中实现。希望无需用户手动登录扩展,就能将认证功能无缝扩展至Chrome扩展,且不使用Cookie。请问是否有可行方案?是否需要通过消息将Bearer token发送至扩展?

现有代码问题分析

你提供的React组件代码中出现chrome is not defined错误,原因是普通网页(SPA)默认无法直接访问Chrome扩展API,只有扩展的内部脚本(后台脚本、内容脚本)或配置了externally_connectable的可信网页才能调用这些API。

现有React Callback组件代码

function Callback() {
  const { accessToken, setAccessToken } = useAuth();

  const navigate = useNavigate();
  const [searchParams, _] = useSearchParams();

  const sendTokenToExtension = (access_token: string) => {
    const extensionId = "";
    // chrome.runtime.connect(extensionId, { name: "example" });
    chrome.runtime.sendMessage(extensionId, access_token); <----- Chrome未定义错误
  };

  useEffect(() => {
    (async () => {
      try {
        const response = await axios.get(
          `http://127.0.0.1:8000/auth/github/callback`,
          {
            params: {
              state: searchParams.get("state"),
              code: searchParams.get("code"),
              scope: searchParams.get("scope"),
              prompt: "consent",
            },
          }
        );

        if (response.status === 200) {
          setAccessToken(response.data.access_token);
          sendTokenToExtension(response.data.access_token);
          navigate("/dashboard");
        }
      } catch (error) {
        console.error("Error:", error);
      }
    })();
  }, []);

  return <>Redirecting now..</>;
}

export default Callback;

现有扩展background.ts代码

console.log("Starting auth-listener..")

chrome.runtime.onMessageExternal.addListener(
  (request, sender, sendResponse) => {
    console.log("Received message from " + sender + ": ", request)
    sendResponse({ received: true }) //respond however you like
  }
)

可行实现方案

方案1:配置externally_connectable让SPA直接调用Chrome API

这是最直接的方式,通过扩展配置允许指定域名的网页调用Chrome API:

  1. 修改扩展manifest.json,添加externally_connectable字段,指定SPA的可信域名:
{
  "manifest_version": 3,
  // 其他配置...
  "externally_connectable": {
    "matches": ["https://your-spa-domain.com/*"] // 替换为你的SPA实际域名
  }
}
  1. 修正React组件的token发送逻辑,先检测Chrome API是否可用:
const sendTokenToExtension = (access_token: string) => {
  const extensionId = "你的扩展ID"; // 替换为实际扩展ID(可在扩展管理页面查看)
  // 先验证Chrome API是否存在
  if (typeof chrome !== "undefined" && chrome.runtime) {
    chrome.runtime.sendMessage(
      extensionId,
      { type: "SET_AUTH_TOKEN", token: access_token },
      (response) => {
        if (chrome.runtime.lastError) {
          console.error("Token发送失败:", chrome.runtime.lastError);
        } else {
          console.log("扩展已接收Token:", response);
        }
      }
    );
  } else {
    console.error("Chrome扩展API不可用");
  }
};
  1. 完善扩展后台的消息监听,验证发送者并安全存储Token:
console.log("Starting auth-listener..")

chrome.runtime.onMessageExternal.addListener(
  (request, sender, sendResponse) => {
    // 严格验证发送者域名,防止恶意网站冒充
    if (!sender.url || !sender.url.startsWith("https://your-spa-domain.com/")) {
      sendResponse({ success: false, reason: "未授权的发送者" });
      return;
    }

    if (request.type === "SET_AUTH_TOKEN" && request.token) {
      // 将Token存入扩展的安全存储chrome.storage.local
      chrome.storage.local.set({ accessToken: request.token }, () => {
        console.log("Token已存入扩展存储");
        sendResponse({ success: true });
      });
      // 返回true表示异步处理消息
      return true;
    }

    sendResponse({ success: false, reason: "无效的请求" });
  }
)

方案2:通过内容脚本中转通信(更安全兼容)

如果不想让SPA直接依赖Chrome API,可以用扩展注入内容脚本到SPA页面,作为SPA和扩展后台的中转:

  1. 扩展manifest.json配置内容脚本:
{
  "manifest_version": 3,
  // 其他配置...
  "content_scripts": [
    {
      "matches": ["https://your-spa-domain.com/*"],
      "js": ["content-script.js"]
    }
  ]
}
  1. 编写content-script.js,监听SPA的postMessage并转发到扩展后台:
// 监听来自SPA的消息
window.addEventListener("message", (event) => {
  // 验证消息来源域名
  if (event.origin !== "https://your-spa-domain.com") return;

  if (event.data.type === "SEND_AUTH_TOKEN") {
    // 将Token转发到扩展后台
    chrome.runtime.sendMessage(
      { type: "SET_AUTH_TOKEN", token: event.data.token },
      (response) => {
        // 通知SPA处理结果
        event.source.postMessage(
          { type: "TOKEN_SENT_RESULT", success: response.success },
          event.origin
        );
      }
    );
  }
});
  1. 修改React组件的发送逻辑,用postMessage和内容脚本通信:
const sendTokenToExtension = (access_token: string) => {
  window.postMessage(
    { type: "SEND_AUTH_TOKEN", token: access_token },
    "https://your-spa-domain.com" // 替换为SPA域名
  );
};

安全风险与优化建议

直接发送Token的风险

  • 伪造请求风险:如果扩展未验证发送者域名,恶意网站可能冒充SPA发送伪造Token。
  • XSS窃取风险:如果SPA存在XSS漏洞,Token可能被窃取并发送给恶意扩展或网站。

更优方案:使用授权码换Token(降低风险)

不要直接发送Token,而是让SPA生成短期有效、单次使用的授权码,扩展拿到授权码后去后端换取Token:

  1. SPA在认证成功后,向后端请求一个授权码(有效期5-10分钟,仅限使用一次)。
  2. SPA将授权码发送给扩展(通过上述两种通信方式)。
  3. 扩展用授权码调用后端接口换取正式Token。
  4. 后端验证授权码有效性,返回Token并标记授权码已使用。

这种方式即使授权码泄露,危害范围和时间都有限,比直接发送Token更安全。

其他优化点

  • 扩展存储Token时,优先使用chrome.storage.local(比普通localStorage更安全,仅扩展可访问)。
  • SPA端做好XSS防护:避免使用innerHTML、对用户输入做转义、使用Content Security Policy(CSP)。
  • 如果是OAuth2认证,可以考虑使用Chrome的chrome.identity API,让扩展直接发起授权,与SPA共享用户会话(需要后端支持多客户端认证)。

内容的提问来源于stack exchange,提问作者sharjeel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 17:42:12