use crate::jwt::token_factory; use crate::ServerState; use actix_web::web::Data; use actix_web::{post, web::Json, HttpResponse, Responder, Result}; use argon2::password_hash::rand_core::OsRng; use argon2::password_hash::SaltString; use argon2::password_hash::*; use argon2::Argon2; use argon2::PasswordHasher; use argon2::PasswordVerifier; use log::*; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] pub struct LoginData { username: String, password: String, } #[derive(Debug, Serialize, Deserialize)] struct LoginResponse { username: String, token: String, } #[derive(Debug, Serialize, Deserialize)] pub struct RegisterData { username: String, password: String, captcha: String, } #[post("/register")] pub async fn register( data: Json, state: Data, ) -> Result { // First check if the user already exists let exists = sqlx::query!( "SELECT username FROM users WHERE username = ?", data.username ) .fetch_one(&state.pool) .await .ok() .map(|row| row.username); // Bail out if the user already exists if exists.is_some() { info!("User \"{}\" already exists", data.username); return Ok(HttpResponse::BadRequest().json("Error")); } // Unwrapping here because if this fails, we have a serious problem let phc_hash = Argon2::default() .hash_password(data.password.as_bytes(), &SaltString::generate(&mut OsRng)) .unwrap() .to_string(); // Insert our new user into the database sqlx::query!( "INSERT INTO users (username, password) VALUES (?, ?)", data.username, phc_hash ) .execute(&state.pool) .await .unwrap(); info!("User: {} registered", data.username); Ok(HttpResponse::Ok().json("User registered")) } #[post("/login")] pub async fn login(data: Json, state: Data) -> Result { let uname = data.username.clone(); let q = sqlx::query!("SELECT password FROM users WHERE username = ?", uname) .fetch_one(&state.pool) .await .ok(); if q.is_none() { info!("User \"{}\" failed to log in", data.username); return Ok(HttpResponse::BadRequest().json("Error")); } let phc_password = q.unwrap().password; let phc_password = PasswordHash::new(&phc_password).unwrap_or_else(|_| { warn!( "Invalid hash for user {} fetched from database (not a valid PHC string)", data.username ); panic!(); }); match Argon2::default().verify_password(data.password.as_bytes(), &phc_password) { Ok(_) => { info!("User {} logged in", data.username); let token = token_factory(&data.username).unwrap(); println!("{:?}", token); return Ok(HttpResponse::Ok().json(LoginResponse { username: data.username.clone(), token: token, })); } Err(_) => { info!("User \"{}\" failed to log in", data.username); return Ok(HttpResponse::BadRequest().json("Error")); } } }