feat: add auth middleware chain with ctx resolver and cookie parsing
This commit is contained in:
79
backend/src/web/mw_auth.rs
Normal file
79
backend/src/web/mw_auth.rs
Normal 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()))
|
||||
}
|
||||
Reference in New Issue
Block a user