# How to Set Up OAuth Integration with Codex and Claude Code in AxonHub

> Discover how to set up OAuth integration with Codex and Claude Code. AxonHub uses PKCE-based flows for secure access token exchange. Get started now.

- Repository: [Loop/axonhub](https://github.com/looplj/axonhub)
- Tags: how-to-guide
- Published: 2026-03-06

---

**AxonHub implements PKCE-based OAuth flows for both OpenAI Codex and Anthropic Claude Code, exposing REST endpoints to start sessions and exchange callbacks for access tokens.**

AxonHub provides native OAuth integration with Codex and Claude Code through a unified PKCE (Proof-Key for Code Exchange) architecture. This implementation eliminates the need to store client secrets server-side while securely managing access tokens for LLM transformations. The following guide covers the complete setup process using AxonHub's administrative API endpoints.

## Understanding the OAuth Flow Architecture

The OAuth implementation in AxonHub follows a standardized five-step PKCE pattern that applies to both providers. According to the source code in [`internal/server/api/codex.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/codex.go) and [`internal/server/api/claudecode.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/claudecode.go), the flow generates cryptographically secure parameters before redirecting users to the provider's consent screen.

### PKCE Implementation Details

When initiating a session, AxonHub generates a random 64-byte `code_verifier` and derives a SHA-256 `code_challenge`. The system also creates a unique `state` parameter to prevent CSRF attacks. These values are temporarily cached using the `xcache` abstraction defined in [`internal/pkg/xcache/cache.go`](https://github.com/looplj/axonhub/blob/main/internal/pkg/xcache/cache.go) with a 10-minute expiration window.

### State Management and Caching

The OAuth state persists in AxonHub's cache layer during the handshake period. This allows the system to validate the callback integrity when the user returns from the provider's authorization server. The cache implementation supports both in-memory and Redis-backed configurations depending on your deployment topology.

## API Endpoints for Codex OAuth

AxonHub exposes dedicated REST endpoints under `/admin/codex/oauth/` for managing OpenAI Codex authentication. These routes are registered in [`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go) and handled by the functions in [`internal/server/api/codex.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/codex.go).

### Starting the Flow

To initiate OAuth with Codex, send a POST request to the start endpoint:

```bash
curl -X POST http://localhost:8090/admin/codex/oauth/start \
  -H "Content-Type: application/json" \
  -d '{}'

```

The response contains a `session_id` and the `auth_url` where you must redirect the user:

```json
{
  "session_id": "bFz1V8...5gA",
  "auth_url": "https://codex.ai/oauth/authorize?response_type=code&client_id=...&code_challenge=...&state=bFz1V8...5gA"
}

```

### Exchanging the Callback

After the user consents, Codex redirects to your configured `RedirectURI` with query parameters containing `code` and `state`. Exchange these for credentials by calling:

```bash
curl -X POST http://localhost:8090/admin/codex/oauth/exchange \
  -H "Content-Type: application/json" \
  -d '{
        "session_id": "bFz1V8...5gA",
        "callback_url": "https://your-app/callback?code=XYZ&state=bFz1V8...5gA"
      }'

```

The `codex.NewTokenProvider` function in [`llm/transformer/openai/codex/token.go`](https://github.com/looplj/axonhub/blob/main/llm/transformer/openai/codex/token.go) handles the actual token exchange with OpenAI's endpoint. The response returns a JSON credentials string:

```json
{
  "credentials": "{\"access_token\":\"eyJ...\",\"refresh_token\":\"r1...\",\"expires_in\":3600}"
}

```

## API Endpoints for Claude Code OAuth

Anthropic Claude Code integration follows an identical pattern but requires special handling for state parameters encoded in URL fragments. The implementation resides in [`internal/server/api/claudecode.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/claudecode.go) with token exchange logic in [`llm/transformer/anthropic/claudecode/token_provider.go`](https://github.com/looplj/axonhub/blob/main/llm/transformer/anthropic/claudecode/token_provider.go).

### Fragment-Based State Handling

Claude Code returns the `state` parameter in the URL fragment (after the `#` symbol) rather than query parameters. AxonHub's `parseClaudeCodeCallbackURL` function checks both the fragment and query string as a fallback to maintain compatibility.

Initiate the flow using the Claude Code specific endpoint:

```bash
curl -X POST http://localhost:8090/admin/claudecode/oauth/start \
  -H "Content-Type: application/json" \
  -d '{}'

```

The response structure mirrors the Codex format:

```json
{
  "session_id": "hJk9pW2...xYz",
  "auth_url": "https://claude.ai/oauth/authorize?response_type=code&client_id=...&code_challenge=...&state=hJk9pW2...xYz"
}

```

### Token Exchange

When exchanging the callback for Claude Code, ensure your `callback_url` includes the fragment containing the state:

```bash
curl -X POST http://localhost:8090/admin/claudecode/oauth/exchange \
  -H "Content-Type: application/json" \
  -d '{
        "session_id": "hJk9pW2...xYz",
        "callback_url": "https://your-app/callback?code=ABC#hJk9pW2...xYz"
      }'

```

The `claudecode.NewTokenProvider` handles the exchange with Anthropic's OAuth server, returning credentials in the same JSON format as Codex.

## Token Provider Implementation

Both OAuth flows rely on dedicated token providers that encapsulate provider-specific HTTP logic. The **Codex** provider in [`llm/transformer/openai/codex/token.go`](https://github.com/looplj/axonhub/blob/main/llm/transformer/openai/codex/token.go) and the **Claude Code** provider in [`llm/transformer/anthropic/claudecode/token_provider.go`](https://github.com/looplj/axonhub/blob/main/llm/transformer/anthropic/claudecode/token_provider.go) implement the same interface but target different token endpoints.

These providers support optional HTTP proxy configuration through the [`llm/httpclient/client.go`](https://github.com/looplj/axonhub/blob/main/llm/httpclient/client.go) abstraction. Set the `proxy` field in your request configuration to route OAuth traffic through corporate proxies or regional gateways.

## Configuration and Proxy Support

AxonHub's OAuth implementation supports enterprise network configurations through the HTTP client layer defined in [`llm/httpclient/client.go`](https://github.com/looplj/axonhub/blob/main/llm/httpclient/client.go). When initializing either token provider, you can specify an optional `proxy` parameter to route token exchange requests through a custom HTTP proxy.

This configuration is particularly useful when running AxonHub in restricted network environments where direct access to OpenAI or Anthropic endpoints requires traffic inspection or regional routing.

## Summary

- AxonHub implements **PKCE-based OAuth** for both OpenAI Codex and Anthropic Claude Code, eliminating the need for client secrets.
- The flow uses **POST /admin/codex/oauth/start** and **POST /admin/claudecode/oauth/start** to initiate sessions, returning authorization URLs.
- Callback handling differs between providers: **Codex** uses query parameters while **Claude Code** uses URL fragments for state validation.
- Exchange endpoints (**POST /admin/{provider}/oauth/exchange**) convert callback URLs to JSON credentials using provider-specific **TokenProvider** implementations.
- Both flows support **HTTP proxy configuration** through the shared HTTP client abstraction for enterprise network deployments.

## Frequently Asked Questions

### How does AxonHub handle the state parameter differently for Claude Code compared to Codex?

AxonHub expects the `state` parameter in the URL fragment (after the `#` symbol) for Claude Code callbacks, whereas Codex returns state as a standard query parameter. The `parseClaudeCodeCallbackURL` function in [`internal/server/api/claudecode.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/claudecode.go) checks both locations to maintain compatibility with different redirect scenarios.

### What is the expiration time for OAuth state sessions in AxonHub?

OAuth state parameters are cached for **10 minutes** using the `xcache` abstraction defined in [`internal/pkg/xcache/cache.go`](https://github.com/looplj/axonhub/blob/main/internal/pkg/xcache/cache.go). This window allows sufficient time for users to complete the provider's consent screen while minimizing the risk of replay attacks.

### Can I route OAuth token exchanges through a corporate proxy?

Yes. Both the Codex and Claude Code token providers support optional HTTP proxy configuration through the [`llm/httpclient/client.go`](https://github.com/looplj/axonhub/blob/main/llm/httpclient/client.go) client. When initializing the OAuth flow, include the `proxy` field in your configuration to route token exchange traffic through your specified proxy server.

### Where are the OAuth route handlers registered in the AxonHub server?

The REST endpoints for OAuth are registered in [`internal/server/routes.go`](https://github.com/looplj/axonhub/blob/main/internal/server/routes.go). This file maps `POST /admin/codex/oauth/start`, `POST /admin/codex/oauth/exchange`, and their Claude Code equivalents to the handler functions defined in [`internal/server/api/codex.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/codex.go) and [`internal/server/api/claudecode.go`](https://github.com/looplj/axonhub/blob/main/internal/server/api/claudecode.go) respectively.