> ## Documentation Index
> Fetch the complete documentation index at: https://docs.horizon.prefect.io/llms.txt
> Use this file to discover all available pages before exploring further.

# External authentication

> Configure OAuth and API-key credentials for external servers and hosted delegated authorization.

export const externalServersPlans = "Developer and Enterprise";

<Note>
  External authentication is available on {externalServersPlans}.
</Note>

External authentication controls the credential that Horizon sends to a
downstream service. The MCP client first proves who the caller is to Horizon.
Horizon then sends an OAuth token or API key for that caller, or sends no
credential when a remote MCP server accepts anonymous requests.

For credential-based modes, an **external auth configuration** is the saved
definition for that second credential. The Horizon dashboard presents it as an
**auth source**. It records how to acquire and inject credentials, while each
user's OAuth grant or API key is stored separately. The no-authentication mode
does not create an auth source.

Horizon uses external auth configurations in two places:

| Server type     | How external authentication is used                                                                                                          |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| External server | Horizon authenticates to the remote MCP server after accepting the caller at the Horizon gateway.                                            |
| Hosted server   | Delegated authorization replaces the caller's Horizon credential with a downstream credential before the request reaches hosted server code. |

The two credentials serve different trust boundaries:

| Boundary                          | Credential                                                                                   | Purpose                                                                     |
| --------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| MCP client to Horizon             | Horizon sign-in token or Horizon API key                                                     | Identifies the caller and lets the gateway check server access.             |
| Horizon to the downstream service | OAuth token or API key from the auth source, or no credential for a public remote MCP server | Authorizes the downstream request when the service requires authentication. |

For the first boundary, see [Authentication](/platform/authentication). Server
roles and capability policies are covered in
[Authorization](/platform/authorization).

## Authentication modes

Choose the mode based on whether the downstream provider requires a credential
and who owns that credential.

| Mode              | Credential owner | Setup behavior                                                                                          | Best fit                                                           |
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| No authentication | None             | Horizon sends the downstream request without an `Authorization` header.                                 | Public remote MCP servers that accept anonymous requests.          |
| OAuth             | Each user        | Each user completes the provider's authorization flow. Horizon stores and refreshes that user's tokens. | Providers with OAuth authorization-code support.                   |
| Per-user API key  | Each user        | Each user enters their own token.                                                                       | Providers that issue personal tokens but do not support OAuth.     |
| Shared API key    | Organization     | A server administrator enters one token for all users.                                                  | Service credentials whose downstream permissions should be shared. |

No authentication applies only to external servers. Horizon still authenticates
and authorizes the caller at the Horizon endpoint, but it does not forward the
caller's Horizon credential to the remote MCP server.

<Warning>
  Select no authentication only when the remote MCP server accepts anonymous
  requests. Horizon sends no downstream `Authorization` header in this mode.
</Warning>

OAuth and per-user API keys preserve the downstream user's identity and
permissions. A shared API key makes every forwarded request act as the same
upstream principal, even though Horizon continues to identify and authorize the
original caller at its own gateway.

<Warning>
  A shared API key gives every authorized caller the downstream permissions of
  the shared upstream principal. Scope that principal to only the access the
  server needs.
</Warning>

## OAuth setup

Horizon starts OAuth setup with the provider or remote MCP URL. It follows
protected-resource and authorization-server metadata to locate the OAuth
endpoints.

| Provider capability                                | Horizon behavior                                                             |
| -------------------------------------------------- | ---------------------------------------------------------------------------- |
| OAuth metadata with Dynamic Client Registration    | Horizon discovers the metadata and registers its OAuth client automatically. |
| OAuth metadata without Dynamic Client Registration | Horizon prefills discovered endpoints and asks for a pre-registered client.  |
| No usable OAuth metadata                           | You provide the OAuth client and endpoint details manually.                  |
| Dynamic Client Registration fails                  | You can continue with a pre-registered OAuth client.                         |

When manual setup is required, register Horizon's callback URL with the
provider, then enter the resulting client details in Horizon. For the hosted
Horizon control plane, use
`https://horizon.prefect.io/oauth/external/callback`.

| Field                     | Required           | Purpose                                                                                                  |
| ------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------- |
| Client ID                 | Yes                | Identifies the pre-registered Horizon OAuth client.                                                      |
| Client secret             | Provider-dependent | Authenticates a confidential OAuth client.                                                               |
| Authorization URL         | Yes                | Sends users to the provider to grant access.                                                             |
| Token URL                 | Yes                | Exchanges authorization grants and refreshes tokens.                                                     |
| Issuer                    | No                 | Identifies the authorization server. It defaults to the authorization URL.                               |
| Scopes                    | No                 | Requests the provider permissions needed by the server. Enter multiple scopes as a comma-separated list. |
| Token to forward upstream | Yes                | Uses the access token for standard OAuth APIs, or the ID token when an upstream OIDC proxy requires it.  |

