{"record":{"id":"06855256eea4df4e","repo":"Hmbown/CodeWhale","slug":"browser-login-is-only-enabled-for-the-canonical-co","errorCode":null,"errorMessage":"Browser login is only enabled for the canonical Codewhale account API or a loopback test API","messagePattern":"Browser login is only enabled for the canonical Codewhale account API or a loopback test API","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/cloud.rs","lineNumber":824,"sourceCode":"\n    let api = Url::parse(api_base).context(\"invalid Codewhale account API base URL\")?;\n    let canonical_api = api.scheme() == \"https\"\n        && api.host_str() == Some(\"api.codewhale.net\")\n        && api.port_or_known_default() == Some(443);\n    let loopback_api = api.host_str().is_some_and(is_loopback_host);\n    if canonical_api {\n        if url.scheme() != \"https\"\n            || !host.eq_ignore_ascii_case(\"app.codewhale.net\")\n            || url.port_or_known_default() != Some(443)\n        {\n            bail!(\"The Codewhale service returned an untrusted verification origin\");\n        }\n    } else if loopback_api {\n        if !matches!(url.scheme(), \"http\" | \"https\") || !is_loopback_host(host) {\n            bail!(\"The Codewhale service returned an untrusted verification origin\");\n        }\n    } else {\n        bail!(\n            \"Browser login is only enabled for the canonical Codewhale account API or a loopback test API\"\n        );\n    }\n\n    let query = url.query_pairs().collect::<Vec<_>>();\n    if complete {\n        if query.len() != 1 || query[0].0 != \"user_code\" || query[0].1 != user_code {\n            bail!(\"The Codewhale service returned an unsafe verification URL\");\n        }\n    } else if !query.is_empty() {\n        bail!(\"The Codewhale service returned an unsafe verification URL\");\n    }\n    Ok(url.to_string())\n}\n\nfn is_loopback_host(host: &str) -> bool {\n    let host = host\n        .strip_prefix('[')","sourceCodeStart":806,"sourceCodeEnd":842,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/cloud.rs#L806-L842","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["For custom endpoints, authenticate with an API key (e.g. --api-key-stdin) instead of browser login","Use the canonical --api-base https://api.codewhale.net for browser login","For testing browser login locally, run the API on a loopback host (localhost/127.0.0.1/::1)","Check for and remove an explicit non-443 port on the api.codewhale.net base, which disqualifies it as canonical"],"exampleFix":"# before\ncodewhale cloud login --api-base https://staging.codewhale.net\n# after\ncodewhale cloud login --provider <provider> --api-key-stdin   # key auth for custom endpoints","handlingStrategy":"validation","validationCode":"fn browser_login_supported(api_base: &str) -> bool {\n    match url::Url::parse(api_base) {\n        Ok(api) => {\n            let canonical = api.scheme() == \"https\"\n                && api.host_str() == Some(\"api.codewhale.net\")\n                && api.port_or_known_default() == Some(443);\n            canonical || api.host_str().is_some_and(is_loopback_host)\n        }\n        Err(_) => false,\n    }\n}\n\nif !browser_login_supported(api_base) { /* use API-key auth path */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Gate browser login on environment before starting the device flow","Script non-canonical deployments with API-key auth (--api-key-stdin) from the start","Validate --api-base early with the same canonical/loopback predicate in setup scripts"],"tags":["cloud","oauth","environment-restriction","configuration"],"backgroundTag":"unsupported-configuration","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}