diff --git a/db/auth.sqlite b/db/auth.sqlite index 61c0241..5999a37 100644 Binary files a/db/auth.sqlite and b/db/auth.sqlite differ diff --git a/db/auth.sqlite-shm b/db/auth.sqlite-shm index 5eba869..51155c7 100644 Binary files a/db/auth.sqlite-shm and b/db/auth.sqlite-shm differ diff --git a/db/auth.sqlite-wal b/db/auth.sqlite-wal index 73ee404..7f2cd26 100644 Binary files a/db/auth.sqlite-wal and b/db/auth.sqlite-wal differ diff --git a/refrech_token.sql b/refrech_token.sql new file mode 100644 index 0000000..6adcce2 --- /dev/null +++ b/refrech_token.sql @@ -0,0 +1,6 @@ +id INT + user_id INTEGER NOT NULL, + token_hash TEXT NOT NULL, + device_id TEXT NOT NULL, + user_agent TEXT NOT NULL, + hotel_id TEXT \ No newline at end of file diff --git a/src/chat/handlers.rs b/src/chat/handlers.rs index 30b0d0e..4d6924e 100644 --- a/src/chat/handlers.rs +++ b/src/chat/handlers.rs @@ -22,7 +22,7 @@ use crate::utils::auth::AuthClaims; pub async fn create_conversation( State(state): State, - AuthClaims {user_id, hotel_id, username}: AuthClaims, + AuthClaims {user_id, hotel_id}: AuthClaims, CreateConversationPayload(payload): CreateConversationPayload ) -> impl IntoResponse { @@ -48,7 +48,7 @@ pub async fn create_conversation( pub async fn add_user_to_conv( State(state): State, - AuthClaims {user_id, hotel_id,username}: AuthClaims, + AuthClaims {user_id, hotel_id}: AuthClaims, AddUserConversationPayload(payload):AddUserConversationPayload ) -> impl IntoResponse { @@ -102,7 +102,7 @@ pub async fn add_user_to_conv( pub async fn send_message( State(state): State, - AuthClaims {user_id, hotel_id,username}: AuthClaims, + AuthClaims {user_id, hotel_id}: AuthClaims, SendMessagePayload(payload):SendMessagePayload ) -> impl IntoResponse { @@ -187,7 +187,7 @@ struct Message { pub async fn get_message( State(state): State, - AuthClaims {user_id, hotel_id,username}: AuthClaims, + AuthClaims {user_id, hotel_id}: AuthClaims, GetMessagesPayload(payload):GetMessagesPayload ) -> Result { diff --git a/src/inventory/handler.rs b/src/inventory/handler.rs index 0baf5f0..72e104e 100644 --- a/src/inventory/handler.rs +++ b/src/inventory/handler.rs @@ -13,7 +13,7 @@ use crate::utils::{auth::AuthClaims, db_pool::AppState}; pub async fn create_inventory_item( State(state): State, Path((item_name, item_amount)): Path<(String, i32)>, - AuthClaims{ user_id, hotel_id, username}: AuthClaims, + AuthClaims{ user_id, hotel_id}: AuthClaims, ) -> impl IntoResponse { let pool = state.hotel_pools.get_pool(hotel_id); @@ -37,7 +37,7 @@ pub async fn create_inventory_item( pub async fn update_inventory_item( State(state): State, Path((item_id, item_amount)): Path<(i32, i32)>, - AuthClaims { user_id, hotel_id, username }: AuthClaims, + AuthClaims { user_id, hotel_id }: AuthClaims, ) -> impl IntoResponse { //TODO: make better error handling : diff --git a/src/rooms/handler.rs b/src/rooms/handler.rs index ea01c5b..8212f4f 100644 --- a/src/rooms/handler.rs +++ b/src/rooms/handler.rs @@ -26,7 +26,7 @@ pub async fn hello_rooms() -> String { pub async fn fake_db_update( State(state): State, - AuthClaims { user_id, hotel_id, username }: AuthClaims, + AuthClaims { user_id, hotel_id }: AuthClaims, Path(room_id): Path, UpdateRoomPayload(payload): UpdateRoomPayload, ) -> impl IntoResponse { @@ -52,7 +52,7 @@ pub async fn fake_db_update( pub async fn clean_db_update( State(state): State, Path(room_id): Path, - AuthClaims { user_id, hotel_id, username }: AuthClaims, + AuthClaims { user_id, hotel_id }: AuthClaims, UpdateRoomPayload(payload): UpdateRoomPayload, ) -> impl IntoResponse { diff --git a/src/utils/auth.rs b/src/utils/auth.rs index 2689d2a..d8dd782 100644 --- a/src/utils/auth.rs +++ b/src/utils/auth.rs @@ -1,12 +1,6 @@ use std::time::Duration; use axum::{ - body::{to_bytes, Body}, - http::{Request as HttpRequest, StatusCode, header::{SET_COOKIE, HeaderValue} }, - http::request::Parts, - middleware::Next, - response::{Response, IntoResponse}, - Json, - extract::{Path, State, FromRequest, FromRequestParts, Extension} + body::{to_bytes, Body}, extract::{Extension, FromRequest, FromRequestParts, Path, State}, http::{header::{HeaderValue, SET_COOKIE}, request::Parts, Request as HttpRequest, StatusCode }, middleware::Next, response::{IntoResponse, IntoResponseParts, Response}, Json }; use axum_extra::extract::TypedHeader; @@ -44,11 +38,11 @@ pub struct JwtKeys { pub async fn token_tester( State(state): State, //Extension(keys): Extension, - AuthClaims { user_id, hotel_id, username }: AuthClaims, + AuthClaims { user_id, hotel_id }: AuthClaims, ) -> impl IntoResponse { format!( - "Hello {} (user_id: {}) from hotel {}", - username, user_id, hotel_id + "(user_id: {}) from hotel {}", + user_id, hotel_id ) } @@ -58,7 +52,7 @@ pub struct AuthUser(pub Claims); //?? pub struct AuthClaims { pub user_id: i32, pub hotel_id: i32, - pub username: String, + //pub username: String, } impl FromRequestParts for AuthClaims @@ -96,7 +90,7 @@ where Ok(AuthClaims { user_id: token_data.claims.id, hotel_id: token_data.claims.hotel_id, - username: token_data.claims.username, + //username: token_data.claims.username, }) } @@ -381,7 +375,7 @@ pub async fn clean_auth_loging( let claims = serde_json::json!({ "id": user_id, - "hotel_id": payload.hotel_id, + "hotel_id": hotel_id, "username": payload.username, "exp": expiration }); @@ -402,7 +396,7 @@ pub struct CreateRefreshTokenValue { pub username: String, pub password: String, pub device_id: Uuid, - pub timestamp: Option, + //pub timestamp: Option, } @@ -424,28 +418,29 @@ pub async fn create_refresh_token( let mut bytes = [0u8; 64]; OsRng.fill_bytes(&mut bytes); + let raw_token = Uuid::new_v4().to_string(); + let hashed_token = argon2 - .hash_password(&bytes, &salt) + .hash_password(raw_token.as_bytes(), &salt) .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))? .to_string(); - let raw_token = general_purpose::STANDARD.encode(&bytes); - let conn = state.logs_pool.get() .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()))?; let user_row = conn.query_row( - "SELECT id, password FROM users WHERE username = ?1", + "SELECT id, password, hotel_id FROM users WHERE username = ?1", params![&payload.username], |row| { let user_id: i32 = row.get(0)?; let password: String = row.get(1)?; - Ok((user_id, password)) + let hotel_id: i32 = row.get(2)?; + Ok((user_id, password, hotel_id)) }, ).optional() .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB get user id error".to_string()))?; - let (user_id, stored_hash) = user_row + let (user_id, stored_hash, hotel_id) = user_row .ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?; if !verify_password(&payload.password, &stored_hash) { @@ -453,8 +448,8 @@ pub async fn create_refresh_token( } conn.execute( - "INSERT INTO refresh_token (user_id, token_hash, device_id, user_agent) VALUES (?1, ?2, ?3, ?4)", - params![user_id, hashed_token, device_id_str, user_agent_str], + "INSERT INTO refresh_token (user_id, token_hash, device_id, user_agent, hotel_id) VALUES (?1, ?2, ?3, ?4, ?5)", + params![user_id, hashed_token, device_id_str, user_agent_str, hotel_id], ) .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB insert error".to_string()))?; @@ -469,6 +464,84 @@ pub async fn create_refresh_token( Ok(response) // ← Wrap in Ok() } +#[derive(Deserialize)] +pub struct LoginRefreshTokenValues{ + device_id: Uuid, + refresh_token: String, +} + +pub async fn login_refresh_token ( + State(state): State, + Extension(keys): Extension, + user_agent: Option>, + Json(payload): Json +) -> impl IntoResponse { + + let conn = match state.logs_pool.get() { + Ok(c) => c, + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error").into_response(), + }; + + let user_agent_str = user_agent + .map(|ua| ua.to_string()) + .unwrap_or_else(|| "Unknown".to_string()); + + let device_id_str = payload.device_id.to_string(); + + //"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2", + + let device_row = match conn.query_row( + "SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2", + params![&device_id_str, &user_agent_str], + |row| { + let user_id: i32 = row.get(0)?; + let token_hash: String = row.get(1)?; + let hotel_id: i32 = row.get(2)?; + //let displayname: String = row.get(3)?; + Ok((user_id, token_hash, hotel_id)) + }, + ).optional() { + Ok(opt) => opt, + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(), + }; + + + let (user_id, token_hash, hotel_id) = match device_row { + Some(tuple) => tuple, + None => return (StatusCode::UNAUTHORIZED, "No matching device").into_response(), + }; + + + if !verify_password(&payload.refresh_token, &token_hash) { + return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response(); +} + + + let expiration = match chrono::Utc::now().checked_add_signed(chrono::Duration::hours(15)) { + Some(time) => time.timestamp() as usize, + None => { + // Handle overflow — probably a 500, since this should never happen + return (StatusCode::INTERNAL_SERVER_ERROR, "Time overflow".to_string()).into_response(); + } +}; + + let claims = serde_json::json!({ + "id": user_id, + "hotel_id": hotel_id, + //"username": payload.username, + "exp": expiration + }); + + let token = match encode( + &Header::default(), + &claims, + &keys.encoding + ) { + Ok(t) => t, + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(), + }; + Json(LoginResponse { token }).into_response() +} fn internal_error(err: E) -> (StatusCode, String) { (StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {}", err)) diff --git a/src/utils/routes.rs b/src/utils/routes.rs index 9e00831..1f744aa 100644 --- a/src/utils/routes.rs +++ b/src/utils/routes.rs @@ -22,6 +22,6 @@ pub fn utils_routes() -> Router { .route("/update_password", put(UpdatePassword)) .route("/create_refresh", post(create_refresh_token)) - + .route("/login_refresh_token", post(login_refresh_token)) //.with_state(state) } \ No newline at end of file diff --git a/src/utils/websocket.rs b/src/utils/websocket.rs index 793cdb5..8c743d6 100644 --- a/src/utils/websocket.rs +++ b/src/utils/websocket.rs @@ -89,7 +89,7 @@ async fn handle_socket( } pub async fn ws_handler( - AuthClaims {user_id, hotel_id,username}: AuthClaims, + AuthClaims {user_id, hotel_id}: AuthClaims, ws: WebSocketUpgrade, State(state): State, //Path((hotel_id, user_id)): Path<(i32, i32)>,