Last active
November 21, 2023 18:19
-
-
Save PcFerreira/5d2ef15694b4511fef7725a03c43f48a to your computer and use it in GitHub Desktop.
Rust Actix JWT Middleware
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::structs::jwt_claims::Claims; | |
use crate::structs::request_data::RequestData; | |
use actix_web::{ | |
body::{EitherBody, MessageBody}, | |
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, | |
error::ErrorForbidden, | |
Error, HttpMessage, | |
}; | |
use chrono::Utc; | |
use futures::future::{ok, LocalBoxFuture, Ready}; | |
use jsonwebtoken::{decode, DecodingKey, Validation}; | |
use std::env; | |
pub struct Authentication; | |
impl<S, B> Transform<S, ServiceRequest> for Authentication | |
where | |
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, | |
S::Future: 'static, | |
B: MessageBody + 'static, | |
{ | |
type Response = ServiceResponse<EitherBody<B>>; | |
type Error = Error; | |
type InitError = (); | |
type Transform = AuthenticationMiddleware<S>; | |
type Future = Ready<Result<Self::Transform, Self::InitError>>; | |
fn new_transform(&self, service: S) -> Self::Future { | |
ok(AuthenticationMiddleware { service }) | |
} | |
} | |
pub struct AuthenticationMiddleware<S> { | |
service: S, | |
} | |
impl<S, B> Service<ServiceRequest> for AuthenticationMiddleware<S> | |
where | |
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, | |
S::Future: 'static, | |
B: 'static, | |
{ | |
type Response = ServiceResponse<EitherBody<B>>; | |
type Error = Error; | |
type Future = LocalBoxFuture<'static, Result<ServiceResponse<EitherBody<B>>, Error>>; | |
forward_ready!(service); | |
fn call(&self, mut req: ServiceRequest) -> Self::Future { | |
let now = Utc::now(); | |
let time = now.timestamp_millis().to_string(); | |
req.headers_mut().insert( | |
actix_web::http::header::HeaderName::from_static("request-start"), | |
actix_web::http::header::HeaderValue::from_static(Box::leak(time.into_boxed_str())), | |
); | |
let request_data = RequestData { | |
key: "".to_string(), | |
request_start: now.timestamp_millis().to_string() | |
}; | |
let path = req.path().split('/'); | |
let token = path.last().unwrap_or_default(); | |
if req.path().eq("/healthcheck") { | |
let fut = self.service.call(req); | |
return Box::pin(async move { | |
let res = fut.await?; | |
Ok(res.map_into_left_body()) | |
}); | |
} | |
if token.is_empty() { | |
println!("No Token"); | |
return Box::pin(ok(req | |
.error_response(ErrorForbidden("Forbidden")) | |
.map_into_right_body())); | |
} | |
let jwt_valid: bool = match decode::<Claims>( | |
token, | |
&DecodingKey::from_secret(env::var("JWT_SECRET").expect("JWT Key not Found").as_ref()), | |
&Validation::new(jsonwebtoken::Algorithm::HS256) | |
) { | |
Err(_e) => false, | |
Ok(token_data) => { | |
req.extensions_mut().insert(token_data.claims); | |
true | |
} | |
}; | |
req.extensions_mut().insert(request_data); | |
if jwt_valid { | |
let fut = self.service.call(req); | |
Box::pin(async move { | |
let res = fut.await?; | |
Ok(res.map_into_left_body()) | |
}) | |
} else { | |
Box::pin(ok(req | |
.error_response(ErrorForbidden("Forbidden")) | |
.map_into_right_body())) | |
} | |
} | |
} | |
///------------------------------------------------- | |
#[actix_web::main] | |
async fn main() -> std::io::Result<()> { | |
HttpServer::new(|| { | |
App::new() | |
.wrap(crate::middlewares::auth_middleware::Authentication) //Add Middleware | |
.service(hello) | |
.service(echo) | |
.route("/hey", web::get().to(manual_hello)) | |
}) | |
.bind(("127.0.0.1", 8080))? | |
.run() | |
.await | |
} | |
///------------------------ | |
use serde::{Deserialize, Serialize}; | |
#[derive(Debug, Serialize, Deserialize, Clone)] | |
pub struct Claims { | |
pub iss: String, | |
pub iat: i64, | |
pub exp: i64, | |
pub sub: String, | |
} | |
////-------------------- | |
#[get("/hello/{token}")] | |
pub async fn get_document(claims: Option<web::ReqData<Claims>>) -> HttpResponse { | |
if let Some(claims) = claims { | |
iss = claims.iss.to_string(); | |
iat = claims.iat.to_string(); | |
exp = claims.ext.to_string(); | |
sub = claims.sub.to_string(); | |
} | |
HttpResponse::Ok().body("Hello world!") | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment