117 lines
3.2 KiB
Rust
117 lines
3.2 KiB
Rust
use std::time::Duration;
|
|
use thiserror::Error;
|
|
|
|
use tokio::{
|
|
sync::broadcast::{Receiver, Sender},
|
|
time::sleep,
|
|
};
|
|
|
|
use crate::{
|
|
config::{Config, ConfigError},
|
|
models::*,
|
|
pipe::Pipe,
|
|
state_machine::Event,
|
|
BASE_URL, CLIENT_ID, REDIRECT_URI,
|
|
};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum OpenBrowserError {
|
|
#[error(transparent)]
|
|
SerdeQs(#[from] serde_qs::Error),
|
|
#[error(transparent)]
|
|
Io(#[from] std::io::Error),
|
|
#[error(transparent)]
|
|
Config(#[from] ConfigError),
|
|
}
|
|
|
|
pub fn open_browser(config: Config) -> Result<(), OpenBrowserError> {
|
|
let code_verifier = CodeVerifier::new();
|
|
let code_challenge_method = CodeChallengeMethod::Sha256;
|
|
|
|
config.set_code_verifier(Some(code_verifier.clone()))?;
|
|
config.set_code_challenge_method(Some(code_challenge_method.clone()))?;
|
|
|
|
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<AuthorizationResponse>,
|
|
event_receiver: Receiver<Event>,
|
|
) -> 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<AuthorizationResponse>,
|
|
event_sender: Sender<Event>,
|
|
mut event_receiver: Receiver<Event>,
|
|
) -> 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?;
|
|
let token = response.token();
|
|
|
|
config.set_token(Some(token.clone()))?;
|
|
|
|
event_sender.send(Event::TokenReceived)?;
|
|
}
|
|
}
|
|
}
|
|
|
|
tracing::info!("Code Transformer Shutdown");
|
|
Ok(())
|
|
}
|