use std::{ sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; use argon2::{PasswordHash, PasswordVerifier}; use axum::{ Json, extract::State, http::{HeaderValue, StatusCode}, response::IntoResponse, }; use diesel::{ExpressionMethods, QueryDsl, dsl::insert_into}; use diesel_async::RunQueryDsl; use serde::Deserialize; use super::Response; use crate::{ AppState, error::Error, schema::*, utils::{ PASSWORD_REGEX, generate_device_name, generate_token, new_refresh_token_cookie, user_uuid_from_identifier, }, }; #[derive(Deserialize)] pub struct LoginInformation { username: String, password: String, } pub async fn response( State(app_state): State>, Json(login_information): Json, ) -> Result { if !PASSWORD_REGEX.is_match(&login_information.password) { return Err(Error::BadRequest("Bad password".to_string())); } use users::dsl; let mut conn = app_state.pool.get().await?; let uuid = user_uuid_from_identifier(&mut conn, &login_information.username).await?; let database_password: String = dsl::users .filter(dsl::uuid.eq(uuid)) .select(dsl::password) .get_result(&mut conn) .await?; let parsed_hash = PasswordHash::new(&database_password) .map_err(|e| Error::PasswordHashError(e.to_string()))?; if app_state .argon2 .verify_password(login_information.password.as_bytes(), &parsed_hash) .is_err() { return Err(Error::Unauthorized( "Wrong username or password".to_string(), )); } let refresh_token = generate_token::<32>()?; let access_token = generate_token::<16>()?; let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64; use refresh_tokens::dsl as rdsl; let device_name = generate_device_name(); insert_into(refresh_tokens::table) .values(( rdsl::token.eq(&refresh_token), rdsl::uuid.eq(uuid), rdsl::created_at.eq(current_time), rdsl::device_name.eq(&device_name), )) .execute(&mut conn) .await?; use access_tokens::dsl as adsl; insert_into(access_tokens::table) .values(( adsl::token.eq(&access_token), adsl::refresh_token.eq(&refresh_token), adsl::uuid.eq(uuid), adsl::created_at.eq(current_time), )) .execute(&mut conn) .await?; let mut response = ( StatusCode::OK, Json(Response { access_token, device_name, }), ) .into_response(); response.headers_mut().append( "Set-Cookie", HeaderValue::from_str( &new_refresh_token_cookie(&app_state.config, refresh_token).to_string(), )?, ); Ok(response) }