simple Log In endpoint without encryption

This commit is contained in:
2025-09-22 23:58:07 +02:00
parent 3450c216c0
commit 007071cf12
14 changed files with 680 additions and 60 deletions

227
src/utils/auth.rs Normal file
View File

@@ -0,0 +1,227 @@
use std::time::Duration;
use axum::{
body::{to_bytes, Body},
http::{Request as HttpRequest, StatusCode},
middleware::Next,
response::{Response, IntoResponse},
Json,
extract::{Path, State, FromRequest}
};
use axum::extract::Request as ExtractRequest;
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use chrono::{Utc};
use rusqlite::{params, Connection, OptionalExtension};
//use crate::utils::db_pool::;
use crate::utils::db_pool::{
HotelPool,
AppState,
};
pub async fn auth_middleware(
mut req: HttpRequest<Body>,
next: Next,
) -> Result<Response, StatusCode> {
// 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::<Claims>(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)
}
#[derive(Deserialize, Debug)]
pub struct LoginValues {
username : String,
password : String,
hotel_id: i32,
}
pub struct LoginPayload(pub LoginValues);
impl<S> FromRequest<S> for LoginPayload
where S: Send + Sync,
{
type Rejection = (StatusCode, String);
async fn from_request(req: ExtractRequest, state: &S) -> Result<Self, Self::Rejection> {
let Json(payload) = Json::<LoginValues>::from_request(req, state)
.await
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
Ok(LoginPayload(payload))
}
}
#[derive(Deserialize,Debug, Serialize, Clone)]
struct Claims{
id: i32,
hotel_id: i32,
//display_name
username: String,
exp: usize,
}
#[derive(Serialize)]
struct LoginResponse {
token: String,
}
//pub async fn auth_register();
/*
pub async fn auth_loggin(
// State(hotel_pools): State<HotelPools>,
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<LoginResponse> or use axum::Json
return (StatusCode::UNAUTHORIZED, Json(LoginResponse { token: "".to_string() }));
}
*/
pub async fn clean_auth_loging(
State(state): State<AppState>,
LoginPayload(payload): LoginPayload
) -> impl IntoResponse {
// 1⃣ Get a connection from logs pool
let conn = match state.logs_pool.get() {
Ok(c) => c,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error").into_response(),
};
let user_row = match conn.query_row(
"SELECT id, password, hotel_id, displayname FROM users WHERE username = ?1",
params![&payload.username],
|row| {
let user_id: i32 = row.get(0)?;
let password: String = row.get(1)?;
let hotel_id: i32 = row.get(2)?;
let displayname: String = row.get(3)?;
Ok((user_id, password, hotel_id, displayname))
},
).optional() {
Ok(opt) => opt,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
};
let (user_id, stored_password, hotel_id, displayname) = match user_row {
Some(u) => u,
None => return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response(),
};
if payload.password != stored_password {
return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response();
}
let expiration = chrono::Utc::now()
.checked_add_signed(chrono::Duration::hours(15))
.unwrap()
.timestamp() as usize;
let claims = serde_json::json!({
"id": user_id,
"hotel_id": payload.hotel_id,
"username": payload.username,
"exp": expiration
});
let token = match encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(state.jwt_secret.as_ref()),
) {
Ok(t) => t,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
};
Json(LoginResponse { token }).into_response()
}
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
(StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {}", err))
}
/*
match result {
Ok(rows) if rows > 0 => (StatusCode::OK, format!("Logged")),
Ok(_) => (StatusCode::NOT_FOUND, "No used".to_string()),
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {err}")),
}
*/