Compatibility layer for using OpenAI sign-in with LibreChat.
  • Python 98.3%
  • Dockerfile 1.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-04 11:22:12 -07:00
app Add ChatGPT OAuth LibreChat proxy 2026-10-04 10:34:50 -07:00
tests Add ChatGPT OAuth LibreChat proxy 2026-10-04 10:34:50 -07:00
.env.example fix port conflict 2026-10-04 11:22:12 -07:00
.gitignore Add ChatGPT OAuth LibreChat proxy 2026-10-04 10:34:50 -07:00
compose.yaml fix port conflict 2026-10-04 11:22:12 -07:00
Dockerfile Add ChatGPT OAuth LibreChat proxy 2026-10-04 10:34:50 -07:00
pyproject.toml Add ChatGPT OAuth LibreChat proxy 2026-10-04 10:34:50 -07:00
README.md fix port conflict 2026-10-04 11:22:12 -07:00

ChatGPT OAuth Proxy for LibreChat

Small proof-of-concept OpenAI-compatible proxy for LibreChat. It translates POST /v1/chat/completions requests into the public OpenAI Responses API using the officially supported Sign in with ChatGPT flow for open-source apps.

It uses a ChatGPT subscription's eligible plan usage, not a normal OpenAI API key. It does not use browser cookies, ChatGPT private endpoints, session tokens, scraped credentials, or another app's OAuth client credentials.

Architecture

LibreChat -> http://chatgpt-oauth-proxy:8080/v1 -> public /v1/responses
                                                    ^
                                         ChatGPT-plan OAuth access token

OpenAI requires store: false and stream: true for this flow. The proxy therefore always streams upstream. For a stream: false Chat Completions request it consumes that supported stream and returns a regular Chat Completions object; for stream: true it translates events without buffering the completion.

Official behavior verified

Implementation follows these current official documents:

Important restrictions from OpenAI:

  • OAuth is dynamic public-client registration using client_id=dynamic_agent_client, PKCE, a saved ext_agent_host_id, and no client secret/API key.
  • The callback must be http://127.0.0.1:<port>/auth/callback; localhost and a remote callback are not supported.
  • /v1/models returns the signed-in account's catalog and only models with visibility: list.
  • Inference uses only https://api.openai.com/v1/responses, never ChatGPT backend-api endpoints.
  • HTTP requests must include complete history in input; previous_response_id, persistent conversations, temperature, top_p, and many other Responses options are unsupported.

Quick start

  1. Create a Docker network shared with LibreChat, if needed:
docker network create librechat
  1. Create .env from .env.example and set a long random PROXY_SHARED_SECRET. For remote workstation setup, set PROXY_BIND_ADDRESS to the Docker host's private-LAN IP. The default published port is 18080, avoiding a common HTTPS reverse-proxy collision on port 8080. This proxy itself serves HTTP only and should remain private.

  2. Build and start the proxy:

docker compose up --build -d
  1. Query the host ID. This is not secret; it must be preserved when credentials are imported. If ADMIN_SECRET is configured, send it as a bearer token. Without it, management status is intentionally available only on the private Docker/LAN network.
curl http://SERVER:18080/admin/auth/status
  1. On the workstation with the browser, clone this project or install its Python dependencies, then run the local loopback login. Substitute the host ID returned above. This opens only the official OpenAI authorization page and receives the callback on the workstation's 127.0.0.1.
python -m pip install -e '.[dev]'
chatgpt-oauth-login --host-id 'urn:uuid:SERVER_HOST_ID' --credentials ./credentials.json
  1. Transfer that protected file through SSH to the running container volume. This is the exact workflow OpenAI documents for a self-hosted VM: login locally, transfer the selected registration's credential file securely, and keep the VM host ID.
docker compose cp ./credentials.json chatgpt-oauth-proxy:/data/credentials.json
docker compose restart chatgpt-oauth-proxy

The container automatically replaces the imported file's host ID with the ID persisted in /data/host_id.txt, so an imported workstation identity cannot overwrite the server host identity. The credential file is mode 0600; updates use write-then-atomic-replace so refresh-token rotation is saved together.

