xai-org/grok-build · error

Server returned unsupported verification URI scheme

Error message

Server returned unsupported verification URI scheme

What it means

validate_verification_uri only accepts https URIs, plus http when the host is localhost or 127.0.0.1. Any other scheme (ftp, javascript, custom app schemes) or non-local http host is rejected, preventing the user from being directed to an insecure or dangerous destination.

Source

Thrown at crates/codegen/xai-grok-shell/src/auth/device_code.rs:525

    let claims: IdTokenClaims = match serde_json::from_slice(&payload) {
        Ok(claims) => claims,
        Err(_) => return (String::new(), None),
    };
    (claims.sub.unwrap_or_default(), claims.email)
}

fn validate_verification_uri(uri: &str) -> anyhow::Result<()> {
    if uri.chars().any(|c| c.is_ascii_control()) {
        anyhow::bail!("Server returned invalid verification URI");
    }

    let parsed = url::Url::parse(uri)
        .map_err(|_| anyhow::anyhow!("Server returned invalid verification URI"))?;

    match parsed.scheme() {
        "https" => Ok(()),
        "http" if matches!(parsed.host_str(), Some("localhost") | Some("127.0.0.1")) => Ok(()),
        _ => anyhow::bail!("Server returned unsupported verification URI scheme"),
    }
}

#[cfg(test)]
pub(crate) mod tests {
    use std::sync::Arc;

    use super::{AuthManager, build_auth, validate_verification_uri};
    use crate::auth::{AuthMode, GrokComConfig};

    #[test]
    fn validate_verification_uri_rejects_unsupported_scheme() {
        let err = validate_verification_uri("javascript:alert(1)").unwrap_err();
        assert_eq!(
            "Server returned unsupported verification URI scheme",
            err.to_string()
        );
    }

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Ensure the authorization server advertises an https verification_uri.
  2. If testing locally, use http://localhost:<port> or http://127.0.0.1:<port>, which are allowed.
  3. Fix TLS on an internal issuer instead of downgrading to http.
  4. Update the client if the official issuer's scheme policy changed.

Example fix

// before
{"verification_uri": "http://auth.internal.corp/device"}
// after
{"verification_uri": "https://auth.internal.corp/device"}
Defensive patterns

Strategy: validation

Validate before calling

fn uri_scheme_allowed(uri: &str) -> bool {
    match url::Url::parse(uri) {
        Ok(u) => match u.scheme() {
            "https" => true,
            "http" => matches!(u.host_str(), Some("localhost") | Some("127.0.0.1")),
            _ => false,
        },
        Err(_) => false,
    }
}

Type guard

fn as_https_or_local(uri: &str) -> Option<url::Url> {
    let u = url::Url::parse(uri).ok()?;
    match (u.scheme(), u.host_str()) {
        ("https", _) => Some(u),
        ("http", Some("localhost") | Some("127.0.0.1")) => Some(u),
        _ => None,
    }
}

Try / catch

if let Err(e) = request_device_code(&client, &cfg).await {
    if e.to_string().contains("unsupported verification URI scheme") {
        eprintln!("Issuer must serve https (or http on localhost). Fix the auth server.");
    }
    return Err(e);
}

Prevention

When it happens

Trigger: request_device_code (or the test validate_verification_uri_rejects_unsupported_scheme) passes a parsed URI whose scheme is not https, or an http URI whose host is not localhost/127.0.0.1 — e.g. http://example.com/device or myapp://auth.

Common situations: Corporate proxy or on-prem issuer serving verification over plain http on a non-localhost host; issuer using a custom deep-link scheme; typo'd or attacker-supplied URL in a tampered response.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/91ab84777470eaff. Report an issue: GitHub.