chrome-extension-boilerplate-react-vite
chrome-extension-boilerplate-react-vite copied to clipboard
How to sync web app authentication with chrome extension
Hello everyone, I have been working on a project that involves users signing up on my main web app. The web app is created using Nextjs 14 with the new app router support and I used the next-auth to make the user sign in. Now I want to sync the authentication state between my web app and the Chrome extension, so for example, the user can authenticate on the main web app and when the user clicks on the Chrome extension on a separate tab, I want to make sure that the chrome extension shows based on the authenticated routes. It should also be able to make requests to the backend. How can I enable something like this, I tried finding tutorials online, but I have no help yet
Thank you :)
Thank you for your contribution. We will check and reply to you as soon as possible.
Here are a few ways to achieve this:
- Externally Connectable (I personally use this): Use the Externally Connectable feature of Chrome Extensions. By defining specific domains in your manifest file, your web app can communicate with the extension using message passing. When a user authenticates on your web app, you can send a message to the extension to update its state. This approach is secure and provides a real-time solution to keep the extension in sync.
- Content Script with LocalStorage Listener: Implement a content script that runs on your web app's domain. The script can listen for changes to localStorage or sessionStorage, where your authentication state is stored. When a change is detected, the content script can relay that information to the background script of the extension, thereby updating its state. This method relies on the authentication logic to save the auth state on localStorage.
- WebSockets for Real-Time Sync: Implement a WebSocket connection in your Chrome extension that listens to a server endpoint. Whenever the user's authentication state changes on the server side (like through your main web app), the server can push the new state to the extension. This method provides real-time synchronization and works well for applications requiring immediate state updates. Your background service worker might die, add a heartbeat subscription to keep the connection alive. Chrome Docs
Here are a few ways to achieve this:
- Externally Connectable (I personally use this): Use the Externally Connectable feature of Chrome Extensions. By defining specific domains in your manifest file, your web app can communicate with the extension using message passing. When a user authenticates on your web app, you can send a message to the extension to update its state. This approach is secure and provides a real-time solution to keep the extension in sync.
- Content Script with LocalStorage Listener: Implement a content script that runs on your web app's domain. The script can listen for changes to localStorage or sessionStorage, where your authentication state is stored. When a change is detected, the content script can relay that information to the background script of the extension, thereby updating its state. This method relies on the authentication logic to save the auth state on localStorage.
- WebSockets for Real-Time Sync: Implement a WebSocket connection in your Chrome extension that listens to a server endpoint. Whenever the user's authentication state changes on the server side (like through your main web app), the server can push the new state to the extension. This method provides real-time synchronization and works well for applications requiring immediate state updates. Your background service worker might die, add a heartbeat subscription to keep the connection alive. Chrome Docs
Hi, can you share your solution , cause I try to sync the state , I found it does not work , not matter one of the extension or react logout , they can't sync the logout stsate , thank you so much
Hi, can you share your solution , cause I try to sync the state , I found it does not work , not matter one of the extension or react logout , they can't sync the logout stsate , thank you so much
Externally connectable:
// manifest.js
externally_connectable: {
matches: ['*://*.yourdomain.com/*'],
accepts_tls_channel_id: true,
}
// service worker
chrome.runtime.onMessageExternal.addListener(async (message, sender, sendResponse) => {
if (message.type === 'logged-in') {
console.dir(message);
sendResponse({ message: 'OK' });
return true;
}
if (message.type === 'logged-out') {
console.dir(message);
sendResponse({ message: 'OK' });
return true;
}
});
// Web
const user = { id: 1234, name: 'John Doe', token: 'somejwtstuff' };
export async function sendLoggedInMessageToExtension() {
const res = await chrome.runtime.sendMessage(
'your-extension-id',
{ type: 'logged-in', data: user },
{ includeTlsChannelId: true }
);
console.log(res);
}
@raynirola Hello, Thank you so much for sharing the code, just a question, the web part, is that had to be the inside useEffect or it could be a server endpoint in nextjs? And can all this be done without having the web app opened on a tab?
Do we have to send messages everytime to our frontend web app in order to get data from the database etc.?
Once i see this video: https://www.youtube.com/watch?v=dXem_TaH9To&t=529s
Maybe it explain sth for you.
I think safe and easy way to do it, is grab a cookie from your main page, and on your chrome extension, send request with this cookie for backend and check if user was authenticated.
This is good also when e.g session was expiring and the cookie disappear and you can handle logging out on plugin in one time.
@PatrykKuniczak, Yes that's the approach I took, I basically saves a user with userid and session token in the database and that cookie on the frontend. Now that cookie would be received by the chrome extension on every request and it would be able to make request to the API routes :)
Thank you for answering
@zaidbren If you want to look how to do it, let's look here: https://github.com/PatrykKuniczak/YT_Notifier/tree/main/nest-app
If you have any other question, feel free to reopen this issue :)