Configure User-Delegated OAuth
User-delegated OAuth means each end-user authenticates against the MCP server’s upstream system (for example, Slack, Jira, Google) with their own credentials. Redpanda stores their token in the token vault and injects it at call time. Contrast with service-account OAuth, where one shared identity is used for every caller. Which identity the upstream token carries can still depend on the OAuth provider. For Slack, see Choose the posting identity.
For a self-managed server, Redpanda can set up the OAuth client for you from the server URL, or you can attach an OAuth provider your organization already registered. Managed servers always attach a provider.
After completing this guide, you will be able to:
-
Configure an MCP server to use user-delegated OAuth, with either automatic setup or a provider you registered
-
Walk an end-user through the consent flow and verify the connection
-
Troubleshoot scope upgrades, token expiry, and refresh failures
Prerequisites
-
For a managed MCP server, or to attach a provider your organization already manages on a self-managed server: an OAuth provider resource configured on the Outbound providers tab of Integrations setup. The provider declares the upstream’s
authorization_endpoint,token_endpoint, scopes, and client credentials. See Configure an OAuth Provider. -
For automatic setup on a self-managed server: nothing, if the server supports dynamic client registration. If it doesn’t, you need an OAuth app registered with the upstream vendor, its client ID, and, for a confidential app, a client-secret reference already created in the secret store in
UPPER_SNAKE_CASE. -
The scopes the upstream API needs, if you want to request a specific set rather than whatever the server advertises.
-
For self-managed MCP servers: the server URL must be
https://. HTTP is rejected at create time, with one exception for cluster-local servers where cluster-local addressing has been enabled for your environment. See Register a Self-Managed MCP Server. -
For managed MCP servers: the type must support user-delegated OAuth. SQL doesn’t; Slack, Jira, and Google managed types do. Check Managed catalog before configuring.
Configure the server
-
Create your MCP server (see Create an MCP Server).
-
Set up the OAuth client for the upstream. What you do depends on the server type:
-
For a self-managed (remote) server, enter the server URL and let Redpanda check what it supports. For a server that authenticates users over OAuth, Redpanda recommends setting up OAuth automatically, which means Redpanda registers an OAuth client and manages each user’s tokens for you.
If the server supports dynamic client registration, there is nothing else to configure. If it doesn’t, Redpanda walks you through the parts it can’t do for you: register an OAuth app in the upstream’s developer console, add the authorization callback URL that Redpanda displays to that app, then paste the app’s credentials back into the form. The credentials are the
Client IDfrom the developer console and, for a confidential app, a client-secret reference from the secret store inUPPER_SNAKE_CASE, for exampleGITHUB_CLIENT_SECRET. Leave the reference empty for a public PKCE app.Either way, Redpanda records the provider it resolved or created for the server. A provider it creates carries a Managed badge on the Outbound providers tab, is read-only, and is removed when you delete the server. See Discover a provider from an MCP server URL.
To attach an OAuth provider your organization already manages instead, click Use a different authentication method, set
MethodtoUser OAuth (Per-User Delegated), then setSetuptoAttach existing providerand select the provider (UserOAuthAuth.provider_name). Click New to register a provider without leaving the form. The sameSetuptoggle switches back toSet up automatically. -
For a managed server, select the OAuth provider in the type’s configuration (
UserOAuthAuth.provider_name). Managed types always attach a provider, so automatic setup doesn’t apply.
-
-
(Optional) Restrict the scopes Redpanda requests. Expand Set specific scopes and list the scopes the upstream API needs (
UserOAuthAuth.required_scopes). Redpanda enforces them at consent time. Leave it empty to request whatever the server advertises. -
(Optional) Override token injection. By default Redpanda sends
Authorization: Bearer <token>. For a self-managed server, pass--remote.user-oauth.injection.header-nametorpk ai mcp-server createorupdateto use a different header. To omit the prefix entirely (for example, an upstream that expects a bare API key as the token), pass--remote.user-oauth.injection.header-prefixwith an empty value. The self-managed create form doesn’t expose these settings. A managed type’s configuration form includes them. -
Save.
| Choosing user-delegated OAuth instead of service-account OAuth is the credential-mode decision: there’s no separate field. User-delegated gives each caller a per-user upstream identity; service-account gives every caller one shared identity. Switching between them later requires re-consent for every active user. |
Editing an existing server works differently. The edit page doesn’t re-check the server URL, so you set Method and Setup yourself. Leaving the provider empty there keeps the provider the server already has, so switching an existing server to automatic setup is an explicit choice (UserOAuthAuth.automatic_setup): automatic setup re-runs discovery and can register a new upstream client. Setting both a provider name and automatic setup is rejected.
|
From the CLI, --remote.user-oauth.provider-name and --remote.user-oauth.required-scopes on rpk ai mcp-server create or rpk ai mcp-server update attach a provider you already registered and set the scopes it must cover. On create, omitting the provider name asks Redpanda to set OAuth up automatically from the server URL instead. On update, an empty provider name keeps the provider the server already has, so switching an existing server to automatic setup takes an explicit signal: pass --remote.user-oauth.automatic-setup without a provider name, or set automatic_setup: true under the server’s user_oauth block in a GitOps manifest. See Create an MCP Server and Manage Resources with GitOps.
|
The user connection flow
The first time a user calls a tool that needs this server’s authentication, Redpanda doesn’t have a stored token for them. The behavior is:
-
The tool call returns a failed tool result. Its text carries the authorization URL and asks the user to retry once connected, and its structured data has the type
connection_required. Other MCP requests that need the connection return HTTP 424. -
Agentic Data Plane surfaces a consent prompt to the user, pointing at the
authorize_url. -
The user completes a browser-based OAuth 2.0 authorization code flow with the upstream provider, with PKCE when the provider requires it.
-
The upstream provider redirects back to Redpanda with an authorization code, which Redpanda exchanges for a token and stores in the token vault under that user’s identity.
-
The caller retries the original tool call; the gateway’s error message tells the user to retry once connected, and the retry is not automatic. Subsequent calls reuse the stored token.
Where the browser tab finishes depends on where the flow started. A connection started from Connections returns the user to Agentic Data Plane. A flow entered by opening an authorize_url, including the consent prompt that follows a failed tool call, finishes on a Redpanda confirmation page instead. A completed connection shows Connection complete and tells the user to close the tab. If the user already has an active connection whose granted scopes cover the scopes being requested, the page shows Already connected and no second sign-in takes place. A connection that is missing a required scope, or a deliberate Reconnect, still goes through the upstream consent flow. A connection that fails shows Connection failed with the reason.
After consent, the user can see and revoke their connection under Connections in Agentic Data Plane.
Listing a server’s tools before connecting
Listing what a server offers doesn’t need a connection. For a managed MCP server, Redpanda answers a request for the server’s tools, prompts, resources, and resource templates from the tool schema it holds for that server type, without a token and whether or not the user has connected, so every user sees the same list. The exception is a managed type whose tool list depends on the signed-in user. Its tool list needs a connection first. For a self-managed MCP server, when a user has no usable connection Redpanda forwards the listing request without a token, and the upstream server decides whether to answer an anonymous listing. Either way, a client can see the server’s tools and build an agent against them before anyone signs in. The sign-in prompt arrives on the first tool call instead, as a failed tool result whose text carries the authorization URL, which is the form a client relays to its model.
On a self-managed server, a user who does have a usable connection still gets their own token on a listing, so a server that varies its tools by user keeps doing so.
Redpanda forwards a self-managed server’s listing anonymously only when signing in would fix the problem: the user has no connection, their token has expired, or it’s missing a required scope. A listing that fails for any other reason, such as a provider Redpanda can’t resolve or a token store it can’t reach, is refused instead, so a degraded listing can’t be mistaken for the real one.
Which listing an agent works from depends on the server. A self-managed server that refuses an anonymous request gets its own session per user, so each user’s tool list is their own. A server that answers anonymously shares one tool list across that agent’s users, and every tool call still runs under the identity of the user who made the request.
Manage your connections
The Connections page in the sidebar is each user’s view of their own third-party connections, so a user can authorize the providers their MCP servers need without waiting for a tool call to trigger the consent flow. It shows connections for your own identity only.
The provider list groups providers by status:
-
Available to connect: Providers you haven’t authorized yet.
-
Connected: Providers with a usable connection.
-
Needs attention: Connections that have failed, or whose refresh token is close to expiring, and need to be reconnected.
Click a provider to see its details. The details show the granted scopes, a status badge (Connected, Needs attention, Action required, or Not connected), and the actions for that provider: Connect runs the consent flow and stores a token in the vault under your identity, Reconnect re-runs consent for an existing connection (for example, to grant additional scopes or recover after a refresh token expires), and Disconnect revokes it. A connection that the gateway can renew automatically from a stored refresh token stays Connected with no action from you; it moves to Needs attention or Action required only when it can no longer renew on its own.
The details also list the MCP servers that use this connection, both managed servers run by Redpanda and remote servers you registered, and flags any scopes those servers require that the connection is missing. When a scope is missing, click Reconnect to grant it. Otherwise, the servers' tool calls fail even though the connection reads as Connected.
| Connections are per-user. Each user authorizes their own accounts, and one user’s connections are not visible to or usable by another. |
You can also manage your connections from the terminal: run rpk ai connection list to see the services you are signed in to, and rpk ai connection revoke <provider> to disconnect one. See rpk ai connection.
Scope upgrades
If a user’s stored connection has fewer scopes than the server’s required_scopes, the tool call fails with the type scope_upgrade_required (the management API reports it as OAuthScopeUpgrade) and carries a new authorization URL. That URL requests the required scopes together with the ones already granted. The user re-consents, and the connection is updated in place.
When re-consenting cannot fix it
Re-consenting only works when the OAuth Provider can actually grant the scope being asked for. A server’s required_scopes is not checked against the provider’s scopes when you save it, so you can configure a server to require a scope its provider never requests. Every tool call then reports the scope as missing. The consent flow asks the upstream for the scope, but the upstream doesn’t grant it to this provider’s OAuth app. The user re-consents, the call fails again, and there is no way out from their side.
Two shapes this takes:
-
A scope the provider does not list. Compare the server’s
required_scopesagainst the provider’s scopes and remove anything the provider does not request, or add it to the provider and have affected users reconnect once. -
A scope the upstream cannot grant to this kind of token. Some upstreams define scopes that only a user token can carry, not the bot or app token the provider requests. Those can never be satisfied by re-consenting, and belong in neither list.
When a server stops working for every user at once rather than for one person, suspect this before suspecting the user’s connection. The Connections page flags scopes that a server requires and the connection is missing, which is the quickest way to see the mismatch.
Refresh and expiry
Redpanda transparently refreshes tokens before they expire, using the refresh token returned at consent time. A connection is only considered expired when its access token has lapsed and the gateway can no longer renew it automatically. As long as a usable refresh token is stored, the connection stays active across access-token expiry, and the gateway renews access silently on the next call. This matters for upstreams that issue short-lived access tokens with long-lived refresh tokens: a GitHub App, for example, issues 8-hour access tokens backed by refresh tokens that last around six months, so an idle connection stays active for months rather than showing as expired within hours.
A connection is reported as expired, meaning the user must reconnect, only when the access token has lapsed and no usable refresh token exists: either none was stored, or the refresh token itself has expired. When that happens, the next tool call fails with the type token_expired (the management API reports it as OAuthTokenExpired) and a reason, and the user must re-consent through the same flow as the initial connection.
For a managed MCP server, a tool call whose request the upstream refuses as unauthorized also recovers on its own. Redpanda renews the user’s token and retries the request once with the replacement. This covers a session the upstream ends early, before the lifetime Redpanda knows about has lapsed. The retried call returns whatever the upstream answers, so a replacement token the upstream also refuses is reported rather than retried again. The retry happens once, not in a loop, and only for a refused credential. A request the upstream rejects as forbidden is reported as it is, because a new token doesn’t change a permission or quota answer. When many calls are refused at the same moment, they share one renewal. A call whose request can’t be replayed still renews the token, so that one call fails and the calls after it use the replacement. A self-managed server’s refused call is returned to the caller as it is.
If the connection can’t be renewed, because no refresh token was stored or the refresh token itself has expired, the user gets the same reconnect prompt and authorization link that a lapsed connection produces. A provider whose token endpoint is briefly unreachable isn’t reported that way, since reconnecting wouldn’t fix it, and the next call retries the renewal.
Not every upstream states how long its tokens last. Redpanda stores the token without an expiry rather than inventing one, so a connection with no stated expiry isn’t renewed against a guessed clock. For a managed server, it is repaired when the upstream refuses it instead.
Each connection reports whether a refresh token backs it, so a client can tell a connection that renews automatically from one that needs reconnecting. The OAuthConnectionService.ListConnections and GetConnection responses expose this on the Connection message through two read-only fields: refresh_token_present (a refresh token is stored, so the gateway renews access automatically) and refresh_token_expires_at (when the refresh token itself expires, if the upstream reports it, as GitHub Apps do; absent when unknown or non-expiring).
Service-account OAuth contrast
If you want one shared upstream identity for every caller (instead of per-user identities), choose OAuth (Service Account) on the server instead of User OAuth (Per-User Delegated). With service-account OAuth, every caller of every tool sees the same upstream identity; the upstream system has no idea which Agentic Data Plane user invoked the tool. With user-delegated OAuth, the upstream system sees each end-user as themselves and applies their own permissions.
Slack is the exception. A Slack connection is still owned per caller, but whether Slack sees the person or the app’s bot user depends on the OAuth provider’s Slack OAuth token type, and the default is the bot user. See Choose the posting identity.
For the field-by-field service-account-OAuth setup, see Create an MCP Server.
Troubleshooting
| Symptom | What to check |
|---|---|
"OAuth provider not found" |
The provider name on the server doesn’t match an OAuth provider in Agentic Data Plane. Check spelling and that the provider exists. |
"HTTPS required" on save (self-managed only) |
User-delegated OAuth requires |
|
The user’s vault entry might have been revoked or the token expired with no refresh. Have the user re-consent through Connections. |
|
The required scopes on the server changed (or the user originally consented with fewer scopes). The user re-consents with the higher scope. |
Connections shows stale entries |
Connections persist until manually revoked. Have the user disconnect and reconnect the connection if upstream credentials changed. |