use sfr_core as sc;
use sfr_slack_api as ssa;
use sfr_types as st;
use crate::{OauthRedirectResponse, ResponseError};
use axum::async_trait;
use sc::{OauthRedirectQuery, OauthV2AccessResponse, Slack};
use ssa::OauthClient;
use st::OauthV2AccessRequest;
#[async_trait]
pub trait OauthHandlerTrait: Send + Sync {
async fn handle_oauth(
&self,
client: reqwest::Client,
body: OauthV2AccessResponse,
) -> Result<Option<OauthRedirectResponse>, ResponseError>;
async fn take_oauth_token_from_team_id(&self, team_id: &str) -> Result<String, ResponseError>;
fn grant_type(&self) -> &str {
"authorization_code"
}
fn redirect_uri(&self) -> &str;
fn oauth_v2_access_request_form<'a>(
&'a self,
slack: &'a Slack,
code: &'a str,
) -> OauthV2AccessRequest<'a> {
OauthV2AccessRequest {
client_id: slack.client_id(),
client_secret: slack.client_secret(),
grant_type: self.grant_type(),
redirect_uri: self.redirect_uri(),
code,
}
}
async fn treat_oauth_redirect(
&self,
client: reqwest::Client,
slack: &Slack,
code: &str,
) -> Result<OauthV2AccessResponse, ResponseError> {
let client = OauthClient::new(client);
let form = self.oauth_v2_access_request_form(slack, code);
let resp = client
.oauth_v2_access(form)
.await
.map_err(|e| ResponseError::InternalServerError(Box::new(e)))?;
Ok(resp)
}
}
#[derive(Clone)]
pub(crate) struct OauthHandler<T>(T)
where
T: OauthHandlerTrait;
impl<T> OauthHandler<T>
where
T: OauthHandlerTrait,
{
pub fn new(inner: T) -> Self {
Self(inner)
}
pub async fn handle(
&self,
client: reqwest::Client,
slack: Slack,
query: OauthRedirectQuery,
) -> Result<impl axum::response::IntoResponse, ResponseError> {
tracing::info!("oauth redirect: start to process");
let body = self
.0
.treat_oauth_redirect(client.clone(), &slack, query.code())
.await?;
let resp = self
.0
.handle_oauth(client, body)
.await?
.unwrap_or_else(default_redirect_response);
Ok(resp)
}
}
fn default_redirect_response() -> OauthRedirectResponse {
#[allow(clippy::missing_docs_in_private_items)] const DEFAULT_RESPONSE: &str = r#"<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title></title></head><body>ok</body></html>"#;
OauthRedirectResponse::html(DEFAULT_RESPONSE.into())
}
impl<T> std::ops::Deref for OauthHandler<T>
where
T: OauthHandlerTrait,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}