How is Claude Pro/Max authentication implemented?

dev
4f7458bignore: update download stats 2026-01-07
This post might have stale content, as dev is 2273 commits ahead.
Avatar of martinsione
Martin Sione
posted

how is claude pro/max auth implemented?

Avatar of anthropic/claude-haiku-4.5
Claude Haiku 4.5
commented

Perfect! Now I have all the information I need. Let me provide a comprehensive answer about how Claude Pro/Max authentication is implemented.

##Claude Pro/Max Authentication Implementation

Based on the source code, here's how Claude Pro/Max auth is implemented in OpenCode:

###Architecture Overview

Claude Pro/Max authentication is implemented as a plugin system (opencode-anthropic-auth), which is loaded as a built-in component. The system uses a modular, layered approach:

###1. Plugin System (packages/opencode/src/plugin/index.ts)

OpenCode loads the Anthropic auth plugin as a built-in component:

const BUILTIN = ["opencode-copilot-auth@0.0.9", "opencode-anthropic-auth@0.0.5"]

Plugins are dynamically loaded and registered, providing hooks for authentication across the system.

###2. Authentication Storage (packages/opencode/src/auth/index.ts)

Credentials are stored locally in ~/.opencode/data/auth.json with secure file permissions (chmod 0o600). Three auth types are supported:

// OAuth tokens with refresh capability
export const Oauth = z.object({
  type: z.literal("oauth"),
  refresh: z.string(),
  access: z.string(),
  expires: z.number(),
  enterpriseUrl: z.string().optional(),
})

// Direct API keys
export const Api = z.object({
  type: z.literal("api"),
  key: z.string(),
})

// WellKnown auth (enterprise)
export const WellKnown = z.object({
  type: z.literal("wellknown"),
  key: z.string(),
  token: z.string(),
})

###3. Provider Auth Handler (packages/opencode/src/provider/auth.ts)

The ProviderAuth namespace manages OAuth flows:

  • Calls plugin auth methods via the authorize() function
  • Handles OAuth callbacks and token exchange
  • Stores credentials using the Auth module
  • Supports both OAuth and API key methods

###4. Anthropic OAuth Plugin (opencode-anthropic-auth)

The core authentication logic uses OAuth with PKCE (Proof Key for Code Exchange):

####Key Features:

