mabel/src/routes/auth.rs

53 lines
1.3 KiB
Rust

use axum::{extract::State, http::StatusCode, Json};
use chrono::Duration;
use serde::Deserialize;
use serde_json::{json, Value};
use std::sync::Arc;
use crate::{
auth::{verify, Session},
error::AppError,
state::AppState,
};
#[derive(Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
pub async fn login(
State(state): State<Arc<AppState>>,
Json(payload): Json<LoginRequest>,
) -> Result<Json<Value>, AppError> {
let user = sqlx::query!("SELECT * FROM users WHERE name = $1", payload.username)
.fetch_optional(&state.database)
.await?;
let invalid = || -> AppError {
AppError::ClientError {
status: StatusCode::UNAUTHORIZED,
code: "invalid-login".into(),
message: "No matching user was found".into(),
}
};
let user = user.ok_or_else(invalid)?;
let plaintext = user.password.ok_or_else(invalid)?;
if !verify(&payload.password, &plaintext)? {
return Err(invalid());
}
let session = Session::create(
&state.database,
user.id,
Duration::seconds(state.config.session_duration.into()),
)
.await?;
Ok(Json(
json!({ "session_token": session.token(), "expires_at": session.expires_at }),
))
}