use std::{env::temp_dir, fs, future::Future, os::unix::fs::PermissionsExt}; use gpapi::{auth::SamlAuthData, GP_CALLBACK_PORT_FILENAME}; use log::info; use tokio::{io::AsyncReadExt, net::TcpListener}; use crate::{browser_auth::browser_auth_impl::BrowserAuthenticatorImpl, AuthWindow}; pub trait BrowserAuthenticator { fn browser_authenticate(&self, browser: Option<&str>) -> impl Future> + Send; } impl BrowserAuthenticator for AuthWindow<'_> { async fn browser_authenticate(&self, browser: Option<&str>) -> anyhow::Result { let auth_request = self.initial_auth_request().await?; let browser_auth = if let Some(browser) = browser { BrowserAuthenticatorImpl::new_with_browser(&auth_request, browser) } else { BrowserAuthenticatorImpl::new(&auth_request) }; browser_auth.authenticate()?; info!("Please continue the authentication process in the default browser"); wait_auth_data().await } } async fn wait_auth_data() -> anyhow::Result { // Start a local server to receive the browser authentication data let listener = TcpListener::bind("127.0.0.1:0").await?; let port = listener.local_addr()?.port(); let port_file = temp_dir().join(GP_CALLBACK_PORT_FILENAME); // Write the port to a file fs::write(&port_file, port.to_string())?; fs::set_permissions(&port_file, fs::Permissions::from_mode(0o600))?; // Remove the previous log file let callback_log = temp_dir().join("gpcallback.log"); let _ = fs::remove_file(&callback_log); info!("Listening authentication data on port {}", port); info!( "If it hangs, please check the logs at `{}` for more information", callback_log.display() ); let (mut socket, _) = listener.accept().await?; info!("Received the browser authentication data from the socket"); let mut data = String::new(); socket.read_to_string(&mut data).await?; // Remove the port file fs::remove_file(&port_file)?; let auth_data = SamlAuthData::from_gpcallback(&data)?; Ok(auth_data) }