use axum::{ extract::{FromRequestParts, Request}, http::{StatusCode, request::Parts}, middleware::Next, response::Response, }; use jsonwebtoken::{DecodingKey, Validation, decode}; use uuid::Uuid; use crate::auth::Claims; /// Contexto de la organización extraído del JWT. #[derive(Debug, Clone)] pub struct OrgContext { pub id: Uuid, } /// Middleware que valida el token JWT y extrae el `organization_id`. pub async fn org_extractor_middleware( mut req: Request, next: Next, ) -> Result { let auth_header = req .headers() .get("authorization") .and_then(|header: &axum::http::HeaderValue| header.to_str().ok()); let token = if let Some(token_str) = auth_header.and_then(|s: &str| s.strip_prefix("Bearer ")) { token_str.to_string() } else { // Check for preview_token in query string let query = req.uri().query().unwrap_or_default(); let preview_token = query .split('&') .find(|part| part.starts_with("preview_token=")) .and_then(|part| part.split('=').nth(1)); if let Some(token) = preview_token { token.to_string() } else { return Err(StatusCode::UNAUTHORIZED); } }; // NOTA: El secreto debe venir de una variable de entorno en producción. let secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string()); let claims = decode::( &token, &DecodingKey::from_secret(secret.as_ref()), &Validation::default(), ) .map_err(|_| StatusCode::UNAUTHORIZED)? .claims; // Forzar el uso de la organización por defecto para arquitectura single-tenant let org_id = Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(); // Insertamos el contexto y las claims en las extensiones de la petición. req.extensions_mut().insert(OrgContext { id: org_id }); req.extensions_mut().insert(claims); Ok(next.run(req).await) } impl FromRequestParts for Claims where S: Send + Sync, { type Rejection = (StatusCode, &'static str); async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { parts.extensions.get::().cloned().ok_or(( StatusCode::INTERNAL_SERVER_ERROR, "Claims no encontradas. ¿El middleware está configurado?", )) } } /// Extractor de Axum para acceder fácilmente al `OrgContext` en los handlers. #[derive(Debug, Clone)] pub struct Org(pub OrgContext); impl FromRequestParts for Org where S: Send + Sync, { type Rejection = (StatusCode, &'static str); async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { let org_context = parts.extensions.get::().ok_or(( StatusCode::INTERNAL_SERVER_ERROR, "Contexto de organización no encontrado. ¿El middleware está configurado?", ))?; Ok(Org(org_context.clone())) } }