windmill-labs/windmill · error

MCP server does not publish OAuth authorization metadata

Error message

MCP server does not publish OAuth authorization metadata

What it means

Per OAuth 2.0 authorization-server metadata discovery (RFC 8414), the MCP server refuses to fall back to well-known legacy endpoints that the operator never published. When `resolve_metadata` reports `LegacyEndpointFallback` — meaning no authorization metadata was actually configured — discovery bails so clients don't authenticate against endpoints the server never designated.

Source

Thrown at backend/windmill-mcp/src/lib.rs:62

    pub use rmcp::transport::auth::{AuthorizationManager, AuthorizationMetadata};

    const DEFAULT_OAUTH_HTTP_TIMEOUT: Duration = Duration::from_secs(30);

    /// Discover the MCP server's OAuth metadata, refusing endpoints the server
    /// never advertised.
    ///
    /// When a server publishes no metadata at all, rmcp's `resolve_metadata`
    /// falls back to inventing `/authorize`, `/token` and `/register` on the
    /// server's own host. Dynamic client registration and the token exchange
    /// both carry secrets, so they must only ever reach endpoints the server
    /// actually published — a guessed path would send them somewhere the
    /// operator never designated as an authorization server.
    pub async fn discover_authorization_metadata(
        manager: &AuthorizationManager,
    ) -> anyhow::Result<AuthorizationMetadata> {
        let resolution = manager.resolve_metadata().await?;
        if resolution.source == AuthorizationMetadataSource::LegacyEndpointFallback {
            anyhow::bail!("MCP server does not publish OAuth authorization metadata");
        }
        Ok(resolution.metadata)
    }

    pub fn no_redirect_http_client() -> Result<reqwest::Client, reqwest::Error> {
        no_redirect_http_client_with_timeout(DEFAULT_OAUTH_HTTP_TIMEOUT)
    }

    pub(crate) fn no_redirect_http_client_with_timeout(
        timeout: Duration,
    ) -> Result<reqwest::Client, reqwest::Error> {
        reqwest::Client::builder()
            .timeout(timeout)
            .redirect(reqwest::redirect::Policy::none())
            .build()
    }

    /// Like [`no_redirect_http_client`], but pins DNS to the address the SSRF

View on GitHub (pinned to e474e8803c)

Solutions

  1. Configure OAuth authorization server metadata for the MCP server (issuer, authorization/token endpoints) in instance settings
  2. Verify the AuthorizationManager is initialized with published metadata before clients perform discovery
  3. Use a non-OAuth auth method (token) if the deployment intentionally has no OAuth metadata
Defensive patterns

Strategy: type-guard

Validate before calling

// before discovery, check that metadata is configured
let resolution = manager.resolve_metadata().await?;
if resolution.source == AuthorizationMetadataSource::LegacyEndpointFallback {
  // OAuth discovery will fail; surface config instructions instead
}

Type guard

fn has_published_metadata(source: &AuthorizationMetadataSource) -> bool {
  *source != AuthorizationMetadataSource::LegacyEndpointFallback
}

Try / catch

match discover_authorization_metadata(&manager).await {
  Err(e) if e.to_string().contains("does not publish OAuth") =>
    eprintln!("configure OAuth authorization server metadata first"),
  Err(e) => return Err(e),
  Ok(meta) => meta,
}

Prevention

When it happens

Trigger: Calling `discover_authorization_metadata` when the AuthorizationManager has no configured OAuth authorization server metadata and would fall back to a legacy well-known endpoint; exercised by test `discovery_refuses_endpoints_the_server_never_published`.

Common situations: MCP OAuth not yet configured by the instance operator; misconfigured or missing authorization server metadata in instance settings; clients attempting OAuth discovery against a server that only supports legacy flows.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/f8a8846954dd12a5. Report an issue: GitHub.