Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist/
config.yaml
*.log
.env
.worktrees/
109 changes: 93 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,34 @@

[中文](./README_CN.md)

A lightweight single-account Claude OAuth to API proxy for Claude Code and OpenAI-compatible clients.
A lightweight dual-provider API proxy for Claude Code and OpenAI-compatible clients.

auth2api is intentionally small and focused:

- one Claude OAuth account
- one Claude OAuth account at most
- one local Codex login reused from `~/.codex/auth.json`
- one local or self-hosted proxy
- one simple goal: turn Claude OAuth access into a usable API endpoint
- one simple goal: turn local Claude/Codex auth into usable API endpoints

It is not trying to be a multi-provider gateway or a large routing platform. If you want a compact, understandable proxy that is easy to run and modify, auth2api is built for that use case.
It is still intentionally not a multi-account pool or a large routing platform. If you want a compact, understandable proxy that is easy to run and modify, auth2api is built for that use case.

## Features

- **Lightweight by design** — small codebase, single-account architecture, minimal moving parts
- **Claude OAuth to API** — use one Claude OAuth login as an API-backed proxy account
- **Claude + Codex** — serves Claude OAuth and local Codex auth from one process
- **OpenAI-compatible API** — supports `/v1/chat/completions`, `/v1/responses`, and `/v1/models`
- **Model-based routing** — `claude-*` stays on Claude, `gpt-*` / `o*` / `codex-*` route to Codex
- **Claude native passthrough** — supports `/v1/messages` and `/v1/messages/count_tokens`
- **Claude Code friendly** — works with both `Authorization: Bearer` and `x-api-key`
- **Streaming, tools, images, and reasoning** — covers the main Claude usage patterns without a large framework
- **Single-account health handling** — cooldown, retry, token refresh, and `/admin/accounts` status
- **Provider-aware status** — Claude account health plus Codex auth status in `/admin/accounts`
- **Basic safety defaults** — timing-safe API key validation, per-IP rate limiting, localhost-only browser CORS

## Requirements

- Node.js 20+
- A Claude account (Claude Max subscription recommended)
- A Claude account if you want Claude models (Claude Max subscription recommended)
- A local Codex login if you want Codex models (`~/.codex/auth.json`)

## Installation

Expand All @@ -37,8 +40,21 @@ npm install
npm run build
```

## Quick start

1. Copy `config.example.yaml` to `config.yaml`.
2. Set at least one API key under `api-keys`.
3. Pick one or both providers:
- Codex only: make sure `~/.codex/auth.json` already exists, then set `codex.models`.
- Claude: run `node dist/index.js --login` once.
4. Start the server with `node dist/index.js`.

If you start with Codex only, Claude routes such as `/v1/messages` remain unavailable until you complete the Claude login flow.

## Login

Claude models still use auth2api's built-in OAuth login flow. Codex models do not have a separate login flow here — auth2api reuses the local Codex session from `~/.codex/auth.json`.

### Auto mode (requires local browser)

```bash
Expand All @@ -63,6 +79,13 @@ node dist/index.js

The server starts on `http://127.0.0.1:8317` by default. On first run, an API key is auto-generated and saved to `config.yaml`.

The process can start with either provider:

- Claude available via `node dist/index.js --login`
- Codex available via an existing `~/.codex/auth.json`

If neither a Claude token nor a usable Codex configuration is available, auth2api exits at startup instead of serving partial misconfiguration.

If the configured Claude account is temporarily cooled down after upstream rate limiting, auth2api now returns `429 Rate limited on the configured account` instead of a generic `503`.

## Configuration
Expand All @@ -73,7 +96,7 @@ Copy `config.example.yaml` to `config.yaml` and edit as needed:
host: "" # bind address, empty = 127.0.0.1
port: 8317

auth-dir: "~/.auth2api" # where OAuth tokens are stored
auth-dir: "~/.auth2api" # where Claude OAuth tokens are stored

api-keys:
- "your-api-key-here" # clients use this to authenticate
Expand All @@ -96,12 +119,28 @@ timeouts:
messages-ms: 120000
stream-messages-ms: 600000
count-tokens-ms: 30000

