#optional authorization

1 messages · Page 1 of 1 (latest)

noble wedge
#

Hello!
I am trying to make a media route for my API where some files allow anybody to access them while others require authorization through a Bearer token. As a result, I would like to have one /media route that has an optional ReqData for a valid TokenClaims struct (consisting of a user ID). However, I can't figure out how I am supposed to do this!
Right now, I can either require authorization for all /media queries by adding .service(scope("").wrap(bearer_middleware).service(media::get_media)) to my app builder, or not require (nor get) any authorization by putting .service(media::get_media). The problem lies where if I require authorization for all media queries, the requests with no auth get shut down with a 401. If I don't require authorization, then the Bearer token gets ignored and the user can't access their file.
Here is my get_media route: ```rs
#[get("/media/{path:.*}")]
pub async fn get_media(
request: HttpRequest,
request_user: Option<ReqData<TokenClaims>>,
) -> impl Responder {
let path: PathBuf = match request.match_info().query("path").parse() {
Ok(path) => path,
// potentially useless, just no chance of panicking
Err(_) => return HttpResponse::BadRequest().body("Failed to query path".to_string()),
};

// Authorization
let user_id = match request_user {
    Some(user) => user.user_id,
    // Having an ID of 0 signifies no auth
    None => 0,
};

// snip (getting and returning the file)

}
``` and request_user is following expected behavior where its None when I do the no authorization needed approach but has the TokenClaims struct when I require authorization.
A workaround I can think of is having a /secure/media route and a /media route, but I'd prefer them to just fall under /media.
Any help would be much appreciated!

noble wedge
#

I found a solution from https://github.com/actix/actix-extras/issues/295#issuecomment-1364422771!
In my validator function, I just do

pub async fn validator(
    request: ServiceRequest,
    credentials: Option<BearerAuth>,
) -> Result<ServiceRequest, (ActixWebError, ServiceRequest)> {
    if credentials.is_none() {
        return Ok(request);
    }
    let credentials = credentials.unwrap();
    // snip
}

and use HttpAuthentication::with_fn(validator) in place of HttpAuthentication::bearer(validator)
which allows me to handle the auth in my services instead

sacred isle
#

Good find, thanks for posting the solution! I imagine the purpose of this would be to return Ok(request) only in cases where request.path() is a route you want unprotected? That way you could wrap a whole service (or app) with authentication and have a single location where the unprotected routes are controlled