Skip to content

Authentication: OAuth & Service Tokens

By default a tunnel is pass-through: mcppipe forwards every request and leaves the Authorization header alone for your own MCP server to interpret. When you want mcppipe itself to gate access, switch the tunnel to OAuth mode. mcppipe then behaves as an OAuth 2.1 Resource Server (per MCP spec 2025-11-25) and refuses any request that does not carry a valid Bearer access token.

This page covers the per-tunnel auth mode, how mcppipe validates tokens, the consent screen for interactive agents, and Service Tokens for headless agents.

Each tunnel has an auth mode, set in the tunnel’s Settings tab under Edge authentication:

  • Legacy (default) — the tunnel is a pass-through. The inbound Authorization header is sent straight to your MCP server untouched. Use this when your upstream does its own auth, or when you front the tunnel with the IP allowlist instead.
  • OAuth — mcppipe enforces an OAuth 2.1 Bearer token on every inbound request before routing it: Authorization Code + PKCE for interactive agents, or a Service Token for headless ones.

Flip the Require OAuth Bearer tokens toggle to switch. The change takes effect immediately on the next request.

Edge authentication settings: the Require OAuth Bearer tokens toggle Edge authentication — toggling OAuth enforcement and revealing the Service Tokens section.

On an OAuth-mode tunnel mcppipe runs these checks against the Authorization: Bearer JWT, all offline against the issuer’s JWKS:

  1. Algorithm — only asymmetric signatures are accepted (RS256/384/512, ES256/384, PS256/384/512). Symmetric (HS*) and none are rejected outright, closing the JWT algorithm-confusion attack.
  2. Signature, exp, iss — verified against the configured issuer’s JWKS (cached for 5 minutes; an unknown kid forces an immediate re-fetch).
  3. Audience (aud) — must equal the tunnel’s canonical resource URI, https://<subdomain>--<account-slug>.<zone> (or your custom domain). This RFC 8707 check stops a token minted for a different resource being replayed here (the confused-deputy attack).
  4. Tunnel pin (tid) — the token is pinned to the exact tunnel UUID it was minted for. A freed <sub>--<slug> label reclaimed by a new tunnel within the same account gets a new UUID, so an old token no longer matches.
  5. Revocation — the token’s consent id (cid) is checked against a Redis denylist, so an owner’s revocation takes effect on the very next request even though the JWT is validated offline.

A missing or invalid token returns 401 Unauthorized with a JSON body and an RFC 9728 WWW-Authenticate challenge that points a spec-compliant client at where to get a token:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token",
error_description="the access token is missing, expired, or invalid",
resource_metadata="https://api--acme.mcppipe.dev/.well-known/oauth-protected-resource"
Content-Type: application/json
{
"error": "invalid_token",
"error_description": "this tunnel requires an OAuth 2.1 access token",
"resource_metadata": "https://api--acme.mcppipe.dev/.well-known/oauth-protected-resource"
}

When no token is offered at all, the challenge is the bare form WWW-Authenticate: Bearer resource_metadata="…" and error is unauthorized.

mcppipe fails closed: if a tunnel asks for OAuth but the service has no issuer configured, or the issuer’s JWKS cannot be reached, mcppipe returns 500 rather than letting the request through.

mcppipe ships its own OAuth 2.1 Authorization Server (it is not a third-party SaaS). It publishes RFC 8414 metadata at /.well-known/oauth-authorization-server and a JWKS at /.well-known/jwks.json, and exposes:

EndpointPurpose
GET /oauth/authorizeStart the Authorization Code + PKCE flow
POST /oauth/tokenExchange the code (+ PKCE verifier) for tokens, or refresh
POST /oauth/registerDynamic Client Registration
POST /oauth/revokeRevoke a token

PKCE is mandatory and only S256 is accepted (no plain). The single scope issued is mcp (tunnel-level — the aud binds the token to one tunnel). Access tokens live 900 seconds, refresh tokens 30 days, and authorization codes are single-use with a 60-second lifetime.

When an agent runs the Authorization Code flow, the Authorization Server parks the request and redirects the tunnel owner’s browser to the consent screen at /consent. There you review which agent is asking and which tunnel it wants — client name, tunnel, endpoint, scope, and the agent id — then Approve or Deny.

Approving hands the browser back to the agent’s redirect URI with an authorization code; denying returns error=access_denied. You can revoke an approved agent at any time, which adds it to the denylist so mcppipe rejects its tokens on the next request.

Half of real-world traffic is server-side agents (LangChain, ADK, n8n, cron scripts) where there is no browser to click through consent. A Service Token is a long-lived JWT you mint once in the dashboard for exactly those agents. It is signed by the same key, carries the same aud and tid, and is revoked through the same denylist as an interactive token — from mcppipe’s point of view the only difference is a tracing attribute.

Service Tokens panel: list, create, and revoke long-lived tokens Service Tokens — minted from the tunnel’s Settings tab once OAuth mode is on.

To mint one, in the tunnel’s Settings tab open the Service Tokens section and choose New Service Token:

  1. Give it a recognisable name (up to 80 characters, e.g. prod-langchain-agent).
  2. Pick an Expires in of 30, 90, or 365 days.
  3. Create token — the JWT is shown exactly once. Copy it before you close the dialog; the clear value is never stored, and subsequent listings show metadata only.

Pass the token to your agent as an HTTP header:

Authorization: Bearer <service-token>

Service Tokens require OAuth mode: the section only validates tokens when the tunnel is set to Require OAuth Bearer tokens, and the API rejects a mint request on a legacy tunnel. Rotation is “revoke and mint a new one”; revoking writes the denylist marker so the token stops authenticating on the next request mcppipe sees.

  • 401 on every request — the tunnel is in OAuth mode but the agent has no valid token, or the token’s aud/tid does not match this tunnel. Check the token was minted for this exact tunnel and host.
  • 500 “OAuth is not configured on this edge” — a transient problem on mcppipe’s managed edge (the OAuth issuer/JWKS was momentarily unreachable), not something you configure. Retry, and contact support if it persists.
  • Token stopped working after a revoke — that is by design; mint a new one.

See Troubleshooting for the full status-code reference, and IP allowlist & access control for network-level gating you can combine with OAuth.