codex:
enabled: true
auth-file: "~/.codex/auth.json"
models:
- "gpt-5.4"
- "o3"
- "codex-mini-latest"
```

By default, streaming upstream requests are allowed to run for 10 minutes before auth2api aborts them.

The default request body limit is `200mb`, which is more suitable for large Claude Code contexts than the previous fixed `20mb`.

Codex models exposed by `/v1/models` come from `codex.models`. Claude models are built in.

Important routing semantics:

- `codex.enabled: false` disables all Codex routing.
- `codex.models` is both the `/v1/models` output and the runtime allowlist for Codex requests.
- A `gpt-*`, `o*`, or `codex-*` request for a model not listed in `codex.models` returns `400 Unsupported model`.

`debug` now supports three levels:
- `off`: no extra logs
- `errors`: log upstream/network failures and upstream error bodies
Expand All @@ -122,8 +161,30 @@ curl http://127.0.0.1:8317/v1/chat/completions \
}'
```

`/v1/chat/completions` and `/v1/responses` route automatically by `model`:

- `claude-*` -> Claude provider
- `gpt-*`, `o*`, `codex-*` -> Codex provider

Unsupported or disallowed models return `400 Unsupported model`.

Example Codex request:

```bash
curl http://127.0.0.1:8317/v1/chat/completions \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4",
"messages": [{"role": "user", "content": "Summarize this repo."}],
"stream": false
}'
```

### Available models

Claude models built into auth2api:

| Model ID | Description |
|----------|-------------|
| `claude-opus-4-6` | Claude Opus 4.6 |
Expand All @@ -137,16 +198,18 @@ Short convenience aliases accepted by auth2api:
- `sonnet` -> `claude-sonnet-4-6`
- `haiku` -> `claude-haiku-4-5-20251001`

Codex models are configured explicitly in `config.yaml` under `codex.models`. Only models listed there are returned by `/v1/models` and accepted at runtime.

### Endpoints

| Endpoint | Description |
|----------|-------------|
| `POST /v1/chat/completions` | OpenAI-compatible chat |
| `POST /v1/responses` | OpenAI Responses API compatibility |
| `POST /v1/messages` | Claude native passthrough |
| `POST /v1/messages/count_tokens` | Claude token counting |
| `POST /v1/chat/completions` | OpenAI-compatible chat, routed by model |
| `POST /v1/responses` | OpenAI Responses API compatibility, routed by model |
| `POST /v1/messages` | Claude native passthrough, Claude-only |
| `POST /v1/messages/count_tokens` | Claude token counting, Claude-only |
| `GET /v1/models` | List available models |
| `GET /admin/accounts` | Account health/status (API key required) |
| `GET /admin/accounts` | Claude + Codex provider status (API key required) |
| `GET /health` | Health check |

## Docker
Expand All @@ -169,6 +232,12 @@ Or with docker-compose:
docker-compose up -d
```

Container notes:

- If you want Claude login persistence in Docker, set `auth-dir: "/data"` in `config.yaml`.
- If you want Codex models in Docker, mount the host auth file to the same path configured by `codex.auth-file`, for example `-v ~/.codex/auth.json:/root/.codex/auth.json:ro`.
- If you change the in-container path, update `codex.auth-file` to match.

## Use with Claude Code

Set `ANTHROPIC_BASE_URL` to point Claude Code at auth2api:
Expand All @@ -183,12 +252,14 @@ Claude Code uses the native `/v1/messages` endpoint which auth2api passes throug

## Single-account mode

This proxy supports exactly one Claude OAuth account at a time.
Claude token storage remains single-account mode:

- Running `--login` again refreshes the stored token for the same account.
- If a different account is already stored, auth2api refuses to overwrite it and asks you to remove the existing token first.
- If more than one token file exists in the auth directory, auth2api exits with an error until you clean up the extra files.

Codex auth is separate: auth2api only reads the local `~/.codex/auth.json` file and does not manage Codex login itself.

## Admin status

Use `/admin/accounts` with your configured API key to inspect the current account state:
Expand All @@ -198,11 +269,17 @@ curl http://127.0.0.1:8317/admin/accounts \
-H "Authorization: Bearer <your-api-key>"
```

The response includes account availability, cooldown, failure counters, last refresh time, and basic request statistics.
The response includes legacy Claude account snapshots plus separate `claude` and `codex` provider sections so you can see provider availability independently.

Useful fields:

- `claude.available`: whether a Claude account is currently usable
- `codex.available`: whether Codex auth plus model configuration is usable
- `codex.details.error`: why Codex is currently unavailable, for example missing auth file or empty model configuration

## Smoke tests

A minimal automated smoke test suite is included and uses mocked upstream responses, so it does not call the real Claude service:
A minimal automated smoke test suite is included and uses mocked upstream responses, so it does not call the real Claude or Codex services:

```bash
npm run test:smoke
Expand Down
Loading