{"record":{"id":"5ab7cee37af0ba9a","repo":"zed-industries/zed","slug":"www-authenticate-header-does-not-use-bearer-scheme","errorCode":null,"errorMessage":"WWW-Authenticate header does not use Bearer scheme","messagePattern":"WWW-Authenticate header does not use Bearer scheme","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/context_server/src/oauth.rs","lineNumber":281,"sourceCode":"    pub error: Option<BearerError>,\n    pub error_description: Option<String>,\n}\n\n/// Parse a `WWW-Authenticate` header value.\n///\n/// Expects the `Bearer` scheme followed by comma-separated `key=\"value\"` pairs.\n/// Per RFC 6750 and RFC 9728, the relevant parameters are:\n/// - `resource_metadata` — URL of the Protected Resource Metadata document\n/// - `scope` — space-separated list of required scopes\n/// - `error` — error code (e.g. \"insufficient_scope\")\n/// - `error_description` — human-readable error description\npub fn parse_www_authenticate(header: &str) -> Result<WwwAuthenticate> {\n    let header = header.trim();\n\n    let params_str = if header.len() >= 6 && header[..6].eq_ignore_ascii_case(\"bearer\") {\n        header[6..].trim()\n    } else {\n        bail!(\"WWW-Authenticate header does not use Bearer scheme\");\n    };\n\n    if params_str.is_empty() {\n        return Ok(WwwAuthenticate {\n            resource_metadata: None,\n            scope: None,\n            error: None,\n            error_description: None,\n        });\n    }\n\n    let params = parse_auth_params(params_str);\n\n    let resource_metadata = params\n        .get(\"resource_metadata\")\n        .map(|v| Url::parse(v))\n        .transpose()\n        .map_err(|e| anyhow!(\"invalid resource_metadata URL: {}\", e))?;","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/context_server/src/oauth.rs#L263-L299","documentation":"parse_www_authenticate() parses the WWW-Authenticate header that an MCP server returns on 401 to bootstrap OAuth discovery. It accepts only the Bearer scheme (RFC 6750): the header must be at least 6 bytes long and match 'bearer' case-insensitively in its first 6 bytes, after which everything up to the parameter list is expected. Any other scheme or a shorter header triggers this bail before parameters like resource_metadata are ever looked at.","triggerScenarios":"Calling parse_www_authenticate(header) where header is e.g. 'Basic realm=\"x\"', 'Digest realm=...', 'BearerX ...' (no space, scheme longer than 6 chars so header[6..] logic still runs but for Basic/Digest it fails outright), an empty string, or a header with leading garbage. The real trigger in the flow is an MCP server answering 401 with a non-Bearer WWW-Authenticate header, meaning it does not implement the RFC 9728 protected-resource-metadata discovery that Zed's MCP OAuth flow requires.","commonSituations":"MCP server behind a corporate proxy that injects its own Basic-auth challenge instead of the server's Bearer challenge; a server that implements only static API-key auth (returns WWW-Authenticate: ApiKey or nothing useful); header truncation or corruption in an intermediary; the server returns 'Bearer' alone with no parameters, which is fine — only non-Bearer schemes fail here.","solutions":["Configure the MCP server to return 401 with 'WWW-Authenticate: Bearer resource_metadata=\"https://server/.well-known/oauth-protected-resource\"' as required by MCP authorization / RFC 9728","Remove or bypass any intermediary (proxy, gateway) that rewrites or replaces the WWW-Authenticate header with a Basic/Digest challenge","If the server only supports static API keys, use the header-based auth mode of the MCP server config instead of OAuth","Check the raw 401 response headers with curl to confirm what scheme is actually being sent before blaming the client"],"exampleFix":"# before (server response)\nHTTP/1.1 401 Unauthorized\nWWW-Authenticate: Basic realm=\"mcp\"\n\n# after\nHTTP/1.1 401 Unauthorized\nWWW-Authenticate: Bearer resource_metadata=\"https://mcp.example.com/.well-known/oauth-protected-resource\"","handlingStrategy":"validation","validationCode":"fn is_bearer_challenge(header: &str) -> bool {\n    let h = header.trim();\n    h.len() >= 6 && h[..6].eq_ignore_ascii_case(\"bearer\")\n}\n\n// before parsing a 401 response's challenge:\nlet header = response.headers().get(\"WWW-Authenticate\")\n    .and_then(|v| v.to_str().ok())\n    .unwrap_or(\"\");\nif !is_bearer_challenge(header) {\n    // server does not implement MCP OAuth discovery; fall back to another auth mode\n    return choose_static_auth_mode();\n}\nlet challenge = parse_www_authenticate(header)?;","typeGuard":"fn is_bearer_challenge(header: &str) -> bool {\n    let h = header.trim();\n    h.len() >= 6 && h[..6].eq_ignore_ascii_case(\"bearer\")\n}","tryCatchPattern":"match parse_www_authenticate(&header_value) {\n    Ok(challenge) => start_oauth_discovery(challenge),\n    Err(err) if err.to_string().contains(\"does not use Bearer scheme\") => {\n        // not an OAuth-capable MCP server — do not retry OAuth; pick another auth strategy\n        fall_back_to_static_credentials()\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Configure MCP servers meant for OAuth to answer 401 with a Bearer challenge carrying resource_metadata","Keep proxies from overwriting WWW-Authenticate on 401 responses","When integrating non-OAuth servers, use the static header auth config so discovery is never attempted"],"tags":["oauth","mcp","http-header","www-authenticate","parsing"],"backgroundTag":"www-authenticate-bearer-scheme-invalid","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}