When developing a Chrome extension, you might need to get an XMLHttpRequest that’s part of a content script to send cookies for a domain when making a request to that domain, if the origin is not that domain. Not much has been written about how to do this.
Dana Woodman, a Chrome extension developer discusses how to do this, but she makes a mistake, claiming that you need to designate the “cookies” permission in your manifest.json. This is not accurate. You can designate the “cookies” permission in manifest.json, but you only need to do that if you want to access cookie data separately from an XmlHttpRequest. Additionally, she makes a mistake that 99% of Chrome extension developers make, assuming that you have to put your domain in the “permissions” field in order to make cross-origin web requests to it.
In this article, I’ll break down exactly what you need to do to pass along cookies to cross-origin XmlHttpRequests in a Chrome extension.
Why would anyone ever want to do this to begin with?
So that your web server endpoint for your Chrome extension can authenticate a user. If part of your Chrome extension setup is to let the user authenticate via a webpage, then you probably set a cookie or a session ID for that authenticated user. If your Chrome extension then makes XHR requests to your web server as part of its functionality, you’ll want to pass cookies along so that you know what user you’re dealing with.
First, let’s clarify the issue of placing “hosts” in the “permissions” field:
Most Chrome extension developers assume that if their website is www.mydomain.com, and their Chrome extension makes XHR requests to www.mydomain.com, then you must put www.mydomain.com in the permissions field of your manifest file. This is simply not true.
I can understand why developers are confused about this though; Google’s documentation on using hosts in the permissions section of manifest.json is poor. The document doesn’t even mention the reason for listing a host, other than to “give access to one or more hosts”. But what does “give access” mean? Furthermore, while the page on match patterns offers a hint at what the purpose of hosts in the permissions field are, clicking on the “host permissions” definition on this page takes you back to the original permissions page with this URL (https://developer.chrome.com/apps/declare_permissions#host-permissions) and unfortunately there is no content tagged with #host-permissions on that page. This is probably an error on Google’s part. SO, we are left to figure out what the purpose of “hosts” in the permissions field ON OUR OWN.
You can avoid putting www.mydomain.com in the “permissions” field entirely if you set your web server to allow cross-origin requests to begin with. To make cross origin XHR requests, listing your domain in the permissions field is only needed if the web server for the domain doesn’t already allow cross-origin requests. That’s worth reading again. So, let’s say you’re making a cross-origin request to www.facebook.com. Well, since you don’t control the web server for www.facebook.com, you DO need to list www.facebook.com in the “permissions” field. But you DO control www.mydomain.com, so while you CAN list it in the permissions field, you don’t have to if you’ve already set the web server for www.mydomain.com to respond with a * for the Access-Control-Allow-Origin response header.
If the only reason you’re putting your domain in the permissions field is so you can make AJAX XHR requests to it, then you probably don’t need to do it. Just handle it on the web server by setting the Access-Control-Allow-Origin header.
There are OTHER reasons you may need a host in the permissions field, EVEN IF the host already has the Access-Control-Allow-Origin header set to *. If you need programatic access to the host’s cookies, and you declare the “cookies” permission in the manifest, then you’ll also need to declare the host in the permissions field. This is only if you need to access the cookies without making an XHR request. The documentation on the cookies permission field states that “To use the cookies API, you must declare the “cookies” permission in your manifest, along with host permissions for any hosts whose cookies you want to access.”
It’s important to understand this because at some point in the evolution of your Chrome extension, you may find that you separate your web server into www.mydomain.com and extension.mydomain.com, so that your marketing website can live at www.mydomain.com and your extension makes calls to extension.mydomain.com. Since Chrome extensions don’t allow you to future-proof your code you may have missed putting extension.mydomain.com in the permissions when you first launched. And if you add it later, your extension will become disabled for everyone until they accept your new permissions, which can be catastrophic to the user base for an extension. So, instead of updating the permissions field, you only need to set your server to allow cross origin requests.
It’s also important to understand this because you don’t want to scare off users when they click the “Install” button and get the permissions warning popup. If you list hosts in “permissions”, the user will be told that you want to have the ability to change the data on those sites. If you don’t, and just handle it via the the web server, you can reduce potentially scary permissions warnings.
Now, let’s talk about sending cookies with XmlHttpRequests.
Now that you understand what is and isn’t required to make cross origin requests, let’s talk about sending cookies with these requests.
First, you do not need to declare the “cookies” permission in your manifest.json, even though most developers assume you do. Google doesn’t do a good job of explaining what this “cookies” permission is for, but it’s NOT for passing along cookies in XHR requests. And as discussed above, you don’t necessarily need to declare the host in your “permissions” field either.
What you DO need to do is set .withCredentials=true in your XMLHttpRequest. That’s it. That’s how I’m doing it with GMass as well. Here’s a more detailed explanation of when .withCredentials is necessary. It’s not necessary for non-cross origin requests, so if the XHR call is being made from the same domain as the destination domain of the XHR request, then .withCrednetials has no effect. But for a Chrome extension’s content script the “origin” is the Chrome extension itself, and so you’re almost always making a cross-origin request when making an XHR call.
But here’s the tricky part. In the latest version of Chrome, you’ll think the cookies AREN’T being sent, when they actually are.
That’s because a recent update to the Chrome Developer Tools made using the Network tab a lot trickier. If you’re using the Network tab to monitor your XHR requests your Chrome extension is making, you’ll often see “Provisional headers shown” for the Request Headers, and you won’t see a Cookie section, so you’ll assume Cookies aren’t being sent. Perhaps you won’t then even try to retrieve the cookie on the server side. It took me a long time to figure this one out. This stack overflow article explains that a recent change to Chrome has made it so that you have to modify a few Chrome flags (chrome://flags) if you want to see the full headers, including any cookies being sent.
Once you make that change, you’ll see that your cookies are being sent.