Skip to content

feat: Auto-Relogin for Anthropic OAuth via Persistent Browser Sessions #9360

Closed

Description

@mguttmann

Problem

OAuth tokens expire after ~12 hours of inactivity, causing "Token refresh failed: 400" errors when users return to OpenCode. This is a widespread issue affecting many users:

Current workaround: Users must manually re-login every morning, which disrupts workflow.

Root cause: Anthropic's OAuth refresh tokens become invalid after extended inactivity periods. This cannot be prevented by periodic token refresh or API pings - the session itself expires server-side.

Proposed Solution

Implement persistent headless browser sessions that can automatically refresh OAuth tokens when 400 errors occur.

How it works

  1. One-time setup: User runs opencode auth browser setup, browser opens, user logs into claude.ai
  2. Session persists: Browser profile saved to ~/.opencode/browsers/anthropic/<account-id>/
  3. Auto-refresh: When a 400 error occurs:
    • Headless browser launches with saved profile (already logged in)
    • Navigates to OAuth authorize URL
    • Automatically redirected to callback (no user interaction needed)
    • New tokens extracted and saved
    • Original request retried

Architecture

┌─────────────────┐     ┌──────────────┐     ┌─────────────────────┐
│ OpenCode        │────▶│ Auth Module  │────▶│ Headless Browser    │
│ (API Call)      │     │ (Error 400)  │     │ (Playwright)        │
└─────────────────┘     └──────────────┘     └─────────────────────┘
                               │                       │
                               ▼                       ▼
                        ┌──────────────┐     ┌─────────────────────┐
                        │ Retry with   │◀────│ New OAuth Token     │
                        │ new Token    │     │ (auto-extracted)    │
                        └──────────────┘     └─────────────────────┘

Features

Multi-Account Support

Each OAuth account gets its own browser profile, enabling auto-relogin for all accounts:

~/.opencode/browsers/anthropic/
  ├── <account-1-id>/    # Browser profile for Account 1
  ├── <account-2-id>/    # Browser profile for Account 2
  └── <account-n-id>/    # Browser profile for Account N

CLI Commands

# Setup browser session for an account
$ opencode auth browser setup
◆ Select account to configure:
│ ○ account-1@email.com
│ ● account-2@email.com
│
◐ Opening browser... Please log in to claude.ai
◐ Login detected. Saving session...
└ Browser session configured for account-2@email.com

# Check status of all browser sessions
$ opencode auth browser status
Account 1 (account-1@email.com): ● Active (last refresh: 2h ago)
Account 2 (account-2@email.com): ○ Not configured
Account 3 (account-3@email.com): ● Active (last refresh: 5min ago)

# Remove a browser session
$ opencode auth browser remove
◆ Select account to remove session:
│ ● account-1@email.com
│
└ Browser session removed

Desktop UI Integration

In Settings → Providers → Anthropic → [Account]:

┌─────────────────────────────────────────────────┐
│ Account: account@email.com                      │
├─────────────────────────────────────────────────┤
│ Usage: ████████░░ 80%                           │
│                                                 │
│ ─────────────────────────────────────────────── │
│                                                 │
│ 🔄 Auto-Relogin                                 │
│                                                 │
│ Status: ● Enabled                               │
│ Last Refresh: 2 hours ago                       │
│                                                 │
│ [Configure Session]  [Remove Session]           │
│                                                 │
├─────────────────────────────────────────────────┤
│ [Switch to this Account]  [Delete Account]      │
└─────────────────────────────────────────────────┘

Technical Implementation

New Files

File Purpose
packages/opencode/src/auth/browser.ts Headless browser session management
packages/opencode/src/auth/auto-relogin.ts Error detection & auto-refresh logic

Modified Files

File Changes
packages/opencode/src/auth/index.ts Add browserSession field to OAuthRecord
packages/opencode/src/cli/cmd/auth.ts Add browser subcommands
packages/app/src/components/dialog-settings.tsx Add Auto-Relogin UI section
packages/opencode/package.json Add Playwright dependency

Auth Store Schema Extension

const OAuthRecord = z.object({
  // ... existing fields ...
  browserSession: z.object({
    enabled: z.boolean(),
    profilePath: z.string(),
    lastRefresh: z.number().optional(),
    lastError: z.string().optional(),
  }).optional(),
})

API Endpoints

