fix getting token through header cookies and not body

This commit is contained in:
2025-12-04 12:31:31 +01:00
parent 02c11137ff
commit 668173c40d
10 changed files with 58 additions and 63 deletions

View File

@@ -2,6 +2,8 @@ use axum::serve;
use axum::Extension;
use axum::extract::{ws::{Message, WebSocket, WebSocketUpgrade}, State};
use jsonwebtoken::{DecodingKey, EncodingKey};
use reqwest::header::AUTHORIZATION;
use reqwest::header::CONTENT_TYPE;
use tokio::net::TcpListener;
use tokio::sync::mpsc;
@@ -24,6 +26,8 @@ use crate::utils::auth::JwtKeys;
use std::env;
use dotenvy::dotenv;
use tower_http::cors::{CorsLayer, Any};
use axum::http::{Method, HeaderValue};
pub async fn notify_discord(msg: &str) -> Result<(), reqwest::Error> {
let payload = serde_json::json!({
@@ -88,10 +92,16 @@ std::panic::set_hook(Box::new(|info| {
};
let cors = CorsLayer::new()
.allow_origin("http://localhost:5173".parse::<HeaderValue>().unwrap())
.allow_credentials(true)
.allow_methods([Method::GET, Method::POST, Method::PUT , Method::OPTIONS])
.allow_headers([CONTENT_TYPE, AUTHORIZATION]);
let app = create_router(state)
.layer(Extension(jwt_keys));
.layer(Extension(jwt_keys))
.layer(cors);
let listener = TcpListener::bind("0.0.0.0:7080").await?;
serve(listener, app).into_future().await?;
Ok(())

View File

@@ -6,14 +6,15 @@ use axum::{
use axum_extra::extract::TypedHeader;
//use axum_extra::TypedHeader;
use headers::UserAgent;
use headers::{UserAgent, Cookie};
use axum::extract::FromRef;
use axum::extract::Request as ExtractRequest;
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header, Algorithm};
use reqwest::header::REFRESH;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use chrono::{Utc};
use chrono::{Utc, format};
use rusqlite::{params, Connection, OptionalExtension};
use rand_core::{RngCore, OsRng};
@@ -468,16 +469,15 @@ pub async fn create_refresh_token(
//TODO: get hotel name
//TODO: get hotel name to return a map/tuple of hotel name
let mut stmt = match conn.prepare(
"SELECt hotel_id FROM hotel_user_link WHERE user_id = ?1"
) {
Ok(stmt) => stmt,
Err(_) => return Err((StatusCode::INTERNAL_SERVER_ERROR, "error building user_id fetch stmt".to_string())),
};
//TODO: compiler les hotel id dans un vecteur pour le feed dans le refresh token
//Deja fait ?
//TODO: compiler les hotel id dans un vecteur pour le feed dans le refresh token
let hotel_ids: Vec<i32> = match stmt
.query_map(params![&user_id],|row| row.get (0))
{
@@ -539,7 +539,9 @@ pub async fn create_refresh_token(
*/
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
//TODO: add a map/tupple of of the allowed hotels and their id+name, maybe update the token ?
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Max-Age=60480000000;Path=/", raw_token);
let mut response = (StatusCode::CREATED, "Refresh token created successfully").into_response();
response.headers_mut().insert(
@@ -553,7 +555,7 @@ pub async fn create_refresh_token(
#[derive(Deserialize)]
pub struct LoginRefreshTokenValues{
device_id: Uuid,
refresh_token: String,
//refresh_token: String,
}
//TODO: LATER : implement hotel-id-selected to allow user to only get part hotels ?
@@ -561,29 +563,39 @@ pub async fn login_refresh_token (
State(state): State<AppState>,
Extension(keys): Extension<JwtKeys>,
user_agent: Option<TypedHeader<UserAgent>>,
cookie_header: Option<TypedHeader<headers::Cookie>>,
Json(payload): Json<LoginRefreshTokenValues>
) -> impl IntoResponse {
println!("login_refresh_token called");
// Log cookies
let cookies = match cookie_header {
Some(token) => token,
None => return (StatusCode::UNAUTHORIZED, "Missing refresh token cookie").into_response(),
};
let refresh_token = match cookies.get("refresh_token") {
Some(token) => token.to_string(),
None => return (StatusCode::UNAUTHORIZED, "Missing refresh token cookie").into_response(),
};
println!("Cookies: {:?}", &refresh_token);
let conn = match state.logs_pool.get() {
Ok(c) => c,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error").into_response(),
};
/*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 user_agent_str = match user_agent {
Some(ua) => ua.to_string(),
None => return (StatusCode::INTERNAL_SERVER_ERROR, "user agent unknown").into_response(),
};
println!("UA {:?}", &user_agent_str);
let device_id_str = payload.device_id.to_string();
println!("device id: {:?}", &device_id_str);
//"SELECT user_id, token_hash, hotel_id FROM refresh_token WHERE device_id = ?1 AND user_agent = ?2",
@@ -593,10 +605,11 @@ pub async fn login_refresh_token (
let mut stmt = match conn.prepare(
"SELECT user_id, token_hash, hotel_id_list
FROM refresh_token
WHERE device_id = ?1 AND user_agent = ?2 "
WHERE device_id = ?1 AND user_agent = ?2
LIMIT 1;"
) {
Ok(s) => s,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Error prepatring hotel_id_list stmt").into_response(),
};
let rows = match stmt.query_one(params![&device_id_str, &user_agent_str], |row| {
@@ -607,7 +620,10 @@ pub async fn login_refresh_token (
))
}) {
Ok(r) => r,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
Err(e) => {
eprintln!("DB ERROR: {:?}", e);
return (StatusCode::INTERNAL_SERVER_ERROR, format!("DB query error: {}", e)).into_response()
}
};
//TODO: extraction of the blob
//let json_hotel_ids = rows.2;
@@ -619,21 +635,11 @@ pub async fn login_refresh_token (
};
/*
let mut entries = Vec::new();
for r in rows {
match r {
Ok(t) => entries.push(t),
Err(_) => continue, // ignore corrupt rows
}
}
*/
if hotel_ids.is_empty() {
return (StatusCode::UNAUTHORIZED, "No matching device").into_response();
}
if !verify_password(&payload.refresh_token, &saved_hash) {
if !verify_password(&refresh_token, &saved_hash) {
// skip rows with wrong hash
return (StatusCode::UNAUTHORIZED, "Invelid credentials").into_response();
}
@@ -663,31 +669,6 @@ pub async fn login_refresh_token (
tokens.push(token);
}
/* OLD ITERATION OVER MULTIPLE REFRESH TOKEN
// swap to "for hotel_id in entries" // interator over vector list
for (user_id, token_hash, hotel_id) in entries {
if !verify_password(&payload.refresh_token, &token_hash) {
// skip rows with wrong hash
continue;
}
//FIXME: single expiration
let claims = serde_json::json!({
"id": user_id,
"hotel_id": hotel_id,
"exp": expiration
});
let token = match encode(&Header::default(), &claims, &keys.encoding) {
Ok(t) => t,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
};
tokens.push(token);
}
*/
if tokens.is_empty() {
return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response();
}
@@ -736,10 +717,12 @@ pub async fn logout_from_single_device (
None => return (StatusCode::UNAUTHORIZED, "No matching device").into_response(),
};
//FIXME: need to chang the way we get refresh token from the cookies instead
/*
if !verify_password(&payload.refresh_token, &token_hash) {
return (StatusCode::UNAUTHORIZED, "Invalid or mismatched token").into_response();
}
*/
let revoked: Result<String, rusqlite::Error> = conn.query_row(
"UPDATE refresh_token SET revoked = 1 WHERE id = ?1 RETURNING device_id",
params![&token_id],