Confirm login and model discovery:

curl -H 'Authorization: Bearer YOUR_PROXY_SHARED_SECRET' http://SERVER:18080/v1/models
curl -N http://SERVER:18080/v1/chat/completions \
  -H 'Authorization: Bearer YOUR_PROXY_SHARED_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{"model":"MODEL_FROM_MODELS","stream":true,"messages":[{"role":"user","content":"Say hello."}]}'

LibreChat configuration

LibreChat's current custom-endpoint documentation requires name, apiKey, baseURL, and models; fetch: true tells it to use this proxy's /v1/models response. Add this to the mounted librechat.yaml:

version: 1.3.17
endpoints:
  custom:
    - name: 'ChatGPT Plan'
      apiKey: '${CHATGPT_PROXY_SECRET}'
      baseURL: 'http://chatgpt-oauth-proxy:8080/v1'
      models:
        fetch: true
        default: []
      titleConvo: true
      titleModel: 'current_model'
      dropParams: ['temperature', 'top_p', 'max_tokens', 'stop']
      modelDisplayLabel: 'ChatGPT Plan'

Add CHATGPT_PROXY_SECRET with the same value as PROXY_SHARED_SECRET to LibreChat's .env, mount librechat.yaml into its API container as documented by LibreChat, then restart LibreChat. The proxy container and LibreChat API container must share the librechat Docker network.

API and compatibility

  • GET /health: unauthenticated health and OAuth availability.
  • GET /v1/models: account-specific supported models.
  • POST /v1/chat/completions: text system/developer, user, assistant, and complete multi-turn history; stream: true SSE translation and a practical stream: false response.
  • GET /admin/auth/status: host ID and login state. Protect it with ADMIN_SECRET if it is reachable beyond the private network.

The proxy returns OpenAI-style JSON errors for an invalid local key, absent or expired credentials, refresh failure, malformed input, models/inference upstream errors, and timeouts. It logs request IDs, endpoint, status, model discovery/inference failures, refresh events, and latency. It never logs tokens or request contents.

Limitations

  • This is single-account and single-host PoC storage. It intentionally does not expose a multi-user account picker.
  • OpenAI's SIWC HTTP preview does not allow temperature, top_p, max_output_tokens, persistent Responses conversations, background jobs, or hosted tools such as image generation, file search, Code Interpreter, hosted MCP, or computer use.
  • The OpenAI Chat Completions flat tools shape is rejected. SIWC supports function/custom tools only with Responses namespaces or additional_tools input items, which cannot be faithfully inferred from a flat Chat Completions request. Assistant tool-call history is accepted for continuing an existing transcript, but this facade does not advertise tools to a model.
  • Only text content is translated. Although SIWC may support images/files for an eligible model, this focused LibreChat facade does not implement multipart/file handling.
  • A Plus account's five-hour plan limit is shared across apps; an upstream usage limit can arrive after streaming begins and is emitted as an SSE error followed by [DONE].
  • Do not expose the inference or admin endpoint publicly. Keep it on the Docker/LAN network behind LibreChat/Pangolin as described.

Operations

Inspect logs:

docker compose logs -f chatgpt-oauth-proxy

Restarting preserves /data and authentication. Access tokens refresh automatically just before expiry using the issued dynamic client ID; a returned refresh token replaces the old token atomically.

To reauthenticate, repeat the local login/import workflow. To remove local access, stop the proxy and delete credentials.json from its /data volume. For complete remote revocation, disconnect the app in ChatGPT Settings; OpenAI's documented revocation endpoint requires the saved refresh token and issued client ID, which this focused PoC does not expose as an HTTP operation to avoid placing renewal credentials on the network.

Development

python -m pip install -e '.[dev]'
pytest
docker compose build

Tests mock OAuth and cover conversion, SSE translation, atomic credential storage, refresh-token rotation, invalid local API keys, and health. They do not require a ChatGPT account.