Lines
18.85 %
Functions
4.17 %
Branches
100 %
use std::sync::Arc;
use axum::{
Extension, Json,
body::Body,
extract::State,
http::{Request, StatusCode, header},
middleware::Next,
response::IntoResponse,
};
use axum_extra::extract::cookie::CookieJar;
use serde::{Deserialize, Serialize};
use crate::{AppState, model::User, token};
use redis::AsyncCommands;
use server::db::get_connection;
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
pub status: &'static str,
pub message: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JWTAuthMiddleware {
pub user: User,
pub access_token_uuid: uuid::Uuid,
pub async fn auth(
cookie_jar: CookieJar,
State(data): State<Arc<AppState>>,
mut req: Request<Body>,
next: Next,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
let access_token = cookie_jar
.get("access_token")
.map(|cookie| cookie.value().to_string())
.or_else(|| {
req.headers()
.get(header::AUTHORIZATION)
.and_then(|auth_header| auth_header.to_str().ok())
.and_then(|auth_value| {
auth_value
.strip_prefix("Bearer ")
.map(std::borrow::ToOwned::to_owned)
})
});
let access_token = access_token.ok_or_else(|| {
let error_response = ErrorResponse {
status: "fail",
message: "You are not logged in, please provide token".to_string(),
(StatusCode::UNAUTHORIZED, Json(error_response))
})?;
let access_token_details =
match token::verify_jwt_token(&data.conf.access_token_public_key, &access_token) {
Ok(token_details) => token_details,
Err(e) => {
message: format!("{e:?}"),
return Err((StatusCode::UNAUTHORIZED, Json(error_response)));
let access_token_uuid = uuid::Uuid::parse_str(&access_token_details.token_uuid.to_string())
.map_err(|_| {
message: "Invalid token".to_string(),
let mut redis_client = data
.redis_client
.get_multiplexed_async_connection()
.await
.map_err(|e| {
status: "error",
message: format!("Redis error: {e}"),
(StatusCode::INTERNAL_SERVER_ERROR, Json(error_response))
let redis_token_user_id = redis_client
.get::<_, String>(access_token_uuid.clone().to_string())
message: "Token is invalid or session has expired".to_string(),
let user_id_uuid = uuid::Uuid::parse_str(&redis_token_user_id).map_err(|_| {
let mut conn = get_connection().await.map_err(|e| {
message: format!("DB error: {e}"),
let user = sqlx::query_as!(User, "SELECT id, user_name as name, email, user_password as password, user_role as role, photo, verified, db_name as database, created_at, updated_at FROM users WHERE id = $1", user_id_uuid)
.fetch_optional(&mut *conn)
message: format!("Error fetching user from database: {e}"),
let user = user.ok_or_else(|| {
message: "The user belonging to this token no longer exists".to_string(),
if !user.verified {
message: "The user is not verified yet".to_string(),
req.extensions_mut().insert(JWTAuthMiddleware {
user,
access_token_uuid,
Ok(next.run(req).await)
pub async fn admin(
Extension(jwtauth): Extension<JWTAuthMiddleware>,
req: Request<Body>,
if jwtauth.user.role != "admin" {
message: "The user does not have admin permissions".to_string(),