Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[actix-web-httpauth] Custom response body when Bearer token is missing? #156

Closed
PauMAVA opened this issue Feb 12, 2021 · 21 comments
Closed
Labels
A-httpauth Project: actix-web-httpauth

Comments

@PauMAVA
Copy link

PauMAVA commented Feb 12, 2021

Hello!

I was wondering if there is any support for body/JSON content when the Authorization: Bearer <token> header is missing. I have the following HttpAutentication when creating the server app:

HttpServer::new(move || {
        let auth_validator = HttpAuthentication::bearer(jwt_validate);
        App::new()
            .wrap(Logger::default())
            .wrap(auth_validator)
            .service(responders::hello)
}).bind("127.0.0.1:80")?.run().await?;

The jwt_validate function is:

async fn jwt_validate(req: ServiceRequest, credentials: BearerAuth) -> Result<ServiceRequest, Error> {
    if req.path().starts_with("/api/v1/auth") {
        return Ok(req)
    }
    let parsed = match auth::decode_token(credentials.token().into()) {
        Ok(val) => val,
        Err(err) => return Err(Error::from(err))
    };
    match auth::validate_jwt(parsed) {
        Ok(_val) => Ok(req),
        Err(err) => return Err(Error::from(err))
    }
}

This works fine (access is granted when the token is correct and access is denied when it's not), but when I don't use the header Authorization: Bearer <token> I just get a 401 Unauthorized code with an empty body. Is there a way of adding a custom JSON body to the 401 Unauthorized error response using actix-web-httpauth?

Thanks in advance!

@robjtede
Copy link
Member

robjtede commented Feb 12, 2021

You can compose this with the errhandlers middleware.

If you need to pass data to it, consider request extensions (search docs for extensions_mut).

@PauMAVA
Copy link
Author

PauMAVA commented Feb 16, 2021

Hey! Thanks for your quick response. 😄

I have searched the docs for actix_web::middleware::errhandlers and used the following middleware error handler:

.wrap(ErrorHandlers::new().handler(actix_web::http::StatusCode::UNAUTHORIZED, |mut res| {
    println!("Inside handler!");
    res = res.map_body(|_,_| ResponseBody::Body(Body::from(serde_json::json!(build_error_response(
        ApiErrorCode::MissingToken as u64,
        ApiErrorMessage::MissingToken.value(),
        None
    )).to_string())));
   Ok(ErrorHandlerResponse::Response(res))
}))

It is set after .wrap(auth_validator).

I'm still getting the status code without body, in fact, the println! macro is never executed, so the handler is never called. I'm quite new to actix. Any idea of what am I doing wrong?

@PauMAVA
Copy link
Author

PauMAVA commented Feb 22, 2021

Does anyone know what I might be doing wrong?

@robjtede
Copy link
Member

probably same bug as #127

@PauMAVA
Copy link
Author

PauMAVA commented Feb 22, 2021

I've taken a look at #127 but I am not using actix-cors.

$ cargo tree | grep actix-cors
$ 

This is the postman response:
imagen
imagen
The red logo at the top is only a self-signed cert warning.

If you need more info I will gladly provide it.

@PauMAVA
Copy link
Author

PauMAVA commented Feb 23, 2021

If not, another option would be to add a function on_extraction_error on HttpAuthentication and HttpAuthenticationMiddleware which would take a EF: Fn(&ServiceRequest, Error) -> Result<ServiceResponse<_>, _> and save it in an optional struct field Option<EF> in HttpAuthentication and HttpAuthenticationMiddleware. Finally on middleware.rs:180 check if the optional callback is set, if it Some call it and return the modified request otherwise return the default request.

...  
async move {
    let (req, credentials) = match Extract::<T>::new(req).await {
        Ok(req) => req,
        Err((err, req)) => {
            return if let Some(callback) = self.extraction_error_callback  {
                callback(req, err);
            } else {
                Ok(req.error_response(err));
            }
    }
};
...

It's just a draft so I know it might have some errors. What do you think about it? If you want I can try to implement it.

@JohnTitor JohnTitor added the A-httpauth Project: actix-web-httpauth label Mar 23, 2021
@Chaostheorie
Copy link

@PauMAVA I would really appreciate this for actix-web-httpauth. I'm not quite sure how to handle this otherwise. Though is this an acceptable solution for @robjtede ?

@PauMAVA
Copy link
Author

PauMAVA commented Mar 29, 2021

Hey. After some hours I managed to achieve what I was looking for. I just pushed the changes in my fork so if you want you and specially @robjtede can review them before I open a PR. Probably my approach is not the best or contains some rookie errors as I'm quite new to rust.

Check them out here: https://github.com/PauMAVA/actix-extras/tree/extraction-error-handler

I'm using the error callback as:

fn on_missing_header(req: ServiceRequest, _err: actix_web::error::Error) -> Result<ServiceResponse, actix_web::error::Error> {
    Ok(req.into_response(
        HttpResponse::Unauthorized().json(serde_json::json!(build_error_response(
                ApiErrorCode::MissingToken as u64,
                ApiErrorMessage::MissingToken.value(),
                None
        )))
    ))
}

HttpServer::new(move || {
        let auth_validator = HttpAuthentication::bearer(jwt_validate)
            .on_extraction_error(Box::new(on_missing_header))
            .exclude_path("/api/v?/auth/*")
            .exclude_path("/hello");
            ...
})

Also, note I added a function exclude_path which excludes some paths from being authenticated using glob patterns.

@Chaostheorie
Copy link

Chaostheorie commented Mar 29, 2021

@PauMAVA That sounds awesome. Thank you for your work, I hope it gets merged. I've recently got the usual ErrorHandlers middleware, with actix-cors and bearer, working too. After looking at your version it seems, at least to me, like the better solution to rely on your implementation though since it involves fewer calls and also doesn't require wrapping around another middleware.

Edit: Thank you for adding exclude_path. This really helps with routing

@PauMAVA
Copy link
Author

PauMAVA commented Apr 1, 2021

Thoughts about this? @robjtede

@cinatic
Copy link

cinatic commented Sep 21, 2021

i as a user would appreciate if i could decide myself what happens if the auth header is missing. How about adding an additional method with Optional<BearerAuth> so that this case, if needed, can be handled in the validator as well?

@aliemjay
Copy link
Member

@PauMAVA Your solution is pretty good. Please consider opening a PR!

@robjtede
Copy link
Member

should be doable now with #205; released in actix-web-httpauth v0.6.0-beta.4

@lermana
Copy link

lermana commented Jan 19, 2024

In case anyone ends up here like I did, searching for optional Bearer capabilities, this answer (on a linked PR above) shows a nice example of how to do it: #295 (comment)

@EvilWatermelon
Copy link

Is this still active? I'm looking for exactly this function in order to make a custom response when the token header is missing.

@lermana
Copy link

lermana commented May 3, 2024

@EvilWatermelon did you see this? #295

@EvilWatermelon
Copy link

EvilWatermelon commented May 3, 2024

Yes, but using .wrap(HttpAuthentication::with_fn(validator)) did not worked. Do I have to implement the ErrorHandlers too?

@lermana
Copy link

lermana commented May 3, 2024

I believe you want .wrap(from_fn( ... (note that you did something different), and the "validator" you pass should have a signature like:

async fn authn(
    app_data: web::Data<MyAppDataType>, // optional + app-specific
    req: ServiceRequest,
    next: Next<impl MessageBody>,
) -> Result<ServiceResponse<impl MessageBody>, Error> {

@EvilWatermelon
Copy link

I did not saw that I can use Optional<BearerAuth> in order to get my custom error. I apologize for taking up your time for this.

@lermana
Copy link

lermana commented May 3, 2024

Np! here's where from_fn comes from by the way:

use actix_web_lab::middleware::from_fn;

@robjtede
Copy link
Member

The HttpAuthentication::with_fn docs have been improved to better demonstrate optional auth & custom no-auth responses.

@actix actix locked as resolved and limited conversation to collaborators Jun 11, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-httpauth Project: actix-web-httpauth
Projects
None yet
Development

No branches or pull requests

8 participants