{"record":{"id":"f8a8846954dd12a5","repo":"windmill-labs/windmill","slug":"mcp-server-does-not-publish-oauth-authorization-me","errorCode":null,"errorMessage":"MCP server does not publish OAuth authorization metadata","messagePattern":"MCP server does not publish OAuth authorization metadata","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/windmill-mcp/src/lib.rs","lineNumber":62,"sourceCode":"    pub use rmcp::transport::auth::{AuthorizationManager, AuthorizationMetadata};\n\n    const DEFAULT_OAUTH_HTTP_TIMEOUT: Duration = Duration::from_secs(30);\n\n    /// Discover the MCP server's OAuth metadata, refusing endpoints the server\n    /// never advertised.\n    ///\n    /// When a server publishes no metadata at all, rmcp's `resolve_metadata`\n    /// falls back to inventing `/authorize`, `/token` and `/register` on the\n    /// server's own host. Dynamic client registration and the token exchange\n    /// both carry secrets, so they must only ever reach endpoints the server\n    /// actually published — a guessed path would send them somewhere the\n    /// operator never designated as an authorization server.\n    pub async fn discover_authorization_metadata(\n        manager: &AuthorizationManager,\n    ) -> anyhow::Result<AuthorizationMetadata> {\n        let resolution = manager.resolve_metadata().await?;\n        if resolution.source == AuthorizationMetadataSource::LegacyEndpointFallback {\n            anyhow::bail!(\"MCP server does not publish OAuth authorization metadata\");\n        }\n        Ok(resolution.metadata)\n    }\n\n    pub fn no_redirect_http_client() -> Result<reqwest::Client, reqwest::Error> {\n        no_redirect_http_client_with_timeout(DEFAULT_OAUTH_HTTP_TIMEOUT)\n    }\n\n    pub(crate) fn no_redirect_http_client_with_timeout(\n        timeout: Duration,\n    ) -> Result<reqwest::Client, reqwest::Error> {\n        reqwest::Client::builder()\n            .timeout(timeout)\n            .redirect(reqwest::redirect::Policy::none())\n            .build()\n    }\n\n    /// Like [`no_redirect_http_client`], but pins DNS to the address the SSRF","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/backend/windmill-mcp/src/lib.rs#L44-L80","documentation":"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.","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Configure OAuth authorization server metadata for the MCP server (issuer, authorization/token endpoints) in instance settings","Verify the AuthorizationManager is initialized with published metadata before clients perform discovery","Use a non-OAuth auth method (token) if the deployment intentionally has no OAuth metadata"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// before discovery, check that metadata is configured\nlet resolution = manager.resolve_metadata().await?;\nif resolution.source == AuthorizationMetadataSource::LegacyEndpointFallback {\n  // OAuth discovery will fail; surface config instructions instead\n}","typeGuard":"fn has_published_metadata(source: &AuthorizationMetadataSource) -> bool {\n  *source != AuthorizationMetadataSource::LegacyEndpointFallback\n}","tryCatchPattern":"match discover_authorization_metadata(&manager).await {\n  Err(e) if e.to_string().contains(\"does not publish OAuth\") =>\n    eprintln!(\"configure OAuth authorization server metadata first\"),\n  Err(e) => return Err(e),\n  Ok(meta) => meta,\n}","preventionTips":["Configure OAuth authorization server metadata before advertising OAuth to clients","Check resolution.source before trusting fallback endpoints","Fall back to static token auth when OAuth metadata is absent"],"tags":["mcp","oauth","discovery","rfc8414"],"backgroundTag":"oauth-metadata-missing","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}