use crate::errors::{ClientError, DPoPError};
use anyhow::Result;
use atproto_identity::key::KeyData;
use atproto_oauth::dpop::{request_dpop, DpopRetry};
use reqwest_chain::ChainMiddleware;
use reqwest_middleware::ClientBuilder;
use tracing::Instrument;
pub struct DPoPAuth {
pub dpop_private_key_data: KeyData,
pub oauth_access_token: String,
pub oauth_issuer: String,
}
pub async fn get_json(http_client: &reqwest::Client, url: &str) -> Result<serde_json::Value> {
let http_response =
http_client
.get(url)
.send()
.await
.map_err(|error| ClientError::HttpRequestFailed {
url: url.to_string(),
error,
})?;
let value = http_response
.json::<serde_json::Value>()
.await
.map_err(|error| ClientError::JsonParseFailed {
url: url.to_string(),
error,
})?;
Ok(value)
}
pub async fn get_dpop_json(
http_client: &reqwest::Client,
dpop_auth: &DPoPAuth,
url: &str,
) -> Result<serde_json::Value> {
let (dpop_proof_token, dpop_proof_header, dpop_proof_claim) = request_dpop(
&dpop_auth.dpop_private_key_data,
"GET",
url,
&dpop_auth.oauth_issuer,
&dpop_auth.oauth_access_token,
)
.map_err(|error| DPoPError::ProofGenerationFailed { error })?;
let dpop_retry = DpopRetry::new(
dpop_proof_header.clone(),
dpop_proof_claim.clone(),
dpop_auth.dpop_private_key_data.clone(),
true,
);
let dpop_retry_client = ClientBuilder::new(http_client.clone())
.with(ChainMiddleware::new(dpop_retry.clone()))
.build();
let http_response = dpop_retry_client
.get(url)
.header(
"Authorization",
&format!("DPoP {}", dpop_auth.oauth_access_token),
)
.header("DPoP", &dpop_proof_token)
.send()
.instrument(tracing::info_span!("dpop_get_request", url = %url))
.await
.map_err(|error| DPoPError::HttpRequestFailed {
url: url.to_string(),
error,
})?;
let value = http_response
.json::<serde_json::Value>()
.await
.map_err(|error| DPoPError::JsonParseFailed {
url: url.to_string(),
error,
})?;
Ok(value)
}
pub async fn post_dpop_json(
http_client: &reqwest::Client,
dpop_auth: &DPoPAuth,
url: &str,
record: serde_json::Value,
) -> Result<serde_json::Value> {
let (dpop_proof_token, dpop_proof_header, dpop_proof_claim) = request_dpop(
&dpop_auth.dpop_private_key_data,
"POST",
url,
&dpop_auth.oauth_issuer,
&dpop_auth.oauth_access_token,
)
.map_err(|error| DPoPError::ProofGenerationFailed { error })?;
let dpop_retry = DpopRetry::new(
dpop_proof_header.clone(),
dpop_proof_claim.clone(),
dpop_auth.dpop_private_key_data.clone(),
true,
);
let dpop_retry_client = ClientBuilder::new(http_client.clone())
.with(ChainMiddleware::new(dpop_retry.clone()))
.build();
let http_response = dpop_retry_client
.post(url)
.header(
"Authorization",
&format!("DPoP {}", dpop_auth.oauth_access_token),
)
.header("DPoP", &dpop_proof_token)
.json(&record)
.send()
.instrument(tracing::info_span!("dpop_post_request", url = %url))
.await
.map_err(|error| DPoPError::HttpRequestFailed {
url: url.to_string(),
error,
})?;
let value = http_response
.json::<serde_json::Value>()
.await
.map_err(|error| DPoPError::JsonParseFailed {
url: url.to_string(),
error,
})?;
Ok(value)
}