mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-02 23:56:24 +08:00
fix(oauth): use localhost for redirect URI when bound to 0.0.0.0 (#2247)
* fix(oauth): use localhost for redirect URI when bound to 0.0.0.0 Google rejects redirect_uri=http://0.0.0.0:<port>/oauth/callback as invalid. When no tunnel public_url is configured, the gateway base URL was built directly from GATEWAY_HOST, which is a bind address — not a routable hostname. Map unspecified addresses (0.0.0.0, ::, [::]) to localhost, matching the social-login flow's existing fallback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(oauth): use IpAddr::is_unspecified for robust wildcard detection Address PR feedback: replace string matching with std::net::IpAddr parsing so all representations of unspecified addresses (including 0:0:0:0:0:0:0:0) are caught. Add regression tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use is_ok_and instead of map_or(false, ..) to satisfy clippy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
44
src/main.rs
44
src/main.rs
@@ -770,7 +770,7 @@ async fn async_main() -> anyhow::Result<()> {
|
||||
.tunnel
|
||||
.public_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("http://{}:{}", gw_config.host, gw_config.port));
|
||||
.unwrap_or_else(|| oauth_base_url(&gw_config.host, gw_config.port));
|
||||
ext_mgr.enable_gateway_mode(gw_base).await;
|
||||
gw = gw.with_extension_manager(Arc::clone(ext_mgr));
|
||||
}
|
||||
@@ -1441,3 +1441,45 @@ async fn async_main() -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the OAuth base URL from the gateway bind address and port.
|
||||
///
|
||||
/// Unspecified addresses (`0.0.0.0`, `::`, `[::]`) are mapped to `localhost`
|
||||
/// because they are valid bind addresses but not valid OAuth redirect hosts.
|
||||
fn oauth_base_url(host: &str, port: u16) -> String {
|
||||
let trimmed = host.trim_start_matches('[').trim_end_matches(']');
|
||||
let is_unspecified = trimmed
|
||||
.parse::<std::net::IpAddr>()
|
||||
.is_ok_and(|ip| ip.is_unspecified());
|
||||
if is_unspecified {
|
||||
format!("http://localhost:{}", port)
|
||||
} else {
|
||||
format!("http://{}:{}", host, port)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn oauth_base_url_maps_unspecified_to_localhost() {
|
||||
assert_eq!(oauth_base_url("0.0.0.0", 3033), "http://localhost:3033");
|
||||
assert_eq!(oauth_base_url("::", 3033), "http://localhost:3033");
|
||||
assert_eq!(oauth_base_url("[::]", 3033), "http://localhost:3033");
|
||||
assert_eq!(
|
||||
oauth_base_url("0:0:0:0:0:0:0:0", 3033),
|
||||
"http://localhost:3033"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oauth_base_url_preserves_explicit_host() {
|
||||
assert_eq!(oauth_base_url("127.0.0.1", 3000), "http://127.0.0.1:3000");
|
||||
assert_eq!(
|
||||
oauth_base_url("my-server.example.com", 8080),
|
||||
"http://my-server.example.com:8080"
|
||||
);
|
||||
assert_eq!(oauth_base_url("::1", 3000), "http://::1:3000");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user