Files
avam/avam-client/src/oauth.rs
2024-10-19 16:00:31 +02:00

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(())
}