feat: add auth middleware chain with ctx resolver and cookie parsing

This commit is contained in:
2026-04-21 00:04:10 +03:00
parent 90c9b9d6ff
commit a120838cbc
10 changed files with 252 additions and 45 deletions

View File

@@ -0,0 +1,79 @@
use axum::{
extract::{FromRequestParts, Request},
middleware::Next,
response::Response,
};
use lazy_regex::regex_captures;
use tower_cookies::{Cookie, Cookies};
use crate::{ctx::Ctx, error::LoftError, web::AUTH_TOKEN};
/// validates the cookie exists and is well-formed (3-part format)
pub async fn mw_require_auth(
ctx: Result<Ctx, LoftError>,
req: Request,
next: Next,
) -> Result<Response, LoftError> {
ctx?;
Ok(next.run(req).await)
}
pub async fn mw_ctx_resolver(
cookies: Cookies,
mut req: Request,
next: Next,
) -> Result<Response, LoftError> {
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
let result_ctx = match auth_token
.ok_or(LoftError::AuthFailNoAuthTokenCookie)
.and_then(parse_auth_token)
{
Ok((user_id, _, _)) => {
//TODO: add validation
Ok(Ctx::new(user_id))
}
Err(e) => Err(e),
};
if result_ctx.is_err() && !matches!(result_ctx, Err(LoftError::AuthFailNoAuthTokenCookie)) {
cookies.remove(Cookie::from(AUTH_TOKEN))
}
req.extensions_mut().insert(result_ctx);
Ok(next.run(req).await)
}
impl<S: Send + Sync> FromRequestParts<S> for Ctx {
type Rejection = LoftError;
// extracts user_id from the token and makes it available to handlers as an extractor
fn from_request_parts(
parts: &mut axum::http::request::Parts,
_: &S,
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send {
async move {
parts
.extensions
.get::<Result<Ctx, LoftError>>()
.ok_or(LoftError::AuthFailCtxNotInRequestExt)?
.clone()
}
}
}
fn parse_auth_token(auth_token: String) -> Result<(u64, u64, String), LoftError> {
let (_, user_id, expiration, signature) =
regex_captures!(r"^user-(\d+)\.(\d+)\.([a-f0-9]+)$", &auth_token)
.ok_or(LoftError::AuthFailTokenWrongFormat)?;
let user_id: u64 = user_id
.parse()
.map_err(|_| LoftError::AuthFailTokenWrongFormat)?;
let expiration: u64 = expiration
.parse()
.map_err(|_| LoftError::AuthFailTokenWrongFormat)?;
Ok((user_id, expiration, signature.to_string()))
}