I Built a Chrome Extension to Fix My Biggest Claude.ai Frustration
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
- 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. - Cookie Serialization: Cookies must be fully serialized, including attributes like
sameSite,secure, andhttpOnly. Partial serialization leads to silent authentication failures. - 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.
- 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.
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.localupon every service worker invocation. Implement a lazy-loading pattern where storage is accessed only when needed.
Cookie Attribute Serialization Gaps
- Explanation: When serializing cookies to JSON, attributes like
sameSitemay be omitted if they areundefinedor default values. Restoring a cookie without the exactsameSiteattribute 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.
- Explanation: When serializing cookies to JSON, attributes like
Race Conditions During Tab Reload
- Explanation: Triggering
chrome.tabs.reloadimmediately 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.setpromises before callingchrome.tabs.reload. UsePromise.allto batch cookie operations and ensure the reload occurs only after the entire payload is injected.
- Explanation: Triggering
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.getAllqueries to ensure comprehensive capture.
- Explanation: Cookies may be set with a leading dot (e.g.,
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.
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
expirationDatebefore 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.
HttpOnly Cookie Permissions
- Explanation: Extensions cannot read
HttpOnlycookies without explicit host permissions in the manifest. Attempting to capture sessions without these permissions results in missing critical authentication tokens. - Fix: Declare
host_permissionsfor the target domain inmanifest.json. Ensure the permission scope covers all subdomains and paths required for the session. Request permissions dynamically if necessary to improve install conversion.
- Explanation: Extensions cannot read
Production Bundle
Action Checklist
- Define
host_permissionsinmanifest.jsonfor 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
sameSitedefaults. - Implement atomic restoration with
Promise.allsynchronization 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
- Initialize Project: Create a directory with
manifest.json,background.js, andpopup.html. Configure permissions as shown in the template. - Implement Core Logic: Add the
SessionCaptureandSessionRestorermodules tobackground.js. Wire up message listeners to handle popup actions. - Load Extension: Open Chrome, navigate to
chrome://extensions, enable Developer Mode, and load the unpacked extension directory. - 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.
- Refine UI: Build the popup interface to list saved sessions, trigger captures, and display status indicators. Ensure error handling is visible to the user.
