diff --git a/.gitignore b/.gitignore index 645e7bf..c3d1b75 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ frontend/node_modules/ /target .antigravitycli/ +.sqlx/ +backend/.sqlx/ +dist/ diff --git a/backend/src/handlers/qr.rs b/backend/src/handlers/qr.rs index 95a5c67..2c920cc 100644 --- a/backend/src/handlers/qr.rs +++ b/backend/src/handlers/qr.rs @@ -4,14 +4,15 @@ use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_ha use axum::{ Json, extract::{Query, State}, - http::StatusCode, - response::{IntoResponse, Redirect}, + http::{StatusCode, header::SET_COOKIE}, + response::{Html, IntoResponse, Response}, }; +use jsonwebtoken::{EncodingKey, Header}; use rand::{RngCore, thread_rng}; use serde::{Deserialize, Serialize}; use sqlx::Row; -use crate::AppState; +use crate::{AppState, cookie::jwt::encode_token}; #[derive(Serialize)] pub struct QrCodeResponse { @@ -57,12 +58,15 @@ pub async fn generate_qr_token( pub async fn validate_qr_token( State(data): State>, Query(params): Query, -) -> Result { - println!("Incoming token: {}", params.token); - let active_tokens = sqlx::query("SELECT user_id, token_hash FROM qr_codes") +) -> Response { + let active_tokens_res = sqlx::query("SELECT user_id, token_hash FROM qr_codes") .fetch_all(&data.db) - .await - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + .await; + + let active_tokens = match active_tokens_res { + Ok(tokens) => tokens, + Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(), + }; let mut authenticated_user_id: Option = None; let argon2 = Argon2::default(); @@ -82,10 +86,45 @@ pub async fn validate_qr_token( } } - if let Some(_user_id) = authenticated_user_id { - let redirect_url = format!("{}/", data.env.origin.trim_end_matches('/')); - Ok(Redirect::to(&redirect_url)) + if let Some(user_id) = authenticated_user_id { + let base_origin = data.env.origin.trim_end_matches('/'); + + let header = Header::default(); + let id_str = user_id.to_string(); + let secret_bytes = data.env.token_secret.as_bytes(); + let encoding_key = EncodingKey::from_secret(secret_bytes); + + let token = encode_token(&header, id_str, &encoding_key); + + let cookie_value = format!( + "token={}; Path=/; HttpOnly; SameSite=Lax; Max-Age=3600", + token + ); + + let redirect_html = format!( + r#" + + + + + Redirecting... + + +

Authenticated! Redirecting you to the dashboard...

+ + "#, + base_origin, base_origin + ); + + let mut response = Html(redirect_html).into_response(); + if let Ok(header_value) = axum::http::HeaderValue::from_str(&cookie_value) { + response.headers_mut().insert(SET_COOKIE, header_value); + } + + response } else { - Err(StatusCode::UNAUTHORIZED) + StatusCode::UNAUTHORIZED.into_response() } } diff --git a/backend/src/router.rs b/backend/src/router.rs index 2d84671..439e642 100644 --- a/backend/src/router.rs +++ b/backend/src/router.rs @@ -1,7 +1,10 @@ use std::sync::Arc; use axum::{ - Router, middleware, + Router, + http::StatusCode, + middleware, + response::IntoResponse, routing::{get, post}, }; @@ -18,6 +21,14 @@ use crate::{ }, }; +async fn debug_fallback_handler(uri: axum::http::Uri) -> impl IntoResponse { + println!( + "[BACKEND DEBUG] Axum Fallback triggered! Unmatched request path: {}", + uri + ); + (StatusCode::NOT_FOUND, "Backend route not found") +} + /// Creates the complete router with all API endpoints. /// /// The router is organized in layers for proper middleware application. Uses [`AppState`] @@ -90,5 +101,6 @@ pub fn create_router(state: Arc) -> Router { Router::new() .merge(protected_routes) .merge(public_routes) + .fallback(debug_fallback_handler) .with_state(state) } diff --git a/frontend/index.html b/frontend/index.html index 3386746..9c05749 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,7 +5,7 @@ - + Yew App diff --git a/frontend/src/pages/basic_pages.rs b/frontend/src/pages/basic_pages.rs index 42a286a..8201ce3 100644 --- a/frontend/src/pages/basic_pages.rs +++ b/frontend/src/pages/basic_pages.rs @@ -136,6 +136,6 @@ pub fn denied_component() -> Html { #[component(Icon)] pub fn icon_component() -> Html { html! { - + } }