Endpoint Purpose
POST /auth/browser/setup Initiate browser session setup
GET /auth/browser/status Get browser session status for all accounts
DELETE /auth/browser/:recordId Remove browser session for account

Dependencies

Security Considerations

  • Browser profiles stored locally in user's data directory
  • No credentials transmitted over network (browser handles auth natively)
  • Sessions can be removed at any time via CLI or UI
  • Headless browser runs only when needed (not persistent daemon)

Acceptance Criteria

  • opencode auth browser setup opens browser for login
  • Browser session persists after setup
  • 400 errors trigger automatic token refresh
  • Multi-account: each account has separate session
  • CLI shows browser session status
  • Desktop UI shows status and config options
  • Sessions can be removed via CLI and UI
  • Works on macOS, Linux, Windows

Related Issues

Activity

  1. mguttmann commented on Jan 19, 2026

    @mguttmann
    Author

    Implementation Complete! 🎉

    PR #9455 implements the auto-relogin feature as described in this issue.

    How It Works

    User's token expires overnight
             ↓
    Plugin throws "Token refresh failed: 400"
             ↓
    rotating-fetch.ts catches the error
             ↓
    attemptBrowserRelogin() is called
             ↓
    Headless Puppeteer + Stealth plugin launches
             ↓
    Uses saved cookies from browser profile
             ↓
    Navigates to claude.ai/oauth/authorize
             ↓
    Auto-clicks "Authorize" button (user is still logged in via cookies)
             ↓
    Extracts authorization code from callback URL
             ↓
    Exchanges code for new access + refresh tokens
             ↓
    Updates auth store with new tokens
             ↓
    Retries the original failed request
             ↓
    Success! ✅
    

    Setup (One-Time)

    ```bash
    opencode auth browser setup
    ```

    This opens a browser window where you log in to claude.ai. The cookies are saved in an isolated browser profile at ~/.local/share/opencode/browsers/anthropic/<recordId>/.

    Key Technical Decisions

    1. Puppeteer over Playwright: Playwright was blocked by Cloudflare's bot detection. Puppeteer with puppeteer-extra-plugin-stealth successfully bypasses it.

    2. Auto-install: Puppeteer and its dependencies are installed automatically on first use to keep the base package small.

    3. Per-account profiles: Each OAuth account has its own isolated browser profile, so multiple accounts can have separate sessions.

    4. Dual callback URLs: The implementation handles both console.anthropic.com/oauth/code/callback and platform.claude.com/oauth/code/callback since Anthropic uses both.

    5. Profile locking: Prevents race conditions when multiple requests trigger auto-relogin simultaneously.

    CLI Commands

    Command Description
    opencode auth browser setup Configure browser session (opens visible browser)
    opencode auth browser status Show all browser session statuses
    opencode auth browser remove Remove a browser session

    Testing Results

    ✅ CLI: Auto-relogin works seamlessly
    ✅ Desktop: Auto-relogin works seamlessly
    ✅ Token invalidation test: Recovers automatically
    ✅ Multiple accounts: Each account can have its own browser session

    Screenshots

    (Will be added below)

  2. mguttmann commented on Jan 19, 2026

    @mguttmann
    Author
    Image
  3. mguttmann commented on Jan 21, 2026

    @mguttmann
    Author

    Implementation Update

    PR #9455 has been rebased onto latest dev (v1.1.30+) and is ready for review.

    What's implemented:

    • ✅ Persistent browser sessions via Puppeteer
    • ✅ One-time setup: opencode auth browser setup
    • ✅ Automatic re-authentication on 401 errors (headless)
    • ✅ CLI: opencode auth browser [setup|status|refresh|remove]
    • ✅ API: /provider/auth/browser/* endpoints
    • ✅ Integration with rotating-fetch for seamless token refresh

    How it solves the problem:

    When OAuth tokens expire after ~12 hours of inactivity, the system:

    1. Detects 401 error during API call
    2. Launches headless browser with saved profile (already logged in)
    3. Navigates to OAuth authorize URL → auto-redirects to callback
    4. Extracts new tokens and retries the original request

    No user interaction needed after initial setup!

    Related PRs:

    Branch: feat/auto-relogin-browser-sessions-clean

  4. mguttmann commented on Jan 22, 2026

    @mguttmann
    Author

    Closing in favor of combined issue with all features

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions