Skip to content

Chrome Extension: cookie bridge + auto-refresh + header capture #21

@Chkhikvadze

Description

@Chkhikvadze

Goal

Build a Chrome Extension that acts as a secure bridge between the user's LinkedIn browser session and the sync service. This is the recommended auth approach — zero detection risk because the user logs in normally.

Why Chrome Extension?

Approach Detection 2FA Auto-refresh
Chrome Extension ❌ none ✅ user handles ✅ automatic
Playwright login ⚠️ medium ❌ blocks ❌ manual
Manual cookie paste ❌ none ✅ user handles ❌ manual

Architecture

chrome-extension/
├── manifest.json          # MV3, permissions: cookies, webRequest, storage
├── background.js          # service worker
│   ├── cookie watcher     # chrome.cookies.onChanged → POST /accounts/refresh
│   ├── header interceptor # chrome.webRequest → capture x-li-track, csrf-token
│   └── sync trigger       # manual button → POST /sync
├── popup.html             # UI: status, account, sync button, service URL config
└── popup.js

manifest.json (key parts)

{
  "manifest_version": 3,
  "permissions": ["cookies", "storage", "webRequest"],
  "host_permissions": [
    "https://www.linkedin.com/*",
    "http://localhost:8899/*"
  ],
  "background": { "service_worker": "background.js" }
}

Cookie auto-refresh flow

// background.js
chrome.cookies.onChanged.addListener(({ cookie, removed }) => {
  if (cookie.domain.includes("linkedin.com") && cookie.name === "li_at" && !removed) {
    // Get JSESSIONID too
    chrome.cookies.get({ url: "https://www.linkedin.com", name: "JSESSIONID" }, (jsession) => {
      fetch(`${SERVICE_URL}/accounts/refresh`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          account_id: storedAccountId,
          li_at: cookie.value,
          jsessionid: jsession?.value || null
        })
      });
    });
  }
});

Header capture (x-li-track, csrf-token)

chrome.webRequest.onSendHeaders.addListener(
  (details) => {
    const track = details.requestHeaders.find(h => h.name === "x-li-track");
    const csrf  = details.requestHeaders.find(h => h.name === "csrf-token");
    if (track || csrf) {
      // store for provider use
      chrome.storage.local.set({ xLiTrack: track?.value, csrfToken: csrf?.value });
    }
  },
  { urls: ["https://www.linkedin.com/voyager/api/*"] },
  ["requestHeaders"]
);

Service-side: POST /accounts/refresh

See issue #8 — this endpoint must exist for the extension to push updated cookies.

Acceptance criteria

  • User installs extension, opens LinkedIn (normal login)
  • Extension captures cookies and registers account via POST /accounts
  • When li_at changes → auto-calls POST /accounts/refresh
  • Popup shows sync status + button to trigger manual sync

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions