use serde::Deserialize;
use crate::application::oauth::registration::{MetadataDocumentLoader, RegistrarError};
use crate::domain::oauth::registration::{ClientId, RegisteredClient};
const MAX_DOCUMENT_BYTES: usize = 64 * 1024;
#[derive(Debug, Clone)]
pub struct PublishedDocumentLoader {
http: reqwest::Client,
}
impl PublishedDocumentLoader {
#[must_use]
pub fn new() -> Self {
Self {
http: reqwest::Client::new(),
}
}
#[must_use]
pub fn with_http_client(mut self, http: reqwest::Client) -> Self {
self.http = http;
self
}
}
impl Default for PublishedDocumentLoader {
fn default() -> Self {
Self::new()
}
}
impl MetadataDocumentLoader for PublishedDocumentLoader {
async fn fetch_metadata_document(
&self,
issuer: &str,
client_id: &ClientId,
) -> Result<RegisteredClient, RegistrarError> {
let response = self
.http
.get(client_id.as_str())
.send()
.await
.map_err(|failure| RegistrarError::Unavailable(failure.to_string()))?;
let status = response.status();
if !status.is_success() {
return Err(RegistrarError::Refused {
status: status.as_u16(),
});
}
let body = read_capped(response).await?;
document(issuer, client_id, &body)
}
}
fn document(
issuer: &str,
client_id: &ClientId,
body: &[u8],
) -> Result<RegisteredClient, RegistrarError> {
let document: Document = serde_json::from_slice(body)
.map_err(|failure| RegistrarError::Malformed(failure.to_string()))?;
client_id
.verify_metadata_document(&document.client_id)
.map_err(RegistrarError::Document)?;
Ok(RegisteredClient::from_metadata_document(
issuer,
client_id.clone(),
))
}
#[derive(Debug, Deserialize)]
struct Document {
client_id: String,
}
async fn read_capped(mut response: reqwest::Response) -> Result<Vec<u8>, RegistrarError> {
let mut body: Vec<u8> = Vec::new();
while let Some(chunk) = response
.chunk()
.await
.map_err(|failure| RegistrarError::Unavailable(failure.to_string()))?
{
if body.len().saturating_add(chunk.len()) > MAX_DOCUMENT_BYTES {
return Err(RegistrarError::Malformed(format!(
"the metadata document exceeded {MAX_DOCUMENT_BYTES} bytes",
)));
}
body.extend_from_slice(&chunk);
}
Ok(body)
}