Can't add user info to current request instance after validate the jwt token in actix web middleware in rust

23 views Asked by At

I'm writing a middleware to validate a jwt token and add current authicated user to the request instance for protected routes in Actix web.

here is my code

use std::{
    future::{ready, Ready},
    rc::Rc
};
 
use actix_web::{
    body::EitherBody,
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
    http::header::AUTHORIZATION,
    Error, HttpMessage, HttpResponse,
};
use futures_util::{future::LocalBoxFuture, FutureExt};
use serde_json::json;


struct AuthUser {
    id: usize,
    email: Option<String>
}
 
pub struct JwtAuthChecker;
 
impl<S, B> Transform<S, ServiceRequest> for JwtAuthChecker
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, // update here
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type InitError = ();
    type Transform = AuthenticationMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;
 
    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(AuthenticationMiddleware {
            service: Rc::new(service), // convert S to Rc<S>
        }))
    }
}
 
pub struct AuthenticationMiddleware<S> {
    service: Rc<S>,
}
 
impl<S, B> Service<ServiceRequest> for AuthenticationMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, // update here
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
 
    forward_ready!(service);
 
    fn call(&self, req: ServiceRequest) -> Self::Future {

        // Do something with the request here
        let auth = req.headers().get(AUTHORIZATION);
        if auth.is_none() {
            let response = json!({
                "status": false,
                "message": "Unauthorized"
            });
            let http_res = HttpResponse::Unauthorized().json(response);
            let (http_req, _) = req.into_parts();
            let res = ServiceResponse::new(http_req, http_res);
            // Map to R type
            return (async move { Ok(res.map_into_right_body()) }).boxed_local();
        }

        let jwt_token = get_jwt_token_from_header(auth.unwrap().to_str().unwrap());


        // Clone the service to keep reference after moving into async block
        let service = self.service.clone();
       

        async move {
            // Getting some data here (just demo code for async function)
            let user = get_some_data(jwt_token).await;
            req.extensions_mut().insert(user);

 
            // Continue with the next middleware / handler
            let res = service.call(req).await?;
            // Map to L type
            Ok(res.map_into_left_body())
        }.boxed_local()

    }
}
 
async fn get_some_data(token: &str) -> AuthUser {
    // will fetch user data from token payload here
    let user = AuthUser {
        id: 1,
        email: Some(String::from("[email protected]"))
    };

    return  user;
}

fn get_jwt_token_from_header(jwt_token: &str) -> &str {
    let bytes = jwt_token.as_bytes();

    let token_len = jwt_token.len();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            let next_index = i + 1;
            return &jwt_token[next_index..token_len];
        }
    }

    return &jwt_token[0..token_len];
}

I'm getting confused inside the async block in the call() method how I can validate the jwt token here and put the user instance in the current request instance?

I have followed this tutorial on ginkcode but didn't get it properly

0

There are 0 answers