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

Closed
opened 2026-02-16 18:05:12 -05:00 by yindo · 4 comments
Owner

Originally created by @mguttmann on GitHub (Jan 19, 2026).

Originally assigned to: @thdxr on GitHub.

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:

  • #6559: "Usually it happens when I stopped using for a while, e.g. 12 hours"
  • #4992: "Getting 'Unauthorized: token expired' during conversation"
  • #8544: "The Claude Subscription Issue is back"

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

  • Requires #9068 (Multi-Account OAuth) for:
    • Per-account OAuth record management
    • Settings UI infrastructure
    • Account selection in CLI

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

  • #6559 - Claude subscription token expires after a period of time
  • #4992 - Getting "Unauthorized: token expired" during conversation
  • #8544 - The Claude Subscription Issue is back
  • #9111 - OAuth token expires after inactivity (closed, solution didn't work)
Originally created by @mguttmann on GitHub (Jan 19, 2026). Originally assigned to: @thdxr on GitHub. ## 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: - #6559: "Usually it happens when I stopped using for a while, e.g. 12 hours" - #4992: "Getting 'Unauthorized: token expired' during conversation" - #8544: "The Claude Subscription Issue is back" **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 ```bash # 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 ```typescript 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 - **Requires #9068 (Multi-Account OAuth)** for: - Per-account OAuth record management - Settings UI infrastructure - Account selection in CLI ## 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 - #6559 - Claude subscription token expires after a period of time - #4992 - Getting "Unauthorized: token expired" during conversation - #8544 - The Claude Subscription Issue is back - #9111 - OAuth token expires after inactivity (closed, solution didn't work)
yindo closed this issue 2026-02-16 18:05:12 -05:00
Author
Owner

@mguttmann commented on GitHub (Jan 19, 2026):

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)

@mguttmann commented on GitHub (Jan 19, 2026): ## 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)_
Author
Owner

@mguttmann commented on GitHub (Jan 19, 2026):

Image
@mguttmann commented on GitHub (Jan 19, 2026): <img width="612" height="494" alt="Image" src="https://github.com/user-attachments/assets/199fbfc3-5cef-4e40-90b7-c7f4638f5fe3" />
Author
Owner

@mguttmann commented on GitHub (Jan 21, 2026):

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:

  • #9069 - Multi-Account OAuth (dependency)
  • #9073 - YOLO Mode (sibling, independent)
  • #9455 - This feature (Auto-Relogin) ← ready for review

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

@mguttmann commented on GitHub (Jan 21, 2026): ## 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: - #9069 - Multi-Account OAuth (dependency) - #9073 - YOLO Mode (sibling, independent) - **#9455** - This feature (Auto-Relogin) ← ready for review Branch: `feat/auto-relogin-browser-sessions-clean`
Author
Owner

@mguttmann commented on GitHub (Jan 22, 2026):

Closing in favor of combined issue with all features

@mguttmann commented on GitHub (Jan 22, 2026): Closing in favor of combined issue with all features
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#6762