Skip to content

Authentication

LLMG has two distinct authentication layers. Understanding the difference is critical to avoiding authentication_error responses.

┌──────────┐ Authorization: Bearer <token> ┌──────────────┐ OPENAI_API_KEY=sk-... ┌──────────┐
│ Client │ ──────────────────────────────────►│ LLMG Gateway │ ─────────────────────────►│ Provider │
└──────────┘ Layer 1: Gateway Auth └──────────────┘ Layer 2: Provider Auth └──────────┘
LayerPurposeConfigured byValidated?
Gateway AuthControls who can access the gatewayClient sends Authorization headerCurrently: presence-only (any Bearer token accepted)
Provider AuthAuthenticates with upstream LLM providersServer-side env vars / llmg.tomlYes — validated by the provider API

Layer 1: Gateway Authentication (Client → Gateway)

Section titled “Layer 1: Gateway Authentication (Client → Gateway)”

The gateway requires an Authorization: Bearer <token> header on every request except /health.

What works:

Terminal window
# Any Bearer token is accepted
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer any-value-here" \
-H "Content-Type: application/json" \
-d '{"model": "openai/gpt-4", "messages": [{"role": "user", "content": "Hi"}]}'

What fails:

Terminal window
# Missing header entirely → 401 authentication_error
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "openai/gpt-4", "messages": [{"role": "user", "content": "Hi"}]}'
# Wrong format (not "Bearer ...") → 401 authentication_error
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Basic dXNlcjpwYXNz" \
-H "Content-Type: application/json" \
-d '{"model": "openai/gpt-4", "messages": [{"role": "user", "content": "Hi"}]}'

When the header is missing or malformed, you’ll receive:

{
"error": {
"message": "Invalid authentication",
"type": "authentication_error"
}
}

HTTP status: 401 Unauthorized

The following endpoints do not require an Authorization header:

EndpointMethodDescription
/healthGETHealth check

Layer 2: Provider Authentication (Gateway → Provider)

Section titled “Layer 2: Provider Authentication (Gateway → Provider)”

Provider API keys are configured on the server side and are never sent by the client. The gateway injects the correct credentials when forwarding requests upstream.

Environment variables (recommended):

Terminal window
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GROQ_API_KEY=gsk_...

Config file (llmg.toml):

[providers.openai]
enabled = true
api_key = "${OPENAI_API_KEY}" # References env var
[providers.anthropic]
enabled = true
api_key = "sk-ant-hardcoded" # Or hardcode (not recommended)

The ${ENV_VAR} syntax in the config file expands environment variables at startup. If the env var is not set, the value is used as-is.

  1. If api_key is set in llmg.toml, that value is used (with ${...} expansion).
  2. If api_key is not set in the config, the gateway checks for the conventional env var (e.g., OPENAI_API_KEY for the openai provider).
  3. If neither is found, the provider is skipped during registration and requests to it return Unknown provider.

If a provider key is invalid or expired, you’ll see a provider_error (not authentication_error):

{
"error": {
"message": "Authentication failed",
"type": "provider_error"
}
}

HTTP status: 401 Unauthorized

This is distinct from the gateway-level authentication_error — it means the gateway accepted your request but the upstream provider rejected the key.


The gateway is designed to work with the OpenAI SDK. Set api_key to any value (it just needs to be present to satisfy the SDK’s own validation):

Python:

from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8080/v1",
api_key="any-value", # Gateway doesn't validate the token (yet)
)
response = client.chat.completions.create(
model="openai/gpt-4",
messages=[{"role": "user", "content": "Hello!"}],
)

Node.js:

import OpenAI from "openai";
const client = new OpenAI({
baseURL: "http://localhost:8080/v1",
apiKey: "any-value",
});
const response = await client.chat.completions.create({
model: "openai/gpt-4",
messages: [{ role: "user", content: "Hello!" }],
});

The planned configuration will look like:

[server.auth]
# Enable or disable gateway auth entirely
enabled = true
# Accepted gateway API keys
# Clients must send one of these as "Authorization: Bearer <key>"
keys = [
"${LLMG_API_KEY}",
"${LLMG_API_KEY_2}",
]

Planned behavior:

enabledkeysResult
falseNo auth required, any client can access the gateway
trueempty []Current behavior — any Bearer token accepted
true["sk-gw-abc"]Only requests with Bearer sk-gw-abc are accepted
true["key1", "key2"]Multiple keys supported (e.g., per-team or per-environment)

This will allow the gateway to be locked down in production while remaining easy to use in development.


SymptomCauseFix
authentication_errorMissing Authorization: Bearer ... headerAdd -H "Authorization: Bearer any-token" to your request
Unknown provider: xyzProvider not enabled or API key missingSet the provider’s env var or enable it in llmg.toml
Authentication failed (provider_error)Provider API key is invalidCheck / rotate the provider’s API key (e.g., OPENAI_API_KEY)