Search code examples
google-chromecookiesgoogle-chrome-extensionwebsocket

How to send cookies in websocket connection from extension's background script?


I am trying to open an authenticated websocket connection to my backend from a chrome extension, for that I am using session cookies. This is my manifest file:

{
    "manifest_version": 2,
    "background": {
        "scripts": ["extension.js"],
        "persistent": true
    },
    "permissions": [
        "tabs",
        "storage",
        "http://localhost:8000/",
        "ws://localhost:8000/"
    ],
    "icons": {
        "16": "img/icon16.png",
        "48": "img/icon48.png",
        "128": "img/icon128.png"
    }
}

there I asked permission to access localhost:8000 on both HTTP and websocket schemes, but Chrome send cookies only for AJAX requests. If I try

await fetch('http://localhost:8000/checklogin', {
    method: 'GET',
    credentials: 'include'
});

from background script cookies are sent, but if I try

const ws = new WebSocket('ws://localhost:8000/');

no cookies are sent at all.

Is not that same domain, why Chrome is not sending cookies for websocket?


Solution

  • From my research I concluded CORS with credentials for websocket is not supported in chrome extensions, so Chrome won't inject credentials in websocket upgrade requests. A workaround for that is to use chrome.webRequest to intercept websocket upgrade request and inject the Cookie header:

    class WebsocketCookie {
    
        constructor(websocketUrl, name, value) {
            this.name = name;
            this.value = value;
            chrome.webRequest.onBeforeSendHeaders.addListener(
                ({ requestHeaders }) => {
                    requestHeaders.push({name: 'Cookie', value: `${this.name}=${this.value}`});
                    return {requestHeaders};
                },
                {types: ['websocket'], urls: [websocketUrl]},
                ['blocking', 'requestHeaders', 'extraHeaders']
            );
        }
    }
    
    // you can change this cookie later by changing cookie.name or cookie.value
    const cookie = new WebsocketCookie('ws://localhost:8000/', 'sessionid', 'my_session_id');
    const ws = new WebSocket('ws://localhost:8000/'); // sessionid cookie will be injected
    

    that requires websocket URL, webRequest and the webRequestBlocking permissions to be added to manifest file. Use chrome.cookies to get cookies you need to inject in websocket upgrade request.

    If you would like to improve security, onBeforeSendHeaders callback takes the origin of that request as initiator member, use that to check if origin is your own extension. You can get your extension's origin using chrome.management API.