use std::time::Duration; use tokio::{ sync::broadcast::{Receiver, Sender}, time::sleep, }; use crate::{ config::Config, models::*, pipe::Pipe, state_machine::Event, BASE_URL, CLIENT_ID, REDIRECT_URI, }; pub fn open_browser( code_verifier: CodeVerifier, code_challenge_method: CodeChallengeMethod, ) -> Result<(), anyhow::Error> { let code_challenge = match code_challenge_method { CodeChallengeMethod::Plain => { use base64::prelude::*; BASE64_URL_SAFE_NO_PAD.encode(code_verifier.to_string()) } CodeChallengeMethod::Sha256 => { use base64::prelude::*; BASE64_URL_SAFE_NO_PAD.encode(sha256::digest(code_verifier.to_string())) } }; let request = AuthorizeRequest::new( CLIENT_ID, ResponseType::Code, None, code_challenge.clone(), Some(code_challenge_method.clone()), RedirectUri::new(REDIRECT_URI), None, ); let qs = serde_qs::to_string(&request)?; open::that(format!("{}/oauth2/authorize?{}", BASE_URL, qs))?; Ok(()) } pub async fn start_code_listener( pipe_sender: Sender, event_receiver: Receiver, ) -> Result<(), anyhow::Error> { let pipe = Pipe::new(event_receiver.resubscribe()); pipe.listen(pipe_sender).await?; Ok(()) } pub async fn start_code_to_token( config: Config, mut pipe_receiver: Receiver, event_sender: Sender, mut event_receiver: Receiver, ) -> Result<(), anyhow::Error> { loop { tokio::select! { _ = sleep(Duration::from_millis(100)) => { if let Ok(Event::Quit) = event_receiver.try_recv() { tracing::info!("Shutting down Code Transformer"); break; } } Ok(response) = pipe_receiver.recv() => { let r = AuthorizationCodeRequest::new( GrantType::AuthorizationCode, response.code(), REDIRECT_URI.into(), CLIENT_ID, config.code_verifier().unwrap() ); let qs = serde_qs::to_string(&r)?; let client = reqwest::Client::new(); let response = client .post(format!("{}/oauth2/token", BASE_URL)) .body(qs) .send() .await?; let response: AuthorizationCodeResponse = response.json().await?; event_sender.send(Event::TokenReceived { token: response.token() })?; } } } tracing::info!("Code Transformer Shutdown"); Ok(()) }