Two Auth Methods:

  1. Claude Pro/Max OAuth - Direct login via Anthropic:

    • Uses Claude.ai OAuth endpoint (https://claude.ai/oauth/authorize)
    • Redirects to https://console.anthropic.com/oauth/code/callback
    • Scopes: org:create_api_key user:profile user:inference
    • Uses PKCE for security
  2. Create API Key via Console - For API key generation:

    • Uses Anthropic Console OAuth (https://console.anthropic.com/oauth/authorize)
    • Automatically creates an API key via: https://api.anthropic.com/api/oauth/claude_cli/create_api_key
    • Returns raw_key which is then used as standard API auth

####OAuth Flow:

// 1. Authorization URL generation with PKCE
async function authorize(mode) {
  const pkce = await generatePKCE()
  const url = new URL(
    `https://${mode === "console" ? "console.anthropic.com" : "claude.ai"}/oauth/authorize`
  )
  url.searchParams.set("client_id", CLIENT_ID) // 9d1c250a-e61b-44d9-88ed-5944d1962f5e
  url.searchParams.set("code_challenge", pkce.challenge)
  url.searchParams.set("code_challenge_method", "S256")
  // ... other params
  return { url, verifier }
}

// 2. Token Exchange
async function exchange(code, verifier) {
  const result = await fetch("https://console.anthropic.com/v1/oauth/token", {
    method: "POST",
    body: JSON.stringify({
      code, 
      grant_type: "authorization_code",
      client_id: CLIENT_ID,
      code_verifier: verifier,
    }),
  })
  return {
    type: "success",
    refresh: json.refresh_token,
    access: json.access_token,
    expires: Date.now() + json.expires_in * 1000,
  }
}

####Token Refresh:

When tokens expire, the system automatically refreshes:

if (!auth.access || auth.expires < Date.now()) {
  const response = await fetch("https://console.anthropic.com/v1/oauth/token", {
    method: "POST",
    body: JSON.stringify({
      grant_type: "refresh_token",
      refresh_token: auth.refresh,
      client_id: CLIENT_ID,
    }),
  })
  // Store updated tokens
}

###5. Cost Zeroing for Pro/Max

When OAuth auth is detected, model costs are zeroed out (indicating a paid subscription plan with unlimited usage):

if (auth.type === "oauth") {
  // zero out cost for max plan
  for (const model of Object.values(provider.models)) {
    model.cost = {
      input: 0,
      output: 0,
      cache: { read: 0, write: 0 },
    }
  }
}

###6. Request Interception

The plugin intercepts all API requests to add OAuth tokens and beta headers:

async fetch(input, init) {
  const auth = await getAuth()
  
  // Inject Bearer token
  const headers = {
    ...init.headers,
    authorization: `Bearer ${auth.access}`,
    "anthropic-beta": mergedBetas, // OAuth beta + other feature betas
  }
  
  return fetch(input, { ...init, headers })
}

###7. Beta Headers

For Claude Pro/Max, additional beta features are enabled:

  • oauth-2025-04-20 - OAuth support
  • claude-code-20250219 - Claude Code features
  • interleaved-thinking-2025-05-14 - Extended thinking
  • fine-grained-tool-streaming-2025-05-14 - Streaming support

###Authentication Flow Summary

User selects "Claude Pro/Max" 
  ↓
authorize("max") generates PKCE-protected URL
  ↓
User logs into https://claude.ai/oauth/authorize
  ↓
Redirects to callback with authorization code
  ↓
exchange(code, verifier) exchanges code for tokens
  ↓
OAuth tokens stored in ~/.opencode/data/auth.json
  ↓
On API calls: tokens auto-refresh if expired
  ↓
Bearer token + beta headers injected into requests

This design provides a secure, user-friendly OAuth flow that supports both Claude Pro/Max subscriptions and API key-based authentication, with automatic token management and feature enablement.

Avatar of martinsione
Martin Sione
commented

do i need to do anything special to be able to use those credentials for the completions api in anthropic?

Avatar of anthropic/claude-haiku-4.5
Claude Haiku 4.5
commented

Excellent! Now I have all the information. Let me provide a comprehensive answer.

##Using OAuth Credentials with Anthropic's Completions API

Based on the source code and Anthropic API documentation, here's what you need to know about using Claude Pro/Max OAuth credentials with the Anthropic completions API:

###Yes, OAuth credentials work directly with the Completions API

The OpenCode anthropic-auth plugin intercepts all API requests and automatically injects the OAuth Bearer token. Here's how:

###The Request Interception System

When you have OAuth credentials configured, the plugin wraps the fetch function and adds the Bearer token to every request:

async fetch(input, init) {
  const auth = await getAuth()
  if (auth.type !== "oauth") return fetch(input, init)
  
  // Auto-refresh token if expired
  if (!auth.access || auth.expires < Date.now()) {
    const response = await fetch("https://console.anthropic.com/v1/oauth/token", {
      method: "POST",
      body: JSON.stringify({
        grant_type: "refresh_token",
        refresh_token: auth.refresh,
        client_id: CLIENT_ID,
      }),
    })
    // Update stored tokens
    await client.auth.set({ ... })
  }
  
  // Inject Bearer token into Authorization header
  const headers = {
    ...init.headers,
    authorization: `Bearer ${auth.access}`,
    "anthropic-beta": mergedBetas, // Include feature betas
  }
  
  return fetch(input, { ...init, headers })
}

###Key Implementation Details

1. Token Format The OAuth access token is used directly as a Bearer token:

Authorization: Bearer sk-ant-oat01-xxxxx...

2. Automatic Token Refresh

  • Tokens are checked before each request
  • If expired (auth.expires < Date.now()), automatic refresh happens
  • Refreshed tokens are stored back to disk

3. Beta Headers Enabled OAuth requests automatically get these Anthropic beta headers:

  • oauth-2025-04-20 - OAuth support flag
  • claude-code-20250219 - Claude Code features
  • interleaved-thinking-2025-05-14 - Extended thinking
  • fine-grained-tool-streaming-2025-05-14 - Tool streaming

###Usage in Code

When you use OpenCode with OAuth credentials, the AI SDK is initialized with:

// Credentials are loaded from Auth storage
const auth = await Auth.get("anthropic")

if (auth?.type === "oauth") {
  // SDK is created with custom fetch handler
  const sdk = createAnthropic({
    name: "anthropic",
    fetch: async (input, init) => {
      // Bearer token automatically injected here
      return fetch(input, { 
        ...init, 
        headers: {
          authorization: `Bearer ${auth.access}`,
          ...init.headers
        }
      })
    }
  })
}

###No Special Setup Required

You don't need to do anything special. The system automatically:

✅ Uses OAuth tokens for all requests
✅ Refreshes expired tokens transparently
✅ Strips x-api-key header if using OAuth
✅ Adds required beta headers
✅ Zeros out model costs (indicating unlimited subscription)

###Important Notes

  1. The Anthropic SDK handles this for you - Since OpenCode uses @ai-sdk/anthropic, the SDK wrapper applies the fetch function to all completions API calls

  2. Token refresh is automatic - No manual intervention needed; expired tokens are refreshed before requests

  3. Works with all endpoints - The Bearer token approach works with:

    • /v1/messages (Messages API)
    • Streaming endpoints
    • Batch processing
    • All other Anthropic endpoints that support OAuth

###Why This Works

Anthropic's OAuth flow was designed to work directly with their API. The token you receive from the OAuth flow is exactly what their completions API expects - a Bearer token in the Authorization header. OpenCode just manages the storage, refresh, and injection of this token automatically.

So in summary: Just authenticate once with "Claude Pro/Max" in OpenCode, and the OAuth credentials are used automatically for all completions API calls without any additional configuration needed.


END OF POST

How is Claude Pro/Max authentication implemented? — anomalyco/opencode