added refresh token generator
This commit is contained in:
96
Cargo.lock
generated
96
Cargo.lock
generated
@@ -58,11 +58,12 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.4"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
|
checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
"axum-macros",
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
@@ -78,8 +79,7 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
"serde_core",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
@@ -95,9 +95,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.5.2"
|
version = "0.5.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
|
checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -106,13 +106,47 @@ dependencies = [
|
|||||||
"http-body-util",
|
"http-body-util",
|
||||||
"mime",
|
"mime",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-extra"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"axum-core",
|
||||||
|
"bytes",
|
||||||
|
"cookie",
|
||||||
|
"futures-util",
|
||||||
|
"headers",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
|
"mime",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustversion",
|
||||||
|
"serde_core",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-macros"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.75"
|
version = "0.3.75"
|
||||||
@@ -244,6 +278,17 @@ dependencies = [
|
|||||||
"inout",
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.7"
|
version = "0.8.7"
|
||||||
@@ -478,6 +523,30 @@ dependencies = [
|
|||||||
"hashbrown 0.15.5",
|
"hashbrown 0.15.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "headers"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"headers-core",
|
||||||
|
"http",
|
||||||
|
"httpdate",
|
||||||
|
"mime",
|
||||||
|
"sha1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "headers-core"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hotel-api-rs"
|
name = "hotel-api-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -485,11 +554,14 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
"axum",
|
"axum",
|
||||||
|
"axum-extra",
|
||||||
|
"base64",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"headers",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"r2d2",
|
"r2d2",
|
||||||
"r2d2_sqlite",
|
"r2d2_sqlite",
|
||||||
@@ -498,6 +570,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1247,9 +1320,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.26.2"
|
version = "0.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084"
|
checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
@@ -1307,9 +1380,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.26.2"
|
version = "0.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13"
|
checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
@@ -1355,6 +1428,7 @@ dependencies = [
|
|||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"rand",
|
"rand",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ edition = "2024"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = {version = "0.8.4", features = ["ws"]}
|
axum = {version = "0.8.6", features = ["ws", "macros"]}
|
||||||
|
axum-extra = { version = "0.10.3", features = ["typed-header", "cookie"] }
|
||||||
|
headers = "0.4"
|
||||||
|
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
@@ -24,6 +27,8 @@ anyhow = "1.0.100"
|
|||||||
argon2 = {version = "0.5.3"}
|
argon2 = {version = "0.5.3"}
|
||||||
rand_core = {version = "0.6.4", features = ["getrandom"]}
|
rand_core = {version = "0.6.4", features = ["getrandom"]}
|
||||||
futures-util = {version = "0.3.31"}
|
futures-util = {version = "0.3.31"}
|
||||||
|
uuid = {version = "1.18.1", features = ["serde"] }
|
||||||
|
base64 = "0.22.1"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
db/auth.sqlite
BIN
db/auth.sqlite
Binary file not shown.
Binary file not shown.
Binary file not shown.
51
src/note_endpoint_json.txt
Normal file
51
src/note_endpoint_json.txt
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct GetMessagesValues {
|
||||||
|
pub conv_id: u32,
|
||||||
|
pub timestamp: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn get_message(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
AuthClaims { user_id, hotel_id, username }: AuthClaims,
|
||||||
|
Json(payload): Json<GetMessagesValues>, // ✅ no custom payload wrapper
|
||||||
|
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||||
|
|
||||||
|
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||||
|
|
||||||
|
let conn = pool.get()
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Pool error".to_string()))?;
|
||||||
|
|
||||||
|
let from_time = match payload.timestamp.as_deref() {
|
||||||
|
Some("0") | None => "1970-01-01 00:00:00",
|
||||||
|
Some(ts) => ts,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut stmt = conn.prepare(
|
||||||
|
"SELECT id, sender_id, content, sent_at
|
||||||
|
FROM message
|
||||||
|
WHERE conversation_id = ?1
|
||||||
|
AND sent_at > ?2
|
||||||
|
ORDER BY sent_at DESC
|
||||||
|
LIMIT 50"
|
||||||
|
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Prepare failed".to_string()))?;
|
||||||
|
|
||||||
|
let messages = stmt.query_map(
|
||||||
|
params![payload.conv_id, from_time],
|
||||||
|
|row| {
|
||||||
|
Ok(Message {
|
||||||
|
id: row.get(0)?,
|
||||||
|
sender_id: row.get(1)?,
|
||||||
|
content: row.get(2)?,
|
||||||
|
sent_at: row.get(3)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string()))?
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Collect failed".to_string()))?;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
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, header::{SET_COOKIE, HeaderValue} },
|
||||||
http::request::Parts,
|
http::request::Parts,
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
response::{Response, IntoResponse},
|
response::{Response, IntoResponse},
|
||||||
Json,
|
Json,
|
||||||
extract::{Path, State, FromRequest, FromRequestParts, Extension}
|
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::FromRef;
|
||||||
use axum::extract::Request as ExtractRequest;
|
use axum::extract::Request as ExtractRequest;
|
||||||
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header, Algorithm};
|
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header, Algorithm};
|
||||||
@@ -16,14 +22,19 @@ use serde_json::Value;
|
|||||||
use chrono::{Utc};
|
use chrono::{Utc};
|
||||||
use rusqlite::{params, Connection, OptionalExtension};
|
use rusqlite::{params, Connection, OptionalExtension};
|
||||||
|
|
||||||
use rand_core::OsRng;
|
use rand_core::{RngCore, OsRng};
|
||||||
use argon2::{
|
use argon2::{
|
||||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||||
Argon2};
|
Argon2};
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
//use crate::utils::db_pool::;
|
//use crate::utils::db_pool::;
|
||||||
use crate::utils::db_pool::{HotelPool,AppState};
|
use crate::utils::db_pool::{HotelPool,AppState};
|
||||||
|
|
||||||
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct JwtKeys {
|
pub struct JwtKeys {
|
||||||
pub encoding: EncodingKey,
|
pub encoding: EncodingKey,
|
||||||
@@ -386,9 +397,77 @@ pub async fn clean_auth_loging(
|
|||||||
Json(LoginResponse { token }).into_response()
|
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) {
|
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ use axum::{
|
|||||||
use crate::utils::auth::*;
|
use crate::utils::auth::*;
|
||||||
use crate::utils::db_pool::{HotelPool, AppState, };
|
use crate::utils::db_pool::{HotelPool, AppState, };
|
||||||
use crate::utils::websocket::ws_handler;
|
use crate::utils::websocket::ws_handler;
|
||||||
|
use headers::UserAgent;
|
||||||
|
use axum_extra::TypedHeader;
|
||||||
|
|
||||||
|
|
||||||
// ROOTS
|
// ROOTS
|
||||||
pub fn utils_routes() -> Router<AppState> {
|
pub fn utils_routes() -> Router<AppState> {
|
||||||
@@ -18,5 +21,7 @@ pub fn utils_routes() -> Router<AppState> {
|
|||||||
.route("/force_update_password", put(ForceUpdatePassword))
|
.route("/force_update_password", put(ForceUpdatePassword))
|
||||||
.route("/update_password", put(UpdatePassword))
|
.route("/update_password", put(UpdatePassword))
|
||||||
|
|
||||||
|
.route("/create_refresh", post(create_refresh_token))
|
||||||
|
|
||||||
//.with_state(state)
|
//.with_state(state)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user