I want to access my jwt decode data to a route

jwt.rs


use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::{web, Error, HttpResponse, body::{BoxBody, MessageBody}};
use actix_web::http::header;
use actix_web::HttpMessage; // Import the HttpMessage trait
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use std::pin::Pin;
use std::future::{ready, Ready, Future};
use std::env;

pub struct Jwt;

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,   // Subject (user ID)
    email: String, // User email
    exp: usize,    // Expiration time (as timestamp)
}

impl<S, B> Transform<S, ServiceRequest> for Jwt
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: MessageBody + 'static,
{
    type Response = ServiceResponse<BoxBody>;
    type Error = Error;
    type InitError = ();
    type Transform = JwtMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(JwtMiddleware { service }))
    }
}

pub struct JwtMiddleware<S> {
    service: S,
}

type LocalBoxFuture<T> = Pin<Box<dyn Future<Output = T> + 'static>>;

impl<S, B> Service<ServiceRequest> for JwtMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: MessageBody + 'static,
{
    type Response = ServiceResponse<BoxBody>;
    type Error = Error;
    type Future = LocalBoxFuture<Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        println!("Hi from start. You requested: {:#?}", req.headers().get("Authorization"));
        let secret_key = env::var("JWT_SECRET").expect("JWT_SECRET must be set");

        let auth_header = req.headers().get("Authorization");
        let claims = if let Some(auth_header) = auth_header {
            if let Ok(auth_header_str) = auth_header.to_str() {
                if auth_header_str.starts_with("Bearer ") {
                    let token = &auth_header_str[7..];
                    let decoding_key = DecodingKey::from_secret(secret_key.as_bytes());
                    let validation = Validation::new(Algorithm::HS256);
                    match decode::<Claims>(token, &decoding_key, &validation) {
                        Ok(values) => Some(values.claims),
                        Err(err) => {
                            println!("Token error: {:?}", err);
                            None
                        }
                    }
                } else {
                    None
                }
            } else {
                None
            }
        } else {
            None
        };

        if claims.is_none() {
            let (request, _) = req.into_parts();
            let response = HttpResponse::Forbidden()
                .body("Invalid or missing token");
            let res = ServiceResponse::new(request, response);
            Box::pin(async move { Ok(res.map_into_boxed_body()) })
        } else {
            let claims = claims.unwrap();
            let mut req = req;
            req.extensions_mut().insert(claims); // Use extensions_mut
            let fut = self.service.call(req);
            Box::pin(async move {
                let res = fut.await?;
                println!("Hi from response");
                Ok(res.map_into_boxed_body())
            })
        }
    }
}

main.rs

#[post("/chat")]
async fn chat(
    req: HttpRequest,       
    body: web::Json<ChatData>, state: web::Data<AppState>) -> impl Responder {
    let db_connection = state.db.lock().unwrap();

    if body.message.trim() == "" {
        return HttpResponse::BadRequest().body("Please enter vaild input");
    };
    let claims = req.extensions().get::<Claims>();

    if let Some(claims) = claims {
        println!("User ID from claims: {}", claims.sub);
        println!("User email from claims: {}", claims.email);
        // Perform operations with the claims data here...
            HttpResponse::Ok()
    } else {
        println!("No claims available");
 return HttpResponse::HttpResponse::Forbidden().body("Invalid or missing token");
    }

 
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();
    let db = connect_db().await.expect("Failed to connect to database");
    let db_connection = Arc::new(Mutex::new(db));
    let app_state = web::Data::new(AppState {
        db: db_connection,
    });

    HttpServer::new(move || {
        App::new()
            .app_data(app_state.clone())
            .wrap(
                Cors::default()
                    .allowed_origin("http://localhost:3000")
                    .allowed_methods(vec!["GET", "POST"])
                    .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT])
                    .allowed_header(header::CONTENT_TYPE)
                    .supports_credentials()
                    .max_age(3600),
            )
            .service(login)
            .service(signup)
            .service(
                web::scope("")
                    .wrap(Jwt)
                    .service(chat)
            )
            .service(default)
            .default_service(web::get().to(not_found))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

how can access claims data in chat route

You can extend the data associated with the service request with your claims from your Middleware with ServiceRequest::extensions_mut and extract them in your handler using the ReqData extractor.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.