use anyhow::Result; use async_trait::async_trait; use serde::{Deserialize, Serialize}; use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; use crate::api_client::AuthProvider; const DEFAULT_CLIENT_ID: &str = "databricks-cli"; const DEFAULT_REDIRECT_URL: &str = "http://localhost"; const DEFAULT_SCOPES: &[&str] = &["all-apis", "offline_access"]; pub type DatabricksOauthTokenFuture = Pin> + Send>>; pub type DatabricksOauthTokenProvider = Arc) -> DatabricksOauthTokenFuture + Send + Sync>; pub type DatabricksTokenResolver = Arc Option + Send + Sync>; pub type DatabricksRefreshHook = Arc; pub type DatabricksSessionIdProvider = Arc Option + Send + Sync>; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum DatabricksAuth { Token(String), OAuth { host: String, client_id: String, redirect_url: String, scopes: Vec, }, } impl DatabricksAuth { pub fn oauth(host: String) -> Self { Self::OAuth { host, client_id: DEFAULT_CLIENT_ID.to_string(), redirect_url: DEFAULT_REDIRECT_URL.to_string(), scopes: DEFAULT_SCOPES.iter().map(|s| s.to_string()).collect(), } } pub fn token(token: String) -> Self { Self::Token(token) } } pub struct DatabricksAuthProvider { pub auth: DatabricksAuth, pub token_cache: Arc>>, pub oauth_token_provider: Option, pub token_resolver: Option, } #[async_trait] impl AuthProvider for DatabricksAuthProvider { async fn get_auth_header(&self) -> Result<(String, String)> { let token = match &self.auth { DatabricksAuth::Token(original) => { let cached = self.token_cache.lock().unwrap().clone(); match cached { Some(t) => t, None => { let fresh = self .token_resolver .as_ref() .and_then(|resolve| resolve()) .unwrap_or_else(|| original.clone()); *self.token_cache.lock().unwrap() = Some(fresh.clone()); fresh } } } DatabricksAuth::OAuth { host, client_id, redirect_url, scopes, } => { let Some(provider) = &self.oauth_token_provider else { anyhow::bail!("Databricks OAuth token provider is not configured") }; provider( host.clone(), client_id.clone(), redirect_url.clone(), scopes.clone(), ) .await? } }; Ok(("Authorization".to_string(), format!("Bearer {token}"))) } }