Skip to content

Instantly share code, notes, and snippets.

@coenjacobs
Created February 2, 2026 14:24
Show Gist options
  • Select an option

  • Save coenjacobs/d37adc34149d8c30034cd1f20a89cce9 to your computer and use it in GitHub Desktop.

Select an option

Save coenjacobs/d37adc34149d8c30034cd1f20a89cce9 to your computer and use it in GitHub Desktop.
How to automate Claude Code installation and authentication without requiring browser-based OAuth authentication

Automating Claude Code Setup on a Headless VPS

This document describes how to automate Claude Code installation and authentication on headless VPS machines without requiring browser-based OAuth authentication.

The Problem

Claude Code's interactive mode requires OAuth authentication via a web browser. On headless VPS machines without a GUI, this creates a chicken-and-egg problem: you can't authenticate without a browser, but the machine has no browser.

While claude -p (print mode) works with just ANTHROPIC_API_KEY, interactive mode always launches an onboarding wizard that requires browser authentication.

The Solution

The solution involves three components:

  1. Long-lived OAuth token - Generated once on a machine with a browser
  2. Environment variable - CLAUDE_CODE_OAUTH_TOKEN to provide the token
  3. Onboarding bypass - A ~/.claude.json file that marks onboarding as complete

Step 1: Generate a Long-Lived OAuth Token

On a machine with a web browser (your local development machine), run:

claude setup-token

This will:

  1. Open a browser for OAuth authentication
  2. Generate a token valid for 1 year
  3. Display the token once (save it securely!)

The output will look like:

✓ Long-lived authentication token created successfully!

Your OAuth token (valid for 1 year):

sk-ant-oat01-xxxxx...xxxxx

Store this token securely. You won't be able to see it again.

Use this token by setting: export CLAUDE_CODE_OAUTH_TOKEN=<token>

Important: This requires a Claude Pro or Max subscription.

Step 2: Get Your Account Information

On the same machine where you ran setup-token, extract your account info:

cat ~/.claude.json | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(json.dumps(d.get('oauthAccount'), indent=2))"

This will output something like:

{
  "accountUuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "emailAddress": "your@email.com",
  "organizationUuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Save these values for the next step.

Step 3: Configure the Headless VPS

On your headless VPS, create two files:

3a. Set the OAuth token in your shell profile

Add to ~/.bashrc or ~/.bashrc.local:

export CLAUDE_CODE_OAUTH_TOKEN="sk-ant-oat01-your-token-here"

3b. Create the onboarding bypass file

Create ~/.claude.json with:

{
  "hasCompletedOnboarding": true,
  "lastOnboardingVersion": "2.1.29",
  "oauthAccount": {
    "accountUuid": "your-account-uuid",
    "emailAddress": "your@email.com",
    "organizationUuid": "your-organization-uuid"
  }
}

Replace the values with your actual account information from Step 2.

Note: Update lastOnboardingVersion to match your installed Claude Code version (claude --version).

Step 4: Test

source ~/.bashrc  # or reconnect SSH
claude

Claude Code should start directly without the onboarding wizard.

Note: You may still be prompted to "trust" a directory the first time you run Claude in a new project folder. This is expected behavior.

Automation with Ansible

For automated provisioning, you can use Ansible to deploy these configurations. Store the OAuth token in a secrets manager (like 1Password) and template the files during provisioning.

Example Ansible tasks:

- name: Configure Claude Code OAuth token in bashrc.local
  copy:
    content: |
      # Claude Code OAuth token (managed by Ansible)
      export CLAUDE_CODE_OAUTH_TOKEN="{{ claude_code_oauth_token }}"
    dest: "/home/{{ target_user }}/.bashrc.local"
    owner: "{{ target_user }}"
    mode: '0600'
  no_log: true

- name: Configure Claude Code onboarding bypass
  copy:
    content: |
      {
        "hasCompletedOnboarding": true,
        "lastOnboardingVersion": "2.1.29",
        "oauthAccount": {
          "accountUuid": "{{ claude_code_account.account_uuid }}",
          "emailAddress": "{{ claude_code_account.email }}",
          "organizationUuid": "{{ claude_code_account.organization_uuid }}"
        }
      }
    dest: "/home/{{ target_user }}/.claude.json"
    owner: "{{ target_user }}"
    mode: '0600'

Make sure your ~/.bashrc sources ~/.bashrc.local:

[[ -f ~/.bashrc.local ]] && . ~/.bashrc.local

Important Notes

Token vs API Key

  • CLAUDE_CODE_OAUTH_TOKEN - Uses your Claude Pro/Max subscription
  • ANTHROPIC_API_KEY - Uses API credits (pay-per-use)

Do not set both - Claude Code will warn about auth conflicts. Choose one:

  • For subscription-based usage: use CLAUDE_CODE_OAUTH_TOKEN
  • For API credit-based usage: use ANTHROPIC_API_KEY

Token Expiration

The OAuth token is valid for 1 year. You'll need to regenerate it before expiration by running claude setup-token again on a machine with a browser.

Security Considerations

  • Store the OAuth token securely (secrets manager, encrypted vault)
  • Set restrictive permissions on ~/.bashrc.local (mode 0600)
  • The token grants full access to your Claude subscription

Version Compatibility

This was tested with Claude Code version 2.1.29. The lastOnboardingVersion field may need to be updated for future versions if Claude Code changes its onboarding flow.

Troubleshooting

Still seeing the onboarding wizard?

  1. Verify CLAUDE_CODE_OAUTH_TOKEN is set: echo $CLAUDE_CODE_OAUTH_TOKEN | head -c 30
  2. Verify ~/.claude.json exists and has correct content
  3. Make sure you're not also setting ANTHROPIC_API_KEY

"Credit balance is too low" error

This happens when using claude -p (print mode) with an OAuth token. Print mode may require API credits even with a subscription token. For interactive use, this shouldn't be an issue.

Auth conflict warning

If you see a warning about both token and API key being set, unset the one you don't want:

unset ANTHROPIC_API_KEY  # If using OAuth token
# or
unset CLAUDE_CODE_OAUTH_TOKEN  # If using API key
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment