I've been doing rust for just short while but didn't much struggle until this issue. I want to authenticate each request, and make returned value (from auth service) available inside requests.
I've read implementing FromRequest for the struct returned by auth server should make it available.
But I have issues implementing it correctly. It requires extract
and from_request
functions both returning self, and if I understand it a little correctly, extract is being called the first, so I should set it somewhere in request extensions, and get it from there inside from_request. I have following code:
impl FromRequest for CustomClaim {
type Error = actix_web::Error;
type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
fn extract(req: &HttpRequest) -> Self::Future {
let auth_header = req
.headers()
.get("Authorization")
.unwrap()
.to_str()
.unwrap()
.to_owned();
let boxed_future = Box::pin(async move {
let claim_future = get_claim(&auth_header).await;
claim_future
});
req.extensions_mut().insert(boxed_future);
boxed_future
}
fn from_request(req: &HttpRequest, payload: &mut actix_web::dev::Payload) -> Self::Future {
let boxed_future = req.extensions().get::<Self::Future>().unwrap();
boxed_future
}
}
async fn get_claim(auth_header: &str) -> Result<CustomClaim, actix_web::Error>
But from_request doesn't compile because it returns a reference (error message: mismatched types expected struct `Pin\<Box\<(dyn futures::Future\<Output = Result\<auth::CustomClaim, actix_web::Error\>\> + 'static)\>\>`found reference`&Pin\<Box\<dyn futures::Future\<Output = Result\<auth::CustomClaim, actix_web::Error\>\>\>\>`
)
Doing .clone() doesn't help because clone is not implemented on that future type so it still returns a reference. Can I implement clone for the future? Is my approach wrong?
Ok so I feel little dumb but the answer is - you (or I) don't need the extract
function. Only from_request
is required.
So solution is, move the code from extract
function to from_request
, omit extract
, and don't set anything on the request:
impl FromRequest for CustomClaim {
type Error = actix_web::Error;
type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
fn from_request(req: &HttpRequest, _payload: &mut actix_web::dev::Payload) -> Self::Future {
let auth_header = req
.headers()
.get("Authorization")
.unwrap()
.to_str()
.unwrap()
.to_owned();
Box::pin(async move {
let claim_future = get_claim(&auth_header).await;
claim_future
})
}
}
Then it's possible to add a parameter in authenticated handlers:
#[post("/authed-post")]
async fn authed_post(
_req: HttpRequest,
claim: CustomClaim,
// ...
) -> impl Responder