Hmbown/CodeWhale · error · anyhow::Error

Browser login is only enabled for the canonical Codewhale ac

Error message

Browser login is only enabled for the canonical Codewhale account API or a loopback test API

What it means

Browser login is hard-gated to two API environments: the canonical https://api.codewhale.net:443 or a loopback test API. If --api-base (or the configured account API base URL) is anything else - any other HTTPS host - this bail fires before any URL is validated: device-flow login simply does not run against third-party endpoints.

Source

Thrown at crates/cli/src/cloud.rs:824

    let api = Url::parse(api_base).context("invalid Codewhale account API base URL")?;
    let canonical_api = api.scheme() == "https"
        && api.host_str() == Some("api.codewhale.net")
        && api.port_or_known_default() == Some(443);
    let loopback_api = api.host_str().is_some_and(is_loopback_host);
    if canonical_api {
        if url.scheme() != "https"
            || !host.eq_ignore_ascii_case("app.codewhale.net")
            || url.port_or_known_default() != Some(443)
        {
            bail!("The Codewhale service returned an untrusted verification origin");
        }
    } else if loopback_api {
        if !matches!(url.scheme(), "http" | "https") || !is_loopback_host(host) {
            bail!("The Codewhale service returned an untrusted verification origin");
        }
    } else {
        bail!(
            "Browser login is only enabled for the canonical Codewhale account API or a loopback test API"
        );
    }

    let query = url.query_pairs().collect::<Vec<_>>();
    if complete {
        if query.len() != 1 || query[0].0 != "user_code" || query[0].1 != user_code {
            bail!("The Codewhale service returned an unsafe verification URL");
        }
    } else if !query.is_empty() {
        bail!("The Codewhale service returned an unsafe verification URL");
    }
    Ok(url.to_string())
}

fn is_loopback_host(host: &str) -> bool {
    let host = host
        .strip_prefix('[')

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. For custom endpoints, authenticate with an API key (e.g. --api-key-stdin) instead of browser login
  2. Use the canonical --api-base https://api.codewhale.net for browser login
  3. For testing browser login locally, run the API on a loopback host (localhost/127.0.0.1/::1)
  4. Check for and remove an explicit non-443 port on the api.codewhale.net base, which disqualifies it as canonical

Example fix

# before
codewhale cloud login --api-base https://staging.codewhale.net
# after
codewhale cloud login --provider <provider> --api-key-stdin   # key auth for custom endpoints
Defensive patterns

Strategy: validation

Validate before calling

fn browser_login_supported(api_base: &str) -> bool {
    match url::Url::parse(api_base) {
        Ok(api) => {
            let canonical = api.scheme() == "https"
                && api.host_str() == Some("api.codewhale.net")
                && api.port_or_known_default() == Some(443);
            canonical || api.host_str().is_some_and(is_loopback_host)
        }
        Err(_) => false,
    }
}

if !browser_login_supported(api_base) { /* use API-key auth path */ }

Prevention

When it happens

Trigger: Starting browser login with --api-base https://staging.codewhale.net, https://api.codewhale.net:8443, or any custom/private HTTPS host that is not loopback and not exactly api.codewhale.net on port 443.

Common situations: Enterprise or self-hosted deployments pointing the CLI at an internal API; staging environments; typos in the configured API base; http:// on a non-loopback host (also rejected earlier by validate_api_base).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/06855256eea4df4e. Report an issue: GitHub.