I have a bookmarking firefox/chrome WebExtension (using the standard popup
, content
and background
scripts). My api servers have a /login route that returns a JSON Web Token and the web app stores that in its local storage. I have full control over the extension, api, and web apps.
The barefoot way to do this is to have users log in through the popup and save the auth token in the background.js to the WebExtension's local storage. But I would really like to only ask the user to authenticate on my site and have that same auth apply to the extension as well.
Is there a way to share the auth token? I see Pocket and a lot of others doing this, but I'm not sure how.
Most guidelines, while advising against storing access tokens in the session or local storage, recommend the use of session cookies. However, we can use session cookies only with the domain that sets the cookie. Another popular suggestion is to store access tokens in the browser's memory.
When the client logs in to the server, server gives token and saves it to the client localStorage (or sessionStorage ). Whenever the client calls an api which can be accessed only with the token, client retrieves the token back from the localStorage , and send that token with the authorization header ( req. headers.
You can also use the shortcut Ctrl+Shift+M. Right-click the value on the right side, select edit value, then copy it. That's it! You now have your Discord token.
You could store the token as a cookie. Cookies work like localStorage
, but with the extra that the're also by default wrapped into every HTTP request to the server. And here comes the trick. Chrome Extension can gain access to HTTP requests, with the use of webRequest
API. Thus it can take a peek into the request headers and know your cookies. Having that web token as a cookie makes it available to the extension.
But still you need to wait for your website to be opened by the user, to have HTTP requests flowing and ready to be peeked at, right? Not really. You can make make an ajax request directly from the extension.
Here's to illustrate how the whole thing would work:
manifest:
"permissions": [
"webRequest",
"webRequestBlocking",
"*://*.my_site.com/*"
]
Background page:
function callback (details) {
//
token = func_extract_token_from_headers(details.requestHeaders);
chrome.webRequest.onBeforeSendHeaders.removeListener(callback);
return {cancel: false} // set to true to prevent the request
// from reaching the server
}
chrome.webRequest.onBeforeSendHeaders.addListener (callback,
{urls: ["http://www.my_site.com/*", "https://www.my_site.com/*"]},
["blocking", "requestHeaders"]);
var xurl = "https://www.my_site.com/";
var xhr = new XMLHttpRequest();
xhr.open("GET", xurl, true);
xhr.send();
I should mention that there's a cleaner way to do it, but it currently does not work due to CSP--Content Secutiry Policiy. Opening the website in an iframe inside the background page, as mentioned by wOxxOm in comments, should work, with added CSP whitelisting of the website. This approach would also avoid prompting the user for credentials, and would be cleaner. Unfortunately it's not currently working
EDIT:
Sorry, I was wrong about my last claim: that iframes opening external pages are blocked in background pages. To display it in the background page(or popup for that matter), simply whitelist for CSP -- shown below.
Apart from the business of opening the page in an iframe, you need to communicate with it. That should be done using the window.postMessage API
Here's to exemplify how it all should come together:
Manifest:
// Whitelist your website
"content_security_policy": "script-src 'self' https://my_site.com/; object-src 'self'"
// Have the background declared as html
"background": { "page": "background.html"}
Background:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
if(event.origin == "https://my_site.com"); // you may want to check the
// origin to be your site
chrome.storage.storage.local.set({'auth_token': event.data}); // the token
}
iframe = document.createElement('iframe');
iframe.src = "https://my_site.com/";
// Have a div ready to place iframe in
document.getElementById('div').appendChild(iframe);
iframe.contentWindow.postMessage("give_token", "https://my_site.com")
The web page:
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
if(event.origin == "your_extension_id_aeiou12345");
event.source.postMessage(''+localStorage.auth_token, event.origin);
}
EDIT:
Also, to have the website display in an iframe, make sure the X-frame-options
response header is not set to a blocking value. You can either remove it, or set it to a non-blocking value, or white-list the url of the extension.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With