use std::sync::Arc; use axum::{ async_trait, extract::FromRequestParts, http::{ header::{CONTENT_LENGTH, DATE, HOST}, request::Parts, }, Extension, }; use foxchat::{ fed::{SERVER_HEADER, SIGNATURE_HEADER, USER_HEADER}, http::ApiError, signature::{parse_date, verify_signature}, FoxError, }; use tracing::error; use crate::{app_state::AppState, model::identity_instance::IdentityInstance}; pub struct FoxRequestData { pub instance: IdentityInstance, pub user_id: Option, } #[async_trait] impl FromRequestParts for FoxRequestData where S: Send + Sync, { type Rejection = ApiError; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let state: Extension> = Extension::from_request_parts(parts, state) .await .expect("AppState was not added as an extension"); let domain = parts .headers .get(SERVER_HEADER) .ok_or(FoxError::InvalidHeader)? .to_str()?; let instance = IdentityInstance::get(state.0, domain).await?; let public_key = instance.parse_public_key()?; let date = parse_date( parts .headers .get(DATE) .ok_or(FoxError::InvalidHeader)? .to_str()?, )?; let signature = parts .headers .get(SIGNATURE_HEADER) .ok_or(FoxError::MissingSignature)? .to_str()? .to_string(); let host = parts .headers .get(HOST) .ok_or(FoxError::InvalidHeader)? .to_str()?; let content_length = if let Some(raw_length) = parts.headers.get(CONTENT_LENGTH) { Some(raw_length.to_str()?.parse::()?) } else { None }; let user_id = if let Some(raw_id) = parts.headers.get(USER_HEADER) { Some(raw_id.to_str()?) } else { None }; if let Err(e) = verify_signature( &public_key, signature, date, host, parts.uri.path(), content_length, user_id, ) { error!( "Verifying signature from request for {} from {}: {}", parts.uri.path(), domain, e ); return Err(FoxError::InvalidSignature.into()); } Ok(FoxRequestData { instance, user_id: user_id.map(|v| v.to_string()), }) } }