added refresh token generator

This commit is contained in:
2025-10-11 08:25:20 +02:00
parent 11c3fa56d2
commit c937ae8d83
8 changed files with 229 additions and 15 deletions

View File

@@ -1,13 +1,19 @@
use std::time::Duration;
use axum::{
body::{to_bytes, Body},
http::{Request as HttpRequest, StatusCode, },
http::{Request as HttpRequest, StatusCode, header::{SET_COOKIE, HeaderValue} },
http::request::Parts,
middleware::Next,
response::{Response, IntoResponse},
Json,
extract::{Path, State, FromRequest, FromRequestParts, Extension}
};
use axum_extra::extract::TypedHeader;
//use axum_extra::TypedHeader;
use headers::UserAgent;
use axum::extract::FromRef;
use axum::extract::Request as ExtractRequest;
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header, Algorithm};
@@ -16,14 +22,19 @@ use serde_json::Value;
use chrono::{Utc};
use rusqlite::{params, Connection, OptionalExtension};
use rand_core::OsRng;
use rand_core::{RngCore, OsRng};
use argon2::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2};
//use crate::utils::db_pool::;
use uuid::Uuid;
//use crate::utils::db_pool::;
use crate::utils::db_pool::{HotelPool,AppState};
use base64::{engine::general_purpose, Engine as _};
#[derive(Clone)]
pub struct JwtKeys {
pub encoding: EncodingKey,
@@ -386,9 +397,77 @@ pub async fn clean_auth_loging(
Json(LoginResponse { token }).into_response()
}
#[derive(Deserialize, Debug)]
pub struct CreateRefreshTokenValue {
pub username: String,
pub password: String,
pub device_id: Uuid,
pub timestamp: Option<String>,
}
#[axum::debug_handler]
pub async fn create_refresh_token(
State(state): State<AppState>,
user_agent: Option<TypedHeader<UserAgent>>,
Json(payload): Json<CreateRefreshTokenValue>
) -> Result<impl IntoResponse, (StatusCode, String)> { // ← Add Result here
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();
let argon2 = Argon2::default();
let salt = SaltString::generate(&mut OsRng);
let mut bytes = [0u8; 64];
OsRng.fill_bytes(&mut bytes);
let hashed_token = argon2
.hash_password(&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",
params![&payload.username],
|row| {
let user_id: i32 = row.get(0)?;
let password: String = row.get(1)?;
Ok((user_id, password))
},
).optional()
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB get user id error".to_string()))?;
let (user_id, stored_hash) = user_row
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
if !verify_password(&payload.password, &stored_hash) {
return Err((StatusCode::UNAUTHORIZED, "Invalid credentials".to_string()));
}
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],
)
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB insert error".to_string()))?;
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
let mut response = (StatusCode::CREATED, "Refresh token created successfully").into_response();
response.headers_mut().insert(
SET_COOKIE,
HeaderValue::from_str(&cookie_value).unwrap(),
);
Ok(response) // ← Wrap in Ok()
}
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {