diff --git a/src/utils/auth.rs b/src/utils/auth.rs index 62c349d..dce120f 100644 --- a/src/utils/auth.rs +++ b/src/utils/auth.rs @@ -1,75 +1,87 @@ use std::time::Duration; use axum::{ body::{to_bytes, Body}, - http::{Request as HttpRequest, StatusCode}, + http::{Request as HttpRequest, StatusCode, }, + http::request::Parts, middleware::Next, response::{Response, IntoResponse}, Json, - extract::{Path, State, FromRequest} + extract::{Path, State, FromRequest, FromRequestParts} }; +use axum::extract::FromRef; use axum::extract::Request as ExtractRequest; -use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header}; +use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header, Algorithm}; use serde::{Deserialize, Serialize}; use serde_json::Value; use chrono::{Utc}; use rusqlite::{params, Connection, OptionalExtension}; +use async_trait::async_trait; - +use rand_core::OsRng; +use argon2::{ + password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, + Argon2}; //use crate::utils::db_pool::; use crate::utils::db_pool::{HotelPool,AppState}; - -use rand_core::OsRng; -use argon2::{ - password_hash::{ - PasswordHash, PasswordHasher, PasswordVerifier, SaltString - }, - Argon2 -}; - - -pub async fn auth_middleware( - mut req: HttpRequest, - next: Next, -) -> Result { - // buffer body - let body = std::mem::take(req.body_mut()); - - // Buffer into bytes - let bytes = to_bytes(body, 1024 * 1024) - .await - .map_err(|_| StatusCode::BAD_REQUEST)?; - - - // parse JSON as generic `Value` - let value: Value = serde_json::from_slice(&bytes) - .map_err(|_| StatusCode::BAD_REQUEST)?; - - // pull out token - let token = value - .get("token") - .and_then(|t| t.as_str()) - .ok_or(StatusCode::UNAUTHORIZED)?; - - // verify token - let key = DecodingKey::from_secret("your_jwt_secret_key".as_ref()); - let validation = Validation::default(); - let claims = decode::(token, &key, &validation) - .map_err(|_| StatusCode::UNAUTHORIZED)? - .claims; - - // inject claims for downstream handlers - req.extensions_mut().insert(claims); - - // restore the original body so the handler can parse it into *its own* struct - req = req.map(|_| Body::from(bytes)); - - - Ok(next.run(req).await) +pub async fn token_tester( + State(state): State, + AuthClaims { user_id, hotel_id, username }: AuthClaims, +) -> impl IntoResponse { + format!( + "Hello {} (user_id: {}) from hotel {}", + username, user_id, hotel_id + ) } +pub struct AuthUser(pub Claims); //?? +#[derive(Debug, Clone)] +pub struct AuthClaims { + pub user_id: i32, + pub hotel_id: i32, + pub username: String, +} + +impl FromRequestParts for AuthClaims +where + S: Send + Sync + 'static, + AppState: Clone + Send + Sync + 'static, AppState: FromRef + { + type Rejection = (StatusCode, String); + + async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { + // We assume your state has a `jwt_secret` field + + // 1️⃣ Extract the token from the Authorization header + let auth_header = parts + .headers + .get("Authorization") + .ok_or((StatusCode::UNAUTHORIZED, "Missing Authorization header".to_string()))? + .to_str() + .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid Authorization header".to_string()))?; + + // Bearer token? + let token = auth_header + .strip_prefix("Bearer ") + .ok_or((StatusCode::BAD_REQUEST, "Expected Bearer token".to_string()))?; + + // 2️⃣ Decode the token + let token_data = decode::( + token, + &DecodingKey::from_secret("your_jwt_secret_key s".to_string().as_ref()), + &Validation::new(Algorithm::HS256), + ).map_err(|_| (StatusCode::UNAUTHORIZED, "Invalid token".to_string()))?; + + Ok(AuthClaims { + user_id: token_data.claims.id, + hotel_id: token_data.claims.hotel_id, + username: token_data.claims.username, + }) + + } +} // Hash a new password fn hash_password(password: &str) -> anyhow::Result { @@ -172,63 +184,6 @@ struct LoginResponse { token: String, } -//pub async fn auth_register(); -/* -pub async fn auth_loggin( - // State(hotel_pools): State, - LoginPayload(payload): LoginPayload, -) -> impl IntoResponse { - - let pool = .get_pool(payload.hotel_id); - let conn = pool.get().unwrap(); - /* - let conn = match pool.get() { - Ok(conn) => conn, - Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")), - }; -*/ - let mut statement = conn - .prepare("SELECT id, displayname, hotel_id FROM users WHERE username = ? 1 AND password = ?2 ") - .unwrap(); - - let user_row = statement - .query_row(params![&payload.username, &payload.password], |row| { - let id: i32 = row.get(0)?; - let displayname: String = row.get(1)?; - let hotel_id: i32 = row.get(2)?; - Ok((id, displayname, hotel_id)) - }) - .optional() - .unwrap(); // returns Ok(Some(...)) or Ok(None) - - if let Some((id, displayname, hotel_id)) = user_row { - let expiration = chrono::Utc::now() - .checked_add_signed(chrono::Duration::minutes(15)) - .unwrap() - .timestamp() as usize; - - let claims = Claims { - id, - hotel_id, - username: displayname, // or payload.username if you prefer - exp: expiration, - }; - - let token = encode( - &Header::default(), - &claims, - &EncodingKey::from_secret("TEST".as_bytes()), - ) - .unwrap(); - - return (StatusCode::OK, Json(LoginResponse { token })); - } - // Fallback if login failed — wrap String in Json or use axum::Json - return (StatusCode::UNAUTHORIZED, Json(LoginResponse { token: "".to_string() })); -} - -*/ - pub async fn clean_auth_loging( State(state): State, LoginPayload(payload): LoginPayload diff --git a/src/utils/routes.rs b/src/utils/routes.rs index 7a55a28..6974982 100644 --- a/src/utils/routes.rs +++ b/src/utils/routes.rs @@ -4,7 +4,7 @@ use axum::{ }; use crate::utils::auth::*; -use crate::utils::db_pool::{HotelPool, AppState}; +use crate::utils::db_pool::{HotelPool, AppState, }; // ROOTS @@ -13,5 +13,6 @@ pub fn utils_routes() -> Router { Router::new() .route("/login", put(clean_auth_loging)) .route("/register", put(register_user)) + .route("/tokentest", put(token_tester)) //.with_state(state) } \ No newline at end of file