zed-industries/zed · error
authorization server does not support S256 PKCE
Error message
authorization server does not support S256 PKCE
What it means
The MCP authorization spec mandates PKCE with the S256 challenge method. During discover(), after fetching auth server metadata, the code checks code_challenge_methods_supported and requires the list to contain 'S256'. A list that is present but lacks S256 (e.g. only 'plain', or 'S-256' with a non-standard spelling) triggers this bail before any authorization URL is built. This is a deliberate interop gate: without S256 the flow would be non-compliant and weaker against authorization-code interception.
Source
Thrown at crates/context_server/src/oauth.rs:874
pub async fn discover(
http_client: &Arc<dyn HttpClient>,
server_url: &Url,
www_authenticate: &WwwAuthenticate,
) -> Result<OAuthDiscovery> {
let resource_metadata =
fetch_protected_resource_metadata(http_client, server_url, www_authenticate).await?;
let auth_server_url = resource_metadata
.authorization_servers
.first()
.ok_or_else(|| anyhow!("no authorization servers in resource metadata"))?;
let auth_server_metadata = fetch_auth_server_metadata(http_client, auth_server_url).await?;
// Verify PKCE S256 support (spec requirement).
match &auth_server_metadata.code_challenge_methods_supported {
Some(methods) if methods.iter().any(|m| m == "S256") => {}
Some(_) => bail!("authorization server does not support S256 PKCE"),
None => bail!("authorization server does not advertise code_challenge_methods_supported"),
}
let scopes = select_scopes(www_authenticate, &resource_metadata);
Ok(OAuthDiscovery {
resource_metadata,
auth_server_metadata,
scopes,
})
}
/// Resolve the OAuth client registration for an authorization flow.
///
/// CIMD uses the static client metadata document directly. For DCR, a fresh
/// registration is performed each time because the loopback redirect URI
/// includes an ephemeral port that changes every flow.
pub async fn resolve_client_registration(View on GitHub (pinned to f4178619ac)
Solutions
- Enable PKCE S256 in the authorization server configuration (essentially every modern OAuth library supports it; for custom servers, implement SHA-256 of the verifier per RFC 7636)
- Fix the metadata value to advertise the exact string "S256" if the server actually supports it but lists it differently
- If the server genuinely cannot do S256, it is incompatible with MCP authorization — replace or front it with one that can
Example fix
// before (auth server metadata) "code_challenge_methods_supported": ["plain"] // after "code_challenge_methods_supported": ["S256", "plain"]
Defensive patterns
Strategy: validation
Validate before calling
// client-side: inspect metadata before entering the flow
let methods = metadata.get("code_challenge_methods_supported")
.and_then(|v| v.as_array())
.map(|a| a.iter().filter_map(|m| m.as_str().to_string()).collect::<Vec<_>>());
anyhow::ensure!(
methods.as_deref().is_some_and(|m| m.contains(&"S256".to_string())),
"auth server must advertise S256 PKCE; got {:?}", methods
); Type guard
fn supports_s256_pkce(doc: &serde_json::Value) -> bool {
doc.get("code_challenge_methods_supported")
.and_then(|v| v.as_array())
.is_some_and(|list| list.iter().any(|m| m.as_str() == Some("S256")))
} Try / catch
match discover(&client, &server_url, &challenge).await {
Err(err) if err.to_string().contains("S256") => {
// server PKCE config problem — enable S256 server-side; client retries cannot fix it
report_server_requirement("enable and advertise code_challenge_methods_supported: [\"S256\"]");
Err(err)
}
other => other,
} Prevention
- Enable PKCE S256 in the authorization server and advertise the exact string "S256"
- Beware spelling variants ("s256", "S-256") — the comparison is exact
- Include a PKCE-methods assertion in your auth server's deployment config test
When it happens
Trigger: Auth server metadata contains "code_challenge_methods_supported": ["plain"] (or any list without the exact string "S256"), and discover() reaches the match arm Some(_) => bail!.
Common situations: Legacy OAuth2 server that supports only the deprecated 'plain' PKCE method; homegrown auth server that never configured PKCE support; case/typo variations like "s256" or "S-256" that fail the exact m == "S256" comparison.
Related errors
- authorization server does not advertise code_challenge_metho
- OAuth endpoint must use HTTPS (got {}://{})
- OAuth endpoint must not point to private/reserved IP: {}
- OAuth endpoint must not point to private/reserved IP: ::ffff
- OAuth endpoint must not point to reserved IPv6 address: {}
AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20).
Data as JSON: /api/errors/e7b4dbe583f3ffaa.
Report an issue: GitHub.