use axum::{ extract::{Json, State}, http::StatusCode, response::{IntoResponse, Response}, }; use crate::{ app::App, clock::RequestedAt, empty::Empty, error::Internal, name::Name, password::Password, token::{app, extract::IdentityCookie}, }; #[cfg(test)] mod test; pub async fn handler( State(app): State, RequestedAt(now): RequestedAt, identity: IdentityCookie, Json(request): Json, ) -> Result<(IdentityCookie, Empty), Error> { let secret = app .tokens() .login(&request.name, &request.password, &now) .await .map_err(Error)?; let identity = identity.set(secret); Ok((identity, Empty)) } #[derive(serde::Deserialize)] pub struct Request { pub name: Name, pub password: Password, } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct Error(#[from] pub app::LoginError); impl IntoResponse for Error { fn into_response(self) -> Response { let Self(error) = self; match error { app::LoginError::Rejected => { // not error::Unauthorized due to differing messaging (StatusCode::UNAUTHORIZED, "invalid name or password").into_response() } other => Internal::from(other).into_response(), } } }