use anyhow::{anyhow, Result}; use base64::Engine; use hyper::{Request, Response}; use hyper::body::Incoming; use aero_user::login::ArcLoginProvider; use aero_collections::user::User; use super::codec::text_body; use super::controller::HttpResponse; type ArcUser = std::sync::Arc; pub(super) async fn auth<'a>( login: ArcLoginProvider, req: Request, next: impl Fn(ArcUser, Request) -> futures::future::BoxFuture<'a, Result>, ) -> Result { let auth_val = match req.headers().get(hyper::header::AUTHORIZATION) { Some(hv) => hv.to_str()?, None => { tracing::info!("Missing authorization field"); return Ok(Response::builder() .status(401) .header("WWW-Authenticate", "Basic realm=\"Aerogramme\"") .body(text_body("Missing Authorization field"))?) }, }; let b64_creds_maybe_padded = match auth_val.split_once(" ") { Some(("Basic", b64)) => b64, _ => { tracing::info!("Unsupported authorization field"); return Ok(Response::builder() .status(400) .body(text_body("Unsupported Authorization field"))?) }, }; // base64urlencoded may have trailing equals, base64urlsafe has not // theoretically authorization is padded but "be liberal in what you accept" let b64_creds_clean = b64_creds_maybe_padded.trim_end_matches('='); // Decode base64 let creds = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64_creds_clean)?; let str_creds = std::str::from_utf8(&creds)?; // Split username and password let (username, password) = str_creds .split_once(':') .ok_or(anyhow!("Missing colon in Authorization, can't split decoded value into a username/password pair"))?; // Call login provider let creds = match login.login(username, password).await { Ok(c) => c, Err(_) => { tracing::info!(user=username, "Wrong credentials"); return Ok(Response::builder() .status(401) .header("WWW-Authenticate", "Basic realm=\"Aerogramme\"") .body(text_body("Wrong credentials"))?) }, }; // Build a user let user = User::new(username.into(), creds).await?; // Call router with user next(user, req).await }