token tester for login provided token
This commit is contained in:
@@ -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<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 async fn token_tester(
|
||||
State(state): State<AppState>,
|
||||
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<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
|
||||
fn hash_password(password: &str) -> anyhow::Result<String> {
|
||||
@@ -172,63 +184,6 @@ 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
|
||||
|
||||
Reference in New Issue
Block a user