Bring-your-own upstream credential
How to let clients use their own upstream provider or MCP-server credentials while Cortega still governs the traffic, without Cortega storing those credentials.
Overview
Some teams already have provider API keys or MCP server credentials in their own clients and don't want to hand those to Cortega. Cortega supports that on both surfaces, and they work the same way:
| Surface | How a client authenticates to Cortega | How the upstream credential travels |
|---|---|---|
| LLM | A normal Cortega identity (LLM key or JWT), required. | A separate request header (X-Cortega-Upstream-Authorization by default). Cortega rewrites it to the provider-native header and strips it before the upstream call. |
| MCP | A normal Cortega identity (MCP key or JWT), required. | The same header. Cortega forwards its value as the upstream Authorization for a client-supplied-credential MCP server and strips the header before the call. |
In both cases Cortega still applies gateway policy, guardrails, observability, and the confused-deputy safety check below. It never stores the client's upstream credential.
Neither surface has a keyless mode. Every caller authenticates with a Cortega identity, so its traffic is attributed to a team and a tenant like any other. Only the upstream credential is supplied by the client, in a header.
LLM: client-supplied upstream credential
1. Configure the provider
Navigate to Models → Providers → Add provider (or use Guided setup).
- Choose the provider type (OpenAI, Anthropic, Gemini, DeepSeek, Groq, OpenRouter, or a custom OpenAI-compatible endpoint).
- Set Credential source to Client-supplied upstream credential.
- Set the endpoint or a base URL override if needed.
- Save.
Bedrock, Vertex, Azure, and Copilot are not supported for client-supplied credentials; they need SigV4 / OAuth signing that a raw per-request key can't satisfy.
2. Register the models
Navigate to Models → Models and add a concrete model for every model
name clients will request (gpt-4o, claude-sonnet-4-6, and so on).
There is no wildcard model; register the specific names. Two enabled
models may not share a routing name, even across providers.
3. Decide how clients reach the model
- Direct model selection on (Model Authorization tab): a client's LLM key can request any registered model by name.
- Direct model selection off (default): the identity's team needs a model authorization (routing preference) that includes the model.
The Guided setup wizard walks through provider, model, identity, and model authorization in one flow.
4. Calling the gateway
The client sends two headers: its Cortega identity in Authorization,
and its own provider key in the upstream-credential header.
curl "$CORTEGA_LLM_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $CORTEGA_LLM_KEY" \
-H "X-Cortega-Upstream-Authorization: $OPENAI_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "hello"}]}'
Cortega rewrites X-Cortega-Upstream-Authorization to what the provider
expects (Authorization: Bearer … for OpenAI-compatible providers,
x-api-key plus anthropic-version for Anthropic) and removes it before
the upstream call.
If a request routes to a client-supplied-credential provider and the
header is missing, Cortega fails closed with a 403
(upstream_credential_required). The request never reaches the provider.
Changing the header name
Model Authorization → Upstream credential header. Any valid HTTP
header name except the provider-native ones (Authorization,
x-api-key, x-goog-api-key, anthropic-version) and the x-cortega-*
namespace.
MCP: client-supplied upstream credential
- Add or edit an MCP server (MCP → Add Server) and set Upstream Auth to Client-supplied upstream credential.
- Issue an MCP identity for the client as usual (Identities), and grant it the server and tools it needs (MCP Authorization).
The client authenticates to the MCP listener with its Cortega identity and
sends its own MCP-server credential in the upstream-credential header
(same setting as LLM). Cortega forwards that value as the upstream
Authorization verbatim (the client controls whether it is Bearer … or
a raw token) and strips the header before the call.
If a request routes to a client-supplied-credential MCP server and the header is missing, Cortega denies it. The request never reaches the server.
A request that carries the upstream-credential header may only reach a pass-through-safe MCP server:
- servers with Upstream Auth = Client-supplied upstream credential
- stored-credential servers where an administrator has explicitly allowed credential substitution (see below)
For a server with Upstream Auth = None, Cortega strips both the
caller's Authorization and the upstream-credential header before the
call.
The confused-deputy guard and the substitution override
Cortega protects against a confused-deputy mistake: a caller must not be able to present its own credential (LLM) or an unvalidated one (MCP) and then have Cortega silently use its stored provider/MCP credential instead.
- LLM: a request that carried
X-Cortega-Upstream-Authorizationmay only reach a client-supplied-credential provider. If it routes to a stored-key provider, Cortega denies it 403 unless the override below is on. The header is stripped before dispatch either way. - MCP: a pass-through caller may only reach a pass-through-safe server.
The override. When editing a stored-credential provider or MCP server you may see "Allow a request that carried its own upstream credential to use this provider's stored credential instead." Leave it off unless you deliberately want that. Turning it on is a privilege escalation.
Security key management
| Key type | Where it lives | Who manages it |
|---|---|---|
| Provider key for a client-supplied-credential LLM provider | Client / agent environment | The client owner |
| MCP credential for pass-through MCP traffic | MCP client environment | The MCP client owner |
| Cortega LLM key / JWT | Client environment | Cortega admin issues it; it is the enforcement identity |
| Stored provider or MCP server credential | Cortega or gateway runtime | Cortega administrator |
Operational guidance:
- Rotate client-owned provider keys outside Cortega.
- Do not paste a client-supplied provider key into a Cortega provider form.
- Review stored-credential providers and MCP servers before enabling the substitution override.
What to verify
- An LLM call with both headers succeeds and appears in Observability under the right team and model.
- The same call without
X-Cortega-Upstream-Authorizationfails closed (403). - Guardrails still apply.
- A request carrying the upstream header cannot reach a stored-key provider unless the override is enabled.
- The upstream provider sees the client's key, not a Cortega-stored one.
Troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
403 upstream_credential_required | The routed model's provider is client-supplied-credential and the request carried no upstream header | Confirm the client sends the upstream-credential header on every request; check the header name on Model Authorization |
403 credential_substitution_forbidden | A request carrying the upstream header routed to a stored-key provider | Route to a client-supplied-credential provider, or deliberately enable the substitution override |
| Upstream provider rejects the request as unauthorized | The client's own provider key is wrong or the header value is malformed | The value is the raw key; Cortega adds the Bearer prefix / x-api-key shaping |
| "Model not found" | The requested model isn't registered | Add the concrete model under Models → Models |
| MCP caller is denied "no MCP identity matches" | The caller has no matched Cortega MCP identity | Issue an MCP identity and match attributes; there is no keyless MCP mode |
| MCP caller is denied "requires an upstream credential" / "may not use Cortega's stored credential" | Missing upstream header for a client-supplied-credential server, or the header was sent toward a stored-key server | Send the upstream-credential header; only route header-carrying requests to pass-through-safe servers |