fix: use dynamic port allocation for OAuth server (#5019)

Signed-off-by: Izzy Fraimow <izzy@squareup.com>
This commit is contained in:
Izzy
2025-10-09 00:09:09 +09:00
committed by GitHub
parent b179be4cff
commit b040cfc5ab
2 changed files with 24 additions and 12 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ use tokio_stream::StreamExt;
use tokio_util::codec::{FramedRead, LinesCodec}; use tokio_util::codec::{FramedRead, LinesCodec};
const DEFAULT_CLIENT_ID: &str = "databricks-cli"; const DEFAULT_CLIENT_ID: &str = "databricks-cli";
const DEFAULT_REDIRECT_URL: &str = "http://localhost:8020"; const DEFAULT_REDIRECT_URL: &str = "http://localhost";
const DEFAULT_SCOPES: &[&str] = &["all-apis", "offline_access"]; const DEFAULT_SCOPES: &[&str] = &["all-apis", "offline_access"];
const DEFAULT_TIMEOUT_SECS: u64 = 600; const DEFAULT_TIMEOUT_SECS: u64 = 600;
+23 -11
View File
@@ -205,7 +205,7 @@ impl OAuthFlow {
}) })
} }
fn get_authorization_url(&self) -> String { fn get_authorization_url_with_redirect(&self, redirect_url: &str) -> String {
let challenge = { let challenge = {
let digest = sha2::Sha256::digest(self.verifier.as_bytes()); let digest = sha2::Sha256::digest(self.verifier.as_bytes());
base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(digest) base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(digest)
@@ -214,7 +214,7 @@ impl OAuthFlow {
let params = [ let params = [
("response_type", "code"), ("response_type", "code"),
("client_id", &self.client_id), ("client_id", &self.client_id),
("redirect_uri", &self.redirect_url), ("redirect_uri", redirect_url),
("scope", &self.scopes.join(" ")), ("scope", &self.scopes.join(" ")),
("state", &self.state), ("state", &self.state),
("code_challenge", &challenge), ("code_challenge", &challenge),
@@ -228,11 +228,15 @@ impl OAuthFlow {
) )
} }
async fn exchange_code_for_token(&self, code: &str) -> Result<TokenData> { async fn exchange_code_for_token_with_redirect(
&self,
code: &str,
redirect_url: &str,
) -> Result<TokenData> {
let params = [ let params = [
("grant_type", "authorization_code"), ("grant_type", "authorization_code"),
("code", code), ("code", code),
("redirect_uri", &self.redirect_url), ("redirect_uri", redirect_url),
("code_verifier", &self.verifier), ("code_verifier", &self.verifier),
("client_id", &self.client_id), ("client_id", &self.client_id),
]; ];
@@ -323,19 +327,26 @@ impl OAuthFlow {
); );
// Start the server to accept the oauth code // Start the server to accept the oauth code
let redirect_url = Url::parse(&self.redirect_url)?; let redirect_url_parsed = Url::parse(&self.redirect_url)?;
let port = redirect_url.port().unwrap_or(80); let requested_port = redirect_url_parsed.port();
let addr = SocketAddr::from(([127, 0, 0, 1], port));
// If no port is specified (or port is explicitly 0), let the OS assign one
// Otherwise, use the requested port
let bind_port = requested_port.unwrap_or(0);
let addr = SocketAddr::from(([127, 0, 0, 1], bind_port));
let listener = tokio::net::TcpListener::bind(addr).await?; let listener = tokio::net::TcpListener::bind(addr).await?;
let actual_port = listener.local_addr()?.port();
let server_handle = tokio::spawn(async move { let server_handle = tokio::spawn(async move {
let server = axum::serve(listener, app); let server = axum::serve(listener, app);
server.await.unwrap(); server.await.unwrap();
}); });
let actual_redirect_url = format!("http://localhost:{}", actual_port);
// Open the browser which will redirect with the code to the server // Open the browser which will redirect with the code to the server
let authorization_url = self.get_authorization_url(); let authorization_url = self.get_authorization_url_with_redirect(&actual_redirect_url);
if webbrowser::open(&authorization_url).is_err() { if webbrowser::open(&authorization_url).is_err() {
println!( println!(
"Please open this URL in your browser:\n{}", "Please open this URL in your browser:\n{}",
@@ -354,8 +365,9 @@ impl OAuthFlow {
// Stop the server // Stop the server
server_handle.abort(); server_handle.abort();
// Exchange the code for a token // Exchange the code for a token using the actual redirect URL
self.exchange_code_for_token(&code).await self.exchange_code_for_token_with_redirect(&code, &actual_redirect_url)
.await
} }
} }
@@ -531,7 +543,7 @@ mod tests {
let flow = OAuthFlow::new( let flow = OAuthFlow::new(
endpoints, endpoints,
"test-client".to_string(), "test-client".to_string(),
"http://localhost:8020".to_string(), "http://localhost".to_string(),
vec!["all-apis".to_string()], vec!["all-apis".to_string()],
); );