Skip to content

Instantly share code, notes, and snippets.

@PcFerreira
Last active November 21, 2023 18:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PcFerreira/5d2ef15694b4511fef7725a03c43f48a to your computer and use it in GitHub Desktop.
Save PcFerreira/5d2ef15694b4511fef7725a03c43f48a to your computer and use it in GitHub Desktop.
Rust Actix JWT Middleware
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