Skip to main content

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:

SurfaceHow a client authenticates to CortegaHow the upstream credential travels
LLMA 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.
MCPA 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).

  1. Choose the provider type (OpenAI, Anthropic, Gemini, DeepSeek, Groq, OpenRouter, or a custom OpenAI-compatible endpoint).
  2. Set Credential source to Client-supplied upstream credential.
  3. Set the endpoint or a base URL override if needed.
  4. 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

  1. Add or edit an MCP server (MCP → Add Server) and set Upstream Auth to Client-supplied upstream credential.
  2. 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-Authorization may 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 typeWhere it livesWho manages it
Provider key for a client-supplied-credential LLM providerClient / agent environmentThe client owner
MCP credential for pass-through MCP trafficMCP client environmentThe MCP client owner
Cortega LLM key / JWTClient environmentCortega admin issues it; it is the enforcement identity
Stored provider or MCP server credentialCortega or gateway runtimeCortega 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

  1. An LLM call with both headers succeeds and appears in Observability under the right team and model.
  2. The same call without X-Cortega-Upstream-Authorization fails closed (403).
  3. Guardrails still apply.
  4. A request carrying the upstream header cannot reach a stored-key provider unless the override is enabled.
  5. The upstream provider sees the client's key, not a Cortega-stored one.

Troubleshooting

SymptomLikely causeWhat to check
403 upstream_credential_requiredThe routed model's provider is client-supplied-credential and the request carried no upstream headerConfirm the client sends the upstream-credential header on every request; check the header name on Model Authorization
403 credential_substitution_forbiddenA request carrying the upstream header routed to a stored-key providerRoute to a client-supplied-credential provider, or deliberately enable the substitution override
Upstream provider rejects the request as unauthorizedThe client's own provider key is wrong or the header value is malformedThe value is the raw key; Cortega adds the Bearer prefix / x-api-key shaping
"Model not found"The requested model isn't registeredAdd the concrete model under Models → Models
MCP caller is denied "no MCP identity matches"The caller has no matched Cortega MCP identityIssue 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 serverSend the upstream-credential header; only route header-carrying requests to pass-through-safe servers