token tester for login provided token
This commit is contained in:
@@ -1,75 +1,87 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use axum::{
|
use axum::{
|
||||||
body::{to_bytes, Body},
|
body::{to_bytes, Body},
|
||||||
http::{Request as HttpRequest, StatusCode},
|
http::{Request as HttpRequest, StatusCode, },
|
||||||
|
http::request::Parts,
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
response::{Response, IntoResponse},
|
response::{Response, IntoResponse},
|
||||||
Json,
|
Json,
|
||||||
extract::{Path, State, FromRequest}
|
extract::{Path, State, FromRequest, FromRequestParts}
|
||||||
};
|
};
|
||||||
|
use axum::extract::FromRef;
|
||||||
use axum::extract::Request as ExtractRequest;
|
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::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use chrono::{Utc};
|
use chrono::{Utc};
|
||||||
use rusqlite::{params, Connection, OptionalExtension};
|
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::;
|
||||||
use crate::utils::db_pool::{HotelPool,AppState};
|
use crate::utils::db_pool::{HotelPool,AppState};
|
||||||
|
|
||||||
|
pub async fn token_tester(
|
||||||
use rand_core::OsRng;
|
State(state): State<AppState>,
|
||||||
use argon2::{
|
AuthClaims { user_id, hotel_id, username }: AuthClaims,
|
||||||
password_hash::{
|
) -> impl IntoResponse {
|
||||||
PasswordHash, PasswordHasher, PasswordVerifier, SaltString
|
format!(
|
||||||
},
|
"Hello {} (user_id: {}) from hotel {}",
|
||||||
Argon2
|
username, user_id, hotel_id
|
||||||
};
|
)
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct AuthUser(pub Claims); //??
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AuthClaims {
|
||||||
|
pub user_id: i32,
|
||||||
|
pub hotel_id: i32,
|
||||||
|
pub username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> FromRequestParts<S> for AuthClaims
|
||||||
|
where
|
||||||
|
S: Send + Sync + 'static,
|
||||||
|
AppState: Clone + Send + Sync + 'static, AppState: FromRef<S>
|
||||||
|
{
|
||||||
|
type Rejection = (StatusCode, String);
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
// 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::<Claims>(
|
||||||
|
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
|
// Hash a new password
|
||||||
fn hash_password(password: &str) -> anyhow::Result<String> {
|
fn hash_password(password: &str) -> anyhow::Result<String> {
|
||||||
@@ -172,63 +184,6 @@ struct LoginResponse {
|
|||||||
token: String,
|
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(
|
pub async fn clean_auth_loging(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
LoginPayload(payload): LoginPayload
|
LoginPayload(payload): LoginPayload
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::utils::auth::*;
|
use crate::utils::auth::*;
|
||||||
use crate::utils::db_pool::{HotelPool, AppState};
|
use crate::utils::db_pool::{HotelPool, AppState, };
|
||||||
|
|
||||||
|
|
||||||
// ROOTS
|
// ROOTS
|
||||||
@@ -13,5 +13,6 @@ pub fn utils_routes() -> Router<AppState> {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/login", put(clean_auth_loging))
|
.route("/login", put(clean_auth_loging))
|
||||||
.route("/register", put(register_user))
|
.route("/register", put(register_user))
|
||||||
|
.route("/tokentest", put(token_tester))
|
||||||
//.with_state(state)
|
//.with_state(state)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user