Saltar al contenido principal

JWT y Middleware

JWT

  • Obtener token
  • Validar token
use std::env;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use time::{Duration, OffsetDateTime};

pub mod middleware;

#[derive(Serialize, Deserialize)]
struct Claims {
    user_id: String,
    exp: i64,
}

// Generar token
pub fn get_jwt(id: String) -> Result<String, String> {
    let jwt_secret = env::var("JWT_SECRET").expect("JWT_SECRET undefined");

    let token = encode(
        &Header::default(),
        &Claims {
            user_id: id,
            exp: (OffsetDateTime::now_utc() + Duration::minutes(1)).unix_timestamp(),
        },
        &EncodingKey::from_secret(jwt_secret.as_bytes()),
    )
    .map_err(|e| e.to_string());
   
    token
}

// Validar token
pub fn decode_jwt(token: &str) -> Result<String, String> {
    let jwt_secret = env::var("JWT_SECRET").expect("JWT_SECRET undefined");

    let token_data = decode::<Claims>(
        token,
        &DecodingKey::from_secret(jwt_secret.as_bytes()),
        &Validation::default(),
    );

    match token_data {
        Ok(data) => Ok(data.claims.user_id),
        Err(e) => Err(e.to_string()),
    }
}

Uso

// 3. Generar token
let token = match jwt::get_jwt(id.to_string()) {
Ok(token) => token,
Err(e) => {
let message = json!({"message": e});
return Err((StatusCode::INTERNAL_SERVER_ERROR, Json(message)));
}
};

Middlware

use axum::{extract::Request, http::StatusCode, middleware::Next, response::IntoResponse, Json};

use serde_json::{json, Value};

use super::decode_jwt;

pub async fn auth_middleware(
    req: Request,
    next: Next,
) -> Result<impl IntoResponse, (StatusCode, Json<Value>)> {

    let auth_header = match req.headers().get("Authorization") {
        None => {
            let message = json!({"message": "Authorization header not found"});
            return Err((StatusCode::UNAUTHORIZED, Json(message)));
        }

        Some(value) => value,
    };

    // Convertir a string
    let auth_header = match auth_header.to_str() {
        Ok(value) => value,
        Err(_) => {
            let message = json!({"message": "Failed to read Authorization header"});
            return Err((StatusCode::UNAUTHORIZED, Json(message)));
        }
    };

    // Verificar si existe el Bearer token
    if !auth_header.starts_with("Bearer ") {
        let message = json!({"message": "No Bearer token provided"});
        return Err((StatusCode::UNAUTHORIZED, Json(message)));
    }

    // Extraer token
    let token = &auth_header[7..];
   
    // Validar token
    let resut = decode_jwt(token);

    match resut {
        Ok(_jwt_payload) => Ok(next.run(req).await),
        Err(e) => Err((
            StatusCode::UNAUTHORIZED,
            Json(json!({"message": e.to_string()})),
        )),
    }
}

Aplicar middlware

/// Este módulo utiliza
/// - [x] Subgrupo de rutas para aplicar los middlewares
/// - [ ] Middleware en ruta
/// - [ ] Middleware en la raiz (src/router.rs)

pub fn author_routes() -> Router<PgPool> {
    let public_routes = Router::new()
        .route("/", get(get_all))
        .route("/:id", get(get_by_id));

    let private_routes = Router::new()
        .route("/", post(create_author))
        .route("/:id", delete(delete_author))
        .route("/:id", patch(update_author))
        .layer(middleware::from_fn(auth_middleware));

    let merge_routes = Router::new().merge(public_routes).merge(private_routes);
   
    merge_routes
}