Hmbown/CodeWhale · error

returned a verification URI with embedded credentials

Error message

{context} returned a verification URI with embedded credentials

What it means

Thrown when a device-code verification URI contains embedded credentials (user:password@ in the URL). Credentials in a URI leak secrets into logs, browser history, and referrer headers, so the login flow rejects them unconditionally — even for otherwise-allowed https or loopback hosts.

Solutions

  1. Remove the user:password@ portion from the verification URI the provider returns
  2. Configure the auth server to issue credential-free public verification URLs
  3. Use headers or a token exchange for authentication instead of URL basic-auth
  4. Point the provider base URL at the public endpoint rather than the credentialed upstream

Example fix

// before
let uri = "https://user:secret@example.com/activate";
validate_browser_verification_uri(uri, "login")?;
// after
let uri = "https://example.com/activate";
validate_browser_verification_uri(uri, "login")?;
Defensive patterns

Strategy: validation

Validate before calling

fn uri_has_credentials(raw: &str) -> bool {
    raw.trim().split_once("://")
        .and_then(|(_, rest)| rest.find('@').map(|at| rest[..at].contains(':')))
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: Calling validate_browser_verification_uri with a URI like https://user:pass@example.com/activate; a provider constructing its verification_uri from a URL that includes basic-auth credentials.

Common situations: Reverse-proxy setups where the auth server builds links from an authenticated upstream URL; misconfigured providers that bake service credentials into callback URLs.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/9f9cdd209f239a5b. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/device_code.rs:199

///
/// Ported from pi's `validateVerificationUri`
/// (`packages/ai/src/auth/oauth/xai.ts`, MIT, Copyright (c) 2025 Mario
/// Zechner): the URI comes straight off the wire and is passed to the platform
/// "open this" call, so a malicious or compromised response could otherwise
/// launch `file:`, a custom app scheme, or a helper with attacker-chosen
/// arguments. pi requires `https:`; Codewhale additionally allows `http:` on a
/// loopback host, which is what self-hosted issuers and the device-code tests
/// use — matching the loopback allowance the account login already makes.
///
/// Embedded credentials are rejected in every case.
pub fn validate_browser_verification_uri(raw: &str, context: &str) -> Result<String> {
    let trimmed = raw.trim();
    let Ok(url) = url_scheme_and_host(trimmed) else {
        bail!("{context} returned an unusable verification URI");
    };
    let (scheme, host, has_credentials) = url;
    if has_credentials {
        bail!("{context} returned a verification URI with embedded credentials");
    }
    let allowed = scheme == "https" || (scheme == "http" && is_loopback_host(&host));
    if !allowed {
        bail!("{context} returned an untrusted verification URI");
    }
    Ok(trimmed.to_string())
}

/// Minimal scheme/host/credential split, so this module stays free of a URL
/// dependency (`codewhale-config` deliberately has no `reqwest`/`url`).
pub(crate) fn url_scheme_and_host(raw: &str) -> Result<(String, String, bool), ()> {
    let (scheme, rest) = raw.split_once("://").ok_or(())?;
    if scheme.is_empty()
        || !scheme
            .bytes()
            .all(|b| b.is_ascii_alphanumeric() || b == b'+' || b == b'-' || b == b'.')
    {
        return Err(());

View on GitHub (pinned to 73e0f67d83)