After the configuration is saved, each user completes the provider flow for
their own Horizon identity. External-server users can select **Authorize** from
the server's **Connection** page. Hosted-server users authorize when connecting
an interactive MCP client, as described in [User authorization](#user-authorization).
Horizon stores the resulting credential for that Horizon actor. The MCP client
never receives the provider token.

## API-key setup

API-key configurations define both who supplies the key and how Horizon places
it on the downstream request. The default sends a bearer token in the standard
HTTP authorization header:

| Setting      | Default          | Example alternative                |
| ------------ | ---------------- | ---------------------------------- |
| Header name  | `Authorization`  | `X-API-Key`                        |
| Header value | `Bearer {token}` | `{token}` or `Token token={token}` |

The header value must contain `{token}`. Horizon replaces that placeholder with
the stored key when it forwards a request. This supports raw-key headers as
well as providers that require a custom scheme or token format.

Use header injection for remote credentials. Query-string credentials are not a
supported authentication channel, and a secret embedded in a connector URL can
be exposed anywhere that URL is displayed or recorded. A remote MCP server that
requires a credential must accept it through an HTTP header.

With a per-user API key, the configuration is created without a key.
External-server users can select **Add token** from the server's **Connection**
page. Hosted-server users add their key through the client authorization flow
described in [User authorization](#user-authorization). With a shared API key,
a server administrator selects **Add key** and saves the organization-wide
value. Saved values are not displayed again.

## External servers

An [external server](/servers/external) always authenticates the client at the
Horizon-served endpoint. Horizon then uses the configured external credential,
or no credential for a public remote MCP server, when it forwards the request.

<Steps>
  <Step title="Choose a connector">
    From <b>Servers</b>, start creating an external server. Select a
    [global connector](/servers/global-connectors), or add and select an
    organization connector for the remote MCP endpoint.
  </Step>

  <Step title="Choose authentication">
    Enter the connector name, then select <b>No authentication</b>,
    <b>OAuth (per user)</b>, <b>Key (per user)</b>, or <b>Key (shared)</b>.
    Select no authentication only when the remote server accepts anonymous
    requests.
  </Step>

  <Step title="Configure the provider">
    If authentication is required, review the discovered OAuth registration or
    open <b>OAuth Configuration</b> and enter a pre-registered client. For an
    API key, set the header name and header value template expected by the
    remote server.
  </Step>

  <Step title="Add the first credential">
    If authentication is required, complete <b>Authorize with OAuth</b>, enter
    your personal API key, or enter the shared key. Horizon verifies that a
    credential has been saved before continuing.
  </Step>

  <Step title="Create the connector">
    Review the remote endpoint and authentication mode, then select
    <b>Create Connector</b>. Horizon uses the saved credential when one is
    configured and sends anonymous requests otherwise.
  </Step>
</Steps>

For OAuth and per-user API keys, every additional user must authorize the
remote server separately. They can open the external server's **Connection**
page to authorize, replace, or revoke their credential. A user who has Horizon
server access but no valid remote credential can pass Horizon's access checks
and still receive an authentication failure from the remote server.

## Delegated authorization

Hosted delegated authorization applies the same external auth configuration to
a [hosted server](/servers/hosted). It is useful when hosted server code calls
an upstream API as the signed-in user and already expects that provider's token
in an HTTP request header.

The setup flow and runtime flow form a double handshake:

1. The user authorizes Horizon with the upstream provider, or stores a personal
   token in Horizon.
2. The MCP client authenticates to Horizon. The trusted gateway identifies the
   Horizon user, exchanges that identity for the saved downstream credential,
   and injects the credential before invoking hosted server code.

**Setup**

```mermaid theme={null}
flowchart TB
    user["Horizon user"] -->|"Authorizes"| provider["Upstream provider"]
    provider -->|"Stores credential"| store[("Horizon credential store")]
```

**Runtime**

```mermaid theme={null}
flowchart TB
    client["MCP client"] -->|"Horizon credential"| gateway["Horizon gateway"]
    store[("Credential store")] -->|"Credential for actor"| gateway
    gateway -->|"Downstream header"| server["Hosted server"]
    server -->|"Authenticated call"| api["Upstream API"]
```

On a successful exchange, Horizon removes the inbound Horizon
`Authorization` header. It then injects the downstream credential using the
header name and value configured by the auth source. For the default OAuth
configuration, hosted server code receives:

```http theme={null}
Authorization: Bearer <downstream-access-token>
```

Horizon completes the exchange before hosted server code runs and injects only the resulting downstream credential. Your code cannot access the auth source or other credentials stored in Horizon. Code that already reads the configured header can use delegated authorization without implementing its own OAuth callback or credential storage.

## Configure delegation

Delegated authorization requires Horizon Authentication to remain enabled for
the hosted server. Linking and unlinking an auth source updates serving
configuration without rebuilding or redeploying the hosted server. The
**Access** and **Authentication** page requires server write access, so these
configuration steps are for a server administrator.

<Steps>
  <Step title="Open authentication">
    Open the hosted server, then select <b>Access</b> and
    <b>Authentication</b>. Confirm that <b>Horizon Authentication</b> is
    enabled.
  </Step>

  <Step title="Link an auth source">
    In <b>Delegated authentication</b>, select <b>Link auth source</b>. Give the
    source a recognizable name and choose OAuth, per-user API key, or shared API
    key.
  </Step>

  <Step title="Configure credentials">
    Complete OAuth discovery and registration, or configure the API-key header
    and value template. Select <b>Link auth source</b> to create the
    configuration and attach it to the hosted server.
  </Step>

  <Step title="Add the first credential">
    Select <b>Authorize</b>, <b>Add token</b>, or <b>Add key</b> beside the linked
    source. OAuth and per-user API-key credentials belong to your signed-in
    Horizon user. A shared key belongs to the organization.
  </Step>

  <Step title="Test the exchange">
    Call the hosted server through its Horizon endpoint as an authorized user.
    Confirm that the hosted handler receives the configured downstream header
    and that its upstream call succeeds.
  </Step>
</Steps>

To stop delegated exchange, open the linked source's action menu and select
**Unlink auth source**. Unlinking disables delegated authorization for the
hosted server and removes the saved link from serving configuration. Users must
authorize again if the source is linked later.

## User authorization

OAuth and per-user API-key auth sources require every caller to save their own
downstream credential. Users with server access do not need server write access
to complete this authorization.

An interactive MCP client starts the self-authorization flow when it first
connects to the hosted server. After Horizon sign-in, the **Horizon is requesting
access** page lists the linked auth source. Select **Needs Access** to complete
OAuth or **Needs API key** to save a personal token. Horizon then completes the
client connection.

A client that uses a personal Horizon API key does not open this browser flow.
Before connecting that client, sign in to Horizon in a browser and open:

```text theme={null}
https://horizon.prefect.io/<server-slug>/authorize
```

Replace `<server-slug>` with the hostname prefix from the hosted server URL. For
example, the slug in `https://weather-mcp.fastmcp.app/mcp` is `weather-mcp`.
Authorize the provider or add the personal token, then retry the client. This
self-authorization page requires server access, while auth-source configuration
remains restricted to server writers.

Service-account keys cannot use delegated authorization. Authorizing in the
browser saves the downstream credential for the signed-in user, not the service
account. Automation that authenticates with a service-account key must provide
its upstream credential through the hosted server itself, such as with an
[environment variable](/environment-variables), until service-account delegated
authorization is supported.

For user actors, a shared downstream API key needs no per-user authorization.
Horizon uses the organization-wide credential configured by the server
administrator.

## Current boundaries

Hosted delegated authorization has the following first-version boundaries:

| Boundary         | Current behavior                                                                                                                           |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Horizon identity | Exchange runs only for signed-in user actors on hosted servers with Horizon Authentication enabled. Passthrough requests are not eligible. |
| Providers        | One auth source can be linked to a hosted server. Multi-provider routing is on the roadmap.                                                |
| Service accounts | Delegated authorization does not run for service-account actors. Service-account support is on the roadmap.                                |
| Exchange failure | Credential exchange fails open. Horizon forwards the hosted request without replacing the original request credential.                     |

Fail-open behavior preserves hosted server availability when a credential is
missing, expired, revoked, or temporarily unavailable. It does not bypass the
initial Horizon authentication or server access checks, which happen before the
exchange. The hosted server will not receive a usable downstream credential, so
its upstream call will normally fail authentication unless the server has an
independent fallback.

Horizon encrypts OAuth client secrets, OAuth tokens, and API keys at rest. It
keeps credential material out of normal responses, logs, and traces. For the
storage and encryption boundary, see
[Data protection](/security/data-protection).

## Related docs

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/platform/authentication">
    Understand how callers authenticate to Horizon before external exchange.
  </Card>

  <Card title="External servers" icon="plug" href="/servers/external">
    Register and operate remote MCP servers through the Horizon gateway.
  </Card>

  <Card title="Hosted servers" icon="server" href="/servers/hosted">
    Build and run MCP servers that can use delegated authorization.
  </Card>

  <Card title="Data protection" icon="lock" href="/security/data-protection">
    Review how Horizon protects credentials and other sensitive values.
  </Card>
</CardGroup>
