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
- Ensure the authorization server advertises an https verification_uri.
- If testing locally, use http://localhost:<port> or http://127.0.0.1:<port>, which are allowed.
- Fix TLS on an internal issuer instead of downgrading to http.
- 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
- Serve the verification page over https with valid TLS on internal issuers.
- Use localhost/127.0.0.1 for local testing instead of LAN hostnames over http.
- Never accept custom app schemes in the verification_uri from third parties.
- Review proxy rewrites that might downgrade https to http.
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
- Server returned invalid verification URI
- Server returned invalid user_code format (expected [A-Z0-9-]
- Device code expired. Run `grok login --device-auth` again.
- Authorization denied. The user rejected the request.
- Token exchange error: {detail}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/91ab84777470eaff.
Report an issue: GitHub.