use poem::{
error::{InternalServerError, Unauthorized},
Endpoint, Middleware, Request, Response, Result,
};
use super::{
authorizer::{ClerkAuthorizer, ClerkError, ClerkRequest},
jwks::JwksProvider,
};
impl ClerkRequest for Request {
fn get_header(&self, key: &str) -> Option<String> {
self.header(key).map(|s| s.to_string())
}
fn get_cookie(&self, key: &str) -> Option<String> {
let jar = self.cookie();
jar.get(key).map(|c| c.value_str().to_string())
}
}
pub struct ClerkPoemMiddleware<J> {
authorizer: ClerkAuthorizer<J>,
exclude_routes: Option<Vec<String>>,
}
impl<J: JwksProvider> ClerkPoemMiddleware<J> {
pub fn new(jwks_provider: J, validate_session_cookie: bool, exclude_routes: Option<Vec<String>>) -> Self {
let authorizer = ClerkAuthorizer::new(jwks_provider, validate_session_cookie);
Self { authorizer, exclude_routes }
}
}
impl<E: Endpoint, J: JwksProvider + Send + Sync> Middleware<E> for ClerkPoemMiddleware<J>
where
E: poem::Endpoint<Output = Response>,
{
type Output = ClerkPoemMiddlewareImpl<J, E>;
fn transform(&self, ep: E) -> Self::Output {
Self::Output {
authorizer: self.authorizer.clone(),
exclude_routes: self.exclude_routes.clone(),
ep,
}
}
}
pub struct ClerkPoemMiddlewareImpl<J, E> {
authorizer: ClerkAuthorizer<J>,
exclude_routes: Option<Vec<String>>,
ep: E,
}
impl<E: Endpoint, J: JwksProvider + Send + Sync> Endpoint for ClerkPoemMiddlewareImpl<J, E>
where
E: poem::Endpoint<Output = Response>,
{
type Output = Response;
async fn call(&self, mut req: Request) -> Result<Self::Output> {
if let Some(exclude_routes) = &self.exclude_routes {
if exclude_routes.iter().any(|r| r == req.uri().path()) {
return self.ep.call(req).await;
}
}
match self.authorizer.authorize(&req).await {
Ok(jwt) => {
req.set_data(jwt);
self.ep.call(req).await
}
Err(error) => match error {
ClerkError::Unauthorized(_) => Err(Unauthorized(error)),
ClerkError::InternalServerError(_) => Err(InternalServerError(error)),
},
}
}
}