콘텐츠로 이동

LiteLLM + GitHub Copilot Auto-Auth Setup Guide

This guide details how to set up LiteLLM to use GitHub Copilot's models (GPT-4, Claude, Gemini) by automatically injecting the authentication token (ghu_...) from your local VS Code environment.

This method allows you to run LiteLLM with Docker Compose without manually updating tokens, as long as you are logged into GitHub Copilot in VS Code.

Prerequisites

  • Linux Environment (Instructions below are optimized for Linux)
  • Docker & Docker Compose installed
  • VS Code installed and logged in to GitHub Copilot
  • (Optional) jq installed for JSON parsing (alternative grep commands provided)

1. Automatic Token Extraction & Setup (.env)

Instead of manually copying the token, we can extract it from VS Code's local configuration file (hosts.json) and save it to the .env file.

Choose one of the following:

  • Option A: Extract the ghu_... token from VS Code (recommended for desktop use).
  • Option B: Store a GitHub PAT (github_pat_...) in .env and exchange it for a short-lived Copilot token (gnu_... or ghu_...) on demand (recommended for headless/CI).

Run this command in your project directory (where your docker-compose.yaml will be). It finds the token and updates/creates the .env file.

# Extract token and save to .env
export GITHUB_TOKEN=$(grep -o 'ghu_[a-zA-Z0-9]\+' ~/.config/Code/User/globalStorage/github.copilot/hosts.json | head -n 1)

# Check if token was found
if [ -z "$GITHUB_TOKEN" ]; then
  echo "Error: Copilot token not found. Please login to GitHub Copilot in VS Code."
else
  echo "Token found! Saving to .env..."
  # Create or update .env
  echo "GITHUB_TOKEN=$GITHUB_TOKEN" > .env
  echo "LITELLM_MASTER_KEY=sk-test-1234" >> .env # Add other defaults if needed
  echo "Success! .env updated."
fi

Alternative: Python Script

If you prefer a more robust script (e.g., if you have multiple accounts), save this as get_token.py:

import json
import os
from pathlib import Path

def get_token():
    # Path to VS Code Copilot config on Linux
    config_path = Path.home() / ".config/Code/User/globalStorage/github.copilot/hosts.json"

    if not config_path.exists():
        print("Config file not found.")
        return None

    try:
        with open(config_path, 'r') as f:
            data = json.load(f)
            # Find the first key containing github.com and get oauth_token
            for key, value in data.items():
                if "github.com" in key and "oauth_token" in value:
                    return value["oauth_token"]
    except Exception as e:
        print(f"Error: {e}")
        return None

token = get_token()
if token:
    print(f"GITHUB_TOKEN={token}")
else:
    print("Token not found")

Run it and append to .env:

python3 get_token.py >> .env

Option B: PAT (github_pat_...) → Copilot token (gnu_.../ghu_...) auto-refresh

Use this when VS Code isn't available (headless/CI). You store the PAT in .env, then run a command to fetch a short-lived Copilot token and write it back into .env as GITHUB_TOKEN.

0) Save the PAT in .env

# .env
GH_TOKEN=github_pat_xxx...
LITELLM_MASTER_KEY=sk-test-1234

Keep .env out of Git (.gitignore). GH_TOKEN is the long-lived PAT; GITHUB_TOKEN will be updated by the commands below.

cat > gnu_token <<'SH'
#!/usr/bin/env bash
set -euo pipefail

set -a
source .env
set +a

if [ -z "${GH_TOKEN:-}" ]; then
  echo "GH_TOKEN (github_pat_...) is missing in .env"
  exit 1
fi

COPILOT_TOKEN=$(curl -sS -X POST \
  -H "Authorization: token ${GH_TOKEN}" \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/copilot_internal/v2/token \
  | jq -r '.token')

if [ -z "${COPILOT_TOKEN}" ] || [ "${COPILOT_TOKEN}" = "null" ]; then
  echo "Failed to fetch Copilot token"
  exit 1
fi

awk -v token="$COPILOT_TOKEN" '
  BEGIN {found=0}
  /^GITHUB_TOKEN=/ {print "GITHUB_TOKEN=" token; found=1; next}
  {print}
  END {if (!found) print "GITHUB_TOKEN=" token}
' .env > .env.tmp && mv .env.tmp .env

echo "GITHUB_TOKEN updated (prefix: ${COPILOT_TOKEN:0:4}...)"
SH

chmod +x gnu_token
./gnu_token

2) curl (one-liner, no script)

set -a
source .env
set +a

COPILOT_TOKEN=$(curl -sS -X POST \
  -H "Authorization: token ${GH_TOKEN}" \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/copilot_internal/v2/token \
  | jq -r '.token')

awk -v token="$COPILOT_TOKEN" '
  BEGIN {found=0}
  /^GITHUB_TOKEN=/ {print "GITHUB_TOKEN=" token; found=1; next}
  {print}
  END {if (!found) print "GITHUB_TOKEN=" token}
' .env > .env.tmp && mv .env.tmp .env

3) Python (stdlib only)

cat > gnu_token.py <<'PY'
import json
import urllib.request
from pathlib import Path

env_path = Path(".env")
env = {}
for line in env_path.read_text().splitlines():
    if not line or line.startswith("#") or "=" not in line:
        continue
    k, v = line.split("=", 1)
    env[k] = v

gh_token = env.get("GH_TOKEN")
if not gh_token:
    raise SystemExit("GH_TOKEN (github_pat_...) is missing in .env")

req = urllib.request.Request(
    "https://api.github.com/copilot_internal/v2/token",
    method="POST",
    headers={
        "Authorization": f"token {gh_token}",
        "Accept": "application/vnd.github+json",
        "X-GitHub-Api-Version": "2022-11-28",
    },
)
with urllib.request.urlopen(req) as resp:
    data = json.load(resp)

token = data.get("token")
if not token:
    raise SystemExit("Failed to fetch Copilot token")

env["GITHUB_TOKEN"] = token
env_path.write_text("\n".join(f"{k}={v}" for k, v in env.items()) + "\n")
print("GITHUB_TOKEN updated")
PY

python3 gnu_token.py

4) Node.js (built-in fetch, Node 18+)

cat > gnu_token.mjs <<'JS'
import fs from "node:fs";

const envText = fs.readFileSync(".env", "utf8");
const env = {};
for (const line of envText.split(/\r?\n/)) {
  if (!line || line.startsWith("#") || !line.includes("=")) continue;
  const [k, ...rest] = line.split("=");
  env[k] = rest.join("=");
}

if (!env.GH_TOKEN) {
  console.error("GH_TOKEN (github_pat_...) is missing in .env");
  process.exit(1);
}

const res = await fetch("https://api.github.com/copilot_internal/v2/token", {
  method: "POST",
  headers: {
    Authorization: `token ${env.GH_TOKEN}`,
    Accept: "application/vnd.github+json",
    "X-GitHub-Api-Version": "2022-11-28",
  },
});

const data = await res.json();
if (!data.token) {
  console.error("Failed to fetch Copilot token");
  process.exit(1);
}

env.GITHUB_TOKEN = data.token;
fs.writeFileSync(
  ".env",
  Object.entries(env)
    .map(([k, v]) => `${k}=${v}`)
    .join("\n") + "\n",
);
console.log("GITHUB_TOKEN updated");
JS

node gnu_token.mjs

5) GitHub CLI (gh api)

set -a
source .env
set +a

COPILOT_TOKEN=$(GH_TOKEN="$GH_TOKEN" gh api --method POST \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  /copilot_internal/v2/token --jq '.token')

awk -v token="$COPILOT_TOKEN" '
  BEGIN {found=0}
  /^GITHUB_TOKEN=/ {print "GITHUB_TOKEN=" token; found=1; next}
  {print}
  END {if (!found) print "GITHUB_TOKEN=" token}
' .env > .env.tmp && mv .env.tmp .env

2. Docker Compose Configuration

Create docker-compose.yaml. This configuration simply passes the GITHUB_TOKEN from your .env file into the container.

version: "3.8"

services:
  litellm:
    image: ghcr.io/berriai/litellm:main-latest
    container_name: litellm-proxy
    ports:
      - "4000:4000"
    volumes:
      - ./config.yaml:/app/config.yaml
    env_file:
      - .env
    environment:
      # The token is passed from .env
      GITHUB_TOKEN: ${GITHUB_TOKEN}

      # Recommended settings
      LITELLM_LOG_LEVEL: INFO
    command: ["litellm", "--config", "/app/config.yaml", "--port", "4000"]
    restart: unless-stopped

3. LiteLLM Config (config.yaml)

Create config.yaml. We use os.environ/GITHUB_TOKEN to tell LiteLLM to use the environment variable we injected.

model_list:
  # GPT-4o via Copilot
  - model_name: copilot-gpt-4o
    litellm_params:
      model: github/gpt-4o
      api_key: os.environ/GITHUB_TOKEN

  # Claude 3.5 Sonnet via Copilot
  - model_name: copilot-claude-3.5
    litellm_params:
      model: github/claude-3.5-sonnet
      api_key: os.environ/GITHUB_TOKEN

general_settings:
  master_key: sk-test-1234  # Must match what you expect or set in .env

4. Run & Verify

  1. Generate .env (using the command from Step 1).
  2. Start Docker:
    docker-compose up -d
    
  3. Test:
    curl http://localhost:4000/chat/completions \
      -H "Authorization: Bearer sk-test-1234" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "copilot-gpt-4o",
        "messages": [{"role": "user", "content": "Hello!"}]
      }'
    

Troubleshooting

  • "Token not found": Ensure you have opened VS Code and the GitHub Copilot Chat extension is active and logged in.
  • Token Expiration: Copilot ghu_ tokens can expire. If auth fails, just restart VS Code (to refresh the token), run the extraction command again, and restart the container (docker-compose restart).