oidc-jwt-validator 0.2.3

Batteries included validaton of Access Tokens from an OIDC Provider
Documentation
use std::fmt::Display;
use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
use std::time::Duration;

use actix_web::FromRequest;
use actix_web::{http::StatusCode, web, App, HttpResponse, HttpServer, Responder, ResponseError};
use jsonwebtoken::TokenData;
use oidc_jwt_validator::cache::Strategy;
use oidc_jwt_validator::{ValidationSettings, Validator};
use serde::Deserialize;

async fn greet(user: Authenticated<UserClaims>) -> impl Responder {
    format!("Hello {}!", user.claims.name)
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let oidc_url = "https://keycloak.udp.lgbt/realms/Main";

    let client = reqwest::ClientBuilder::new()
        .timeout(Duration::from_secs(2))
        .build()
        .unwrap();

    let mut settings = ValidationSettings::new();
    settings.set_issuer(&[oidc_url]);
    settings.set_audience(&["account"]);

    let validator = Validator::new(oidc_url, client, Strategy::Automatic, settings)
        .await
        .unwrap();

    let _server = HttpServer::new(move || {
        App::new()
            .app_data(validator.clone())
            .route("/", web::get().to(greet))
    })
    .bind(("0.0.0.0", 8080))
    .unwrap()
    .run()
    .await;

    Ok(())
}

#[derive(Debug, Deserialize, Clone)]
struct UserClaims {
    name: String,
}

struct Authenticated<T>(TokenData<T>);

impl<T> FromRequest for Authenticated<T>
where
    T: for<'de> serde::de::Deserialize<'de> + Sized + Send + 'static,
{
    type Error = AuthError;

    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + 'static>>;

    fn from_request(
        req: &actix_web::HttpRequest,
        _payload: &mut actix_web::dev::Payload,
    ) -> Self::Future {
        let req = req.clone();
        Box::pin(async move {
            let validator = req.app_data::<Validator>().ok_or(AuthError::Failed)?;
            let token2 = req
                .headers()
                .get("Authorization")
                .ok_or(AuthError::Failed)?
                .to_str()
                .map_err(|_| AuthError::Failed)?
                .replace("Bearer ", "");

            let valid_token = validator.validate::<T>(token2).await?;

            Ok(Authenticated(valid_token))
        })
    }
}

impl<T> Deref for Authenticated<T> {
    type Target = TokenData<T>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Debug, Clone)]
enum AuthError {
    Failed,
}
impl Display for AuthError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Auth Failed")
    }
}

impl From<oidc_jwt_validator::ValidationError> for AuthError {
    fn from(_: oidc_jwt_validator::ValidationError) -> Self {
        Self::Failed
    }
}

impl ResponseError for AuthError {
    fn status_code(&self) -> StatusCode {
        match self {
            AuthError::Failed => StatusCode::UNAUTHORIZED,
        }
    }

    fn error_response(&self) -> HttpResponse {
        HttpResponse::new(self.status_code())
    }
}