Actix-web token validation in middleware

Hi all,

I'm starting working with actix-web v2.0 for publishing a small web app at work.

One of my needs is to authenticate a session token received from another service.

I thought I can do that with a middleware, but I have an issue recovering the Identity.

Here is the server code:

let server = HttpServer::new(move || {
         App::new()
             .data(AppOptions {
                 root: root_folder.clone(),
             })
             .wrap(Logger::default())
             .wrap(Logger::new("%a %{User-Agent}i"))
             .wrap(IdentityService::new(
                 CookieIdentityPolicy::new(SECRET_KEY.as_bytes())
                     .name("auth")
                    .path("/")
                     .max_age_time(chrono::Duration::days(1))
                     .secure(!dev_mode),
             ))
             .wrap(authorization::Authorization::new(
                 auth_service.clone().to_string(),
             ))
             .route("/api", web::to(ws_proxy))
             .service(static_site)
     })
     .keep_alive(keep_alive);

I set a CookieIdentityPolicy before my middleware that is authorization::Authorization.

I can access to the Identity from my service static_site without a problem.

From the middleware I just can't:

impl<S, B> Service for AuthorizationService<S>
 where
     S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
     S::Future: 'static,
     B: 'static,
 {
     type Request = ServiceRequest;
     type Response = ServiceResponse<B>;
     type Error = Error;
     type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

     fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
         self.service.poll_ready(cx)
     }

     fn call(&mut self, req: ServiceRequest) -> Self::Future {
         println!("Hi from start. You requested: {:?}", req.path());

         if req.get_identity().is_none() {
             // Should never reach this branch
             println!("Error not identity found");
             return Box::pin(async { Err(actix_web::error::ErrorUnauthorized("Unauthorized")) });
         } else {
             // Here I should check the identity first
             Box::pin( self.service.call(req) )
         }
    }
}

At the time being the code is just for understanding how a middleware works, the line with req.get_identity() always return a None, and that should be incorrect.

On documentations looks like it should be fine:

https://docs.rs/actix-identity/0.2.1/actix_identity/trait.RequestIdentity.html

Not sure what I'm doing wrong, any help would be appreciated.

Thanks.

I don't think this is documented anywhere yet, but use RequestIdentity for this:

use actix_identity::RequestIdentity;

fn call(&mut self, req: ServiceRequest) -> Self::Future {
    let identity = RequestIdentity::get_identity(&req);
}

@ddimaria thanks for reply, I changed the code as you suggested, but still the identity is None.

Not sure what I'm doing wrong here, the identity session middleware is declared before my middleware, and that, according to the documentation, should be ok to have it defined.

I also made an example with the UserSession middleware and it didn't work. Looks like I cannot read the cookie from the request header. :frowning:

I found a package https://github.com/actix/actix-web-httpauth that can be helpfull to understand this part. I'll have a look on this to see if that works and how it's different from my example.

I'll be back after that.

UPDATE

The actix-web-httpauth works, but just use the header extractors for Basic and Bearer.

That works in my example too.

It doesn't work with cookie.

I'll post my relevant auth code sections:

The server code:

let mut server = HttpServer::new(move || {
    App::new()
        .configure(add_cache)
        .wrap(Cors::new().supports_credentials().finish())
        .wrap(Logger::default())
        .wrap(get_identity_service())
        .configure(add_pool)
        .app_data(data.clone())
        .configure(routes)
});

get_identity_service():

pub fn get_identity_service() -> IdentityService<CookieIdentityPolicy> {
    IdentityService::new(
        CookieIdentityPolicy::new(&CONFIG.session_key.as_ref())
            .name(&CONFIG.session_name)
            .max_age_time(chrono::Duration::minutes(CONFIG.session_timeout))
            .secure(CONFIG.session_secure),
    )
}

For middleware, I have that in within my routes:

pub fn routes(cfg: &mut web::ServiceConfig) {
    cfg
        // Healthcheck
        .route("/health", web::get().to(get_health))
        // /api/v1 routes
        .service(
            web::scope("/api/v1")
                // Lock down routes with AUTH Middleware
                .wrap(AuthMiddleware)
2 Likes

That make sense, didn't have a chance to test it today but I will try it tomorrow.

Thanks a lot for the help @ddimaria, I didn't understood that middleware can be placed inside a server, my bad.

But that is, when I modify my code according to your suggestions, I can get the cookie session in my middleware!

1 Like