use crate::validators::{
authorizer::{ClerkAuthorizer, ClerkError, ClerkRequest},
jwks::JwksProvider,
};
use actix_web::{
body::EitherBody,
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
error::Error,
HttpMessage, HttpResponse,
};
use futures_util::future::LocalBoxFuture;
use std::{
future::{ready, Ready},
rc::Rc,
};
impl ClerkRequest for ServiceRequest {
fn get_header(&self, key: &str) -> Option<String> {
match self.headers().get(key) {
Some(header) => Some(header.to_str().expect("header must be a string").to_string()),
None => None,
}
}
fn get_cookie(&self, key: &str) -> Option<String> {
match self.cookie(key) {
Some(cookie) => Some(cookie.value().to_string()),
None => None,
}
}
}
pub struct ClerkMiddleware<J> {
pub authorizer: ClerkAuthorizer<J>,
pub routes: Option<Vec<String>>,
}
impl<J: JwksProvider> ClerkMiddleware<J> {
pub fn new(jwks_provider: J, routes: Option<Vec<String>>, validate_session_cookie: bool) -> Self {
let authorizer = ClerkAuthorizer::new(jwks_provider, validate_session_cookie);
Self { authorizer, routes }
}
}
impl<S: 'static, B, J> Transform<S, ServiceRequest> for ClerkMiddleware<J>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
J: JwksProvider + 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type InitError = ();
type Transform = ClerkMiddlewareService<S, J>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(ClerkMiddlewareService {
service: Rc::new(service),
authorizer: self.authorizer.clone(),
routes: self.routes.clone(),
}))
}
}
pub struct ClerkMiddlewareService<S, J> {
service: Rc<S>,
authorizer: ClerkAuthorizer<J>,
routes: Option<Vec<String>>,
}
impl<S: 'static, B, J> Service<ServiceRequest> for ClerkMiddlewareService<S, J>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
J: JwksProvider + 'static,
{
type Response = ServiceResponse<EitherBody<B>>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
forward_ready!(service);
fn call(&self, request: ServiceRequest) -> Self::Future {
let svc = self.service.clone();
let authorizer = self.authorizer.clone();
match &self.routes {
Some(route_matches) => {
let path = request.path();
let path_not_in_specified_routes = route_matches.iter().find(|&route| route == path).is_none();
if path_not_in_specified_routes {
return Box::pin(async move {
let res = svc.call(request).await?;
return Ok(res.map_into_left_body());
});
}
}
None => {}
}
Box::pin(async move {
match authorizer.authorize(&request).await {
Ok(jwt) => {
request.extensions_mut().insert(jwt);
let res = svc.call(request).await?;
return Ok(res.map_into_left_body());
}
Err(error) => {
match error {
ClerkError::Unauthorized(msg) => {
return Ok(ServiceResponse::new(
request.into_parts().0,
HttpResponse::Unauthorized().body(msg).map_into_right_body(),
))
}
ClerkError::InternalServerError(msg) => {
return Ok(ServiceResponse::new(
request.into_parts().0,
HttpResponse::InternalServerError().body(msg).map_into_right_body(),
))
}
};
}
}
})
}
}