← Back to Blog
AI/ML2026-05-10Β·76 min read

I Built a Chrome Extension to Fix My Biggest Claude.ai Frustration

By Sreyas

Zero-Latency Account Switching: A Cookie Orchestration Pattern for Browser Extensions

Current Situation Analysis

Developers and power users frequently manage multiple accounts on AI platforms and SaaS tools to separate work, personal, and experimental contexts. The standard browser model enforces a single authenticated session per domain, forcing users into a repetitive manual cycle: logout, clear state, navigate to login, enter credentials, pass MFA, and wait for session initialization.

This friction is often underestimated. A single account switch typically consumes 30 to 60 seconds of active time and introduces significant cognitive load due to context interruption. For users switching accounts 10+ times daily, this accumulates to over an hour of lost productivity per week. Furthermore, manual switching increases the risk of credential fatigue and accidental data leakage between contexts.

The root cause is a misunderstanding of browser session mechanics. Most developers treat authentication as an immutable state tied to the browser profile. However, session management is fundamentally driven by cookie state. By treating cookies as portable data artifacts rather than static browser state, it is possible to decouple account identity from the browser session, enabling programmatic switching with near-zero latency.

WOW Moment: Key Findings

The shift from manual authentication to cookie orchestration transforms account switching from a network-bound operation to a local state restoration. The performance delta is substantial, effectively eliminating the "switch tax" that degrades workflow efficiency.

Approach Switch Latency Cognitive Overhead Error Probability Network Dependency
Manual Auth Cycle 30–60s High (Credentials, MFA, Navigation) Medium (Typos, Lockouts) Required
Cookie Orchestration <1s Near Zero (Selection only) Low (Deterministic) None

This finding enables workflows where account context can be switched dynamically based on task requirements without breaking flow. It also allows for the creation of "session brokers" that can manage dozens of accounts within a single browser instance, a capability previously restricted to complex multi-profile setups or third-party password managers with limited automation.

Core Solution

The architecture relies on a Manifest V3 Chrome Extension acting as a session broker. The extension intercepts, persists, and restores cookie payloads for specific domains. The implementation avoids frameworks to minimize bundle size and latency, leveraging direct access to Chrome's extension APIs.

Architecture Decisions

  1. Manifest V3 Service Worker: The background script runs as a service worker. This requires a stateless design where all session data is persisted to chrome.storage.local. The service worker cannot hold state in memory across wake/sleep cycles.
  2. Cookie Serialization: Cookies must be fully serialized, including attributes like sameSite, secure, and httpOnly. Partial serialization leads to silent authentication failures.
  3. Atomic Restoration: Switching requires clearing existing cookies and injecting new ones atomically. A race condition between clearing and setting can leave the browser in an invalid state. The restoration process must await all cookie operations before triggering a tab reload.
  4. Incognito Onboarding: Adding a new account requires capturing a fresh login session. Since the main window is already authenticated, the extension spawns a temporary incognito window. This isolates the login flow, allows the user to authenticate without disturbing the active session, and enables the extension to capture the resulting cookies before closing the window.

Implementation: Session Broker

The following TypeScript implementation demonstrates the core logic using a modular class structure. This differs from functional approaches by encapsulating state management and providing type safety for cookie attributes.

1. Session Capture Module

This module extracts cookies from the target domain and normalizes them for storage. It ensures all attributes required for a valid session are preserved.

// types.ts
export interface SerializableCookie {
  name: string;
  value: string;
  domain: string;
  path: string;
  sameSite: chrome.cookies.SameSiteStatus;
  secure: boolean;
  httpOnly: boolean;
  session: boolean;
  expirationDate?: number;
}

// SessionCapture.ts
export class SessionCapture {
  private static readonly TARGET_DOMAIN = '.claude.ai';

  static async extractSession(): Promise<SerializableCookie[]> {
    const rawCookies = await chrome.cookies.getAll({
      domain: this.TARGET_DOMAIN
    });

    return rawCookies.map(cookie => ({
      name: cookie.name,
      value: cookie.value,
      domain: cookie.domain,
      path: cookie.path,
      sameSite: cookie.sameSite,
      secure: cookie.secure,
      httpOnly: cookie.httpOnly,
      session: cookie.session,
      expirationDate: cookie.expirationDate
    }));
  }

  static async persist(alias: string, cookies: SerializableCookie[]): Promise<void> {
    const payload = {
      cookies,
      timestamp: Date.now(),
      domain: this.TARGET_DOMAIN
    };
    
    // Use a namespaced key to prevent collisions
    const storageKey = `session_broker_${alias}`;
    await chrome.storage.local.set({ [storageKey]: payload });
  }
}

2. Session Restoration Module

This module handles the atomic switch. It clears active cookies for the domain and injects the saved payload. It includes a synchronization mechanism to ensure the tab reloads only after cookies are fully applied.

// SessionRestorer.ts
export class SessionRestorer {
  private static readonly TARGET_URL = 'https://claude.ai';

  static async switchTo(alias: string): Promise<void> {
    const storageKey = `session_broker_${alias}`;
    const result = await chrome.storage.local.get(storageKey);
    const payload = result[storageKey];

    if (!payload || !payload.cookies) {
      throw new Error(`Session '${alias}' not found.`);    }

    // Phase 1: Clear existing session cookies
    const activeCookies = await chrome.cookies.getAll({
      domain: payload.domain
    });

    const removalPromises = activeCookies.map(c => 
      chrome.cookies.remove({
        url: this.TARGET_URL,
        name: c.name
      })
    );
    await Promise.all(removalPromises);

    // Phase 2: Inject saved cookies
    const injectionPromises = payload.cookies.map(c => 
      chrome.cookies.set({
        url: this.TARGET_URL,
        name: c.name,
        value: c.value,
        domain: c.domain,
        path: c.path,
        sameSite: c.sameSite,
        secure: c.secure,
        httpOnly: c.httpOnly,
        expirationDate: c.expirationDate
      })
    );
    await Promise.all(injectionPromises);

    // Phase 3: Reload active tab to apply session
    const [tab] = await chrome.tabs.query({
      active: true,
      currentWindow: true,
      url: '*://claude.ai/*'
    });

    if (tab?.id) {
      await chrome.tabs.reload(tab.id);
    }
  }
}

3. Incognito Account Onboarding

For adding new accounts, the extension manages a transient incognito window. It monitors the window for successful authentication by observing URL changes or cookie creation events.

// AccountOnboarding.ts
export class AccountOnboarding {
  static async captureNewAccount(alias: string): Promise<void> {
    // Create isolated window
    const window = await chrome.windows.create({
      incognito: true,
      url: 'https://claude.ai/login',
      focused: true
    });

    // Monitor for login success via cookie changes
    const listener = (changeInfo: chrome.cookies.CookieChangedCause) => {
      if (changeInfo.cause === 'explicit' && changeInfo.cookie.domain.includes('claude.ai')) {
        // Login detected; capture and close
        chrome.cookies.onChanged.removeListener(listener);
        SessionCapture.extractSession().then(cookies => {
          SessionCapture.persist(alias, cookies);
          chrome.windows.remove(window.id);
        });
      }
    };

    chrome.cookies.onChanged.addListener(listener);

    // Fallback timeout to prevent orphaned windows
    setTimeout(() => {
      chrome.cookies.onChanged.removeListener(listener);
      chrome.windows.remove(window.id);
    }, 300000); // 5-minute timeout
  }
}

Pitfall Guide

Browser extension development introduces unique constraints that differ significantly from standard web application development. The following pitfalls are critical to address for a robust implementation.

  1. Service Worker State Loss

    • Explanation: Manifest V3 service workers terminate after a period of inactivity. Any state stored in JavaScript variables or module-level scope is lost when the worker sleeps.
    • Fix: Never rely on in-memory state for session data. All session metadata must be read from chrome.storage.local upon every service worker invocation. Implement a lazy-loading pattern where storage is accessed only when needed.
  2. Cookie Attribute Serialization Gaps

    • Explanation: When serializing cookies to JSON, attributes like sameSite may be omitted if they are undefined or default values. Restoring a cookie without the exact sameSite attribute can cause the browser to reject the session or treat it as cross-site, breaking authentication.
    • Fix: Explicitly map every cookie attribute during serialization. Ensure the restoration logic passes all attributes, even defaults. Use a strict interface to guarantee no attributes are dropped during the JSON round-trip.
  3. Race Conditions During Tab Reload

    • Explanation: Triggering chrome.tabs.reload immediately after starting cookie injection can result in the page loading before cookies are fully applied. This leads to a "logged out" state despite the restore operation.
    • Fix: Await the completion of all chrome.cookies.set promises before calling chrome.tabs.reload. Use Promise.all to batch cookie operations and ensure the reload occurs only after the entire payload is injected.
  4. Domain Scope Normalization

    • Explanation: Cookies may be set with a leading dot (e.g., .claude.ai) or without (claude.ai). The browser normalizes these, but storage keys and query filters must handle both formats consistently. Mismatched domain strings can result in cookies not being found or restored.
    • Fix: Normalize domain strings during capture and restoration. Strip leading dots for storage keys but preserve the exact domain attribute for cookie operations. Use wildcard patterns in chrome.cookies.getAll queries to ensure comprehensive capture.
  5. Incognito Window Orphaning

    • Explanation: If the user closes the incognito window manually or the login fails, the extension may leave the window open or fail to clean up event listeners. This degrades user experience and consumes resources.
    • Fix: Implement a timeout mechanism to close the window after a reasonable period. Always remove event listeners in both success and error paths. Provide user feedback if the capture process times out.
  6. Cookie Expiration Handling

    • Explanation: Saved sessions have expiration dates. Restoring an expired session results in immediate logout. Users may not realize their saved session is stale until they attempt to switch.
    • Fix: Validate expirationDate before restoration. If a session is expired or near expiration, flag it in the UI and prompt the user to re-authenticate. Implement automatic refresh logic if the platform supports token renewal.
  7. HttpOnly Cookie Permissions

    • Explanation: Extensions cannot read HttpOnly cookies without explicit host permissions in the manifest. Attempting to capture sessions without these permissions results in missing critical authentication tokens.
    • Fix: Declare host_permissions for the target domain in manifest.json. Ensure the permission scope covers all subdomains and paths required for the session. Request permissions dynamically if necessary to improve install conversion.

Production Bundle

Action Checklist

  • Define host_permissions in manifest.json for the target domain and subdomains.
  • Implement a storage wrapper with error handling and migration logic for schema updates.
  • Add cookie attribute normalization to handle domain dots and sameSite defaults.
  • Implement atomic restoration with Promise.all synchronization before tab reload.
  • Add incognito window management with timeout and listener cleanup.
  • Validate cookie expiration dates and handle stale sessions gracefully.
  • Test with real accounts to verify cookie integrity and session persistence.
  • Add UI feedback for capture success, restoration status, and error states.

Decision Matrix

Scenario Recommended Approach Why Cost Impact
Single User, Few Accounts Local Storage + Vanilla JS Simplicity, low latency, zero dependencies. Low
Enterprise, Compliance Encrypted Storage + Audit Log Security, governance, and auditability. High
Cross-Browser Support Manifest V3 + Polyfills Portability across Chrome, Edge, Firefox. Medium
High-Frequency Switching Optimized Cookie Batching Minimizes API calls and reduces switch time. Low

Configuration Template

The following manifest.json template provides the minimal configuration required for the session broker.

{
  "manifest_version": 3,
  "name": "Session Broker",
  "version": "1.0.0",
  "description": "Zero-latency account switching via cookie orchestration.",
  "permissions": [
    "storage",
    "cookies",
    "tabs"
  ],
  "host_permissions": [
    "https://*.claude.ai/*"
  ],
  "background": {
    "service_worker": "background.js",
    "type": "module"
  },
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  }
}

Quick Start Guide

  1. Initialize Project: Create a directory with manifest.json, background.js, and popup.html. Configure permissions as shown in the template.
  2. Implement Core Logic: Add the SessionCapture and SessionRestorer modules to background.js. Wire up message listeners to handle popup actions.
  3. Load Extension: Open Chrome, navigate to chrome://extensions, enable Developer Mode, and load the unpacked extension directory.
  4. Test Workflow: Navigate to the target domain, trigger a session capture, switch accounts manually, and verify the restoration flow. Validate cookie attributes and tab reload synchronization.
  5. Refine UI: Build the popup interface to list saved sessions, trigger captures, and display status indicators. Ensure error handling is visible to the user.