backend/src/api/v1/auth/login.rs
Radical a602c2624f
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
style: cargo fmt & clippy fixes
2025-07-20 16:30:46 +02:00

115 lines
2.9 KiB
Rust

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<Arc<AppState>>,
Json(login_information): Json<LoginInformation>,
) -> Result<impl IntoResponse, Error> {
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)
}