我一直在从事一个涉及用户在我的主网络应用程序上注册的项目。该 Web 应用程序是使用 Nextjs 14 和新的应用程序路由器支持创建的,我使用了 next-auth (将用户会话保存在 cookie 中)来让用户登录。现在我想在我的 Web 应用程序和Chrome 扩展程序,例如,用户可以在主 Web 应用程序上进行身份验证,当用户在单独的选项卡上单击 Chrome 扩展程序时,我想确保 Chrome 扩展程序根据经过身份验证的路由显示。如果注册的话,它还应该能够向受保护的 api 路由发出请求。 我从后台开发工具检查了流行的 chrome 扩展的网络选项卡,它们似乎调用其 api 端点来检查身份验证状态,即它们不会将令牌保存在 cookie 或本地存储中来检查身份验证状态。 我怎样才能启用这样的功能,我尝试在网上查找教程,但还没有帮助
谢谢你:)
要通过扩展实现登录,您需要将一些东西放在一起:
background worker
将监视浏览器 cookie 的变化listener
在扩展端,它将监听后台工作人员广播的事件。每个 Chrome 扩展程序将有一个实例,这意味着每个打开的选项卡都有一个实例。background worker
每个运行的浏览器实例都有一个实例,这意味着只有一个事件源。它将在您的浏览器启动时启动,并在您的浏览器关闭时关闭(崩溃或手动停止除外)。
background.js
REACT_APP_NEXTAUTH_SECRET
与前端中使用的相同。遗憾的是,这是一种安全隐患,但我从未找到更好的方法来解码 cookie 以获取 JWT。如果您找到一种按原样使用 cookie 的方法,那就更好了。
REACT_APP_COOKIE_DOMAIN
和 REACT_APP_COOKIE_NAME
是您的应用程序所独有的,最好的查找方法是通过 console.log
cookie 更改并查看其值。当您登录网站时,您应该输入更改 cookie 的值。
// Notify each extension instance of the cookie change
function broadcastMessageToAllTabs(message: object) {
chrome.tabs.query({ status: "complete" }, (tabs) => {
tabs.forEach((tab) => {
if (tab.id) {
chrome.tabs
.sendMessage(tab.id, message)
.catch((e) =>
console.error(
`Could not send message to the tab [${tab.id}/${tab.title}]`,
e,
),
);
}
});
});
}
// Decodes the token contained within the cookie
async function setToken(token: string | null) {
const decoded = token
? ((await decode({
token,
secret: process.env.REACT_APP_NEXTAUTH_SECRET as string,
})) as { user: { accessToken: string } })
: undefined;
broadcastMessageToAllTabs({ token: decoded?.user?.accessToken || null });
}
// Adds a listener for cookies changing in the browser, e.g. after login or logout
chrome.cookies.onChanged.addListener(async (reason) => {
if (
reason.cookie.domain === process.env.REACT_APP_COOKIE_DOMAIN &&
reason.cookie.name === process.env.REACT_APP_COOKIE_NAME
) {
// On overwrite, removed is also true when it is actually updating the cookie, so we avoid to remove it for flickering
await setToken(
reason.removed && reason.cause !== "overwrite"
? null
: reason.cookie.value,
);
}
});
my-plugin.ts
在扩展代码中的任何位置,您都应该为后台工作人员广播的更改添加侦听器。
const handleMessage = (message: any) => {
if ("token" in message) {
// Got the token from the worker, save it somewhere so you can use it
}
};
chrome.runtime.onMessage.addListener(handleMessage);
// Ideally somewhere if you are releasing resources it is good to unsubscribe within your extension to avoid memory leaks and issues
// chrome.runtime.onMessage.removeListener(handleMessage);
这部分应该可以帮助您在登录和退出网站时开始。现在还有一种情况没有被涵盖,那就是如果您已经登录并打开一个新选项卡。您的扩展程序将不会收到任何登录事件,因为您已经收到了,这将要求您每次打开选项卡时重新登录。
为了解决这个问题,我们应该实现从扩展到后台工作者的通信。在
background.ts
中,我们添加
// Listens to messages broadcasted, and eventually answers with the current cookie if any
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
(async function () {
if (request.type === "GET_COOKIE") {
const cookie = await chrome.cookies.getAll({
domain: process.env.REACT_APP_COOKIE_DOMAIN,
name: process.env.REACT_APP_COOKIE_NAME,
});
if (cookie.length) {
const decoded = cookie[0].value
? ((await decode({
token: cookie[0].value,
secret: process.env.REACT_APP_NEXTAUTH_SECRET as string,
})) as { user: { accessToken: string } })
: undefined;
sendResponse({ token: decoded?.user?.accessToken || null });
return;
}
}
sendResponse({ token: null });
})();
return true;
});
并且在
my-plugin.ts
内,应在扩展实例启动时随时调用
chrome.runtime.sendMessage({ type: "GET_COOKIE" }, (response) => {
if (response) {
// Got a response from the worker, eventually sending us the token
} else {
console.error("Got an empty response for GET_COOKIE");
}
});
专业提示:为了帮助您调试和了解正在发生的情况,您还可以通过转到 Chrome 内部的设置/扩展来打开后台工作人员的控制台,然后打开您自己的扩展(这里是使用 React Dev Tools 的任意示例) ):