diff options
Diffstat (limited to 'src/routes/auth.rs')
| -rw-r--r-- | src/routes/auth.rs | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/src/routes/auth.rs b/src/routes/auth.rs new file mode 100644 index 0000000..0b17ef4 --- /dev/null +++ b/src/routes/auth.rs @@ -0,0 +1,66 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use axum::extract::{Json, State}; +use axum::response::IntoResponse; +use jsonwebtoken::{encode, Header}; +use serde::{Deserialize, Serialize}; + +use crate::AppState; +use crate::auth::{check_password}; +use super::errors::RouteError; + +#[derive(Deserialize)] +pub struct LoginRequest { + email: String, + password: String, +} + +#[derive(Serialize)] +struct LoginResponse { + token: String, +} + +#[derive(Serialize)] +struct JWTClaims { + sub: String, + exp: usize, + iat: usize, + is_admin: bool, +} + +pub async fn login( + State(state): State<AppState>, + Json(request): Json<LoginRequest> +) -> Result<impl IntoResponse, RouteError> { + + let user = match state.database.fetch_user_by_email(&request.email) { + Err(_) => return Err(RouteError::Internal("database action failed".into())), + Ok(None) => return Err(RouteError::UnregisteredEmail(request.email)), + Ok(Some(user)) => user + }; + + match check_password(&request.password, user.password_hash()) { + Err(_) => return Err(RouteError::Internal("failed to check password".into())), + Ok(false) => return Err(RouteError::AuthorizationFailure()), + Ok(true) => {}, + } + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|_| RouteError::Internal("failed to access system clock".into()))? + .as_secs() as usize; + + let claims = JWTClaims { + sub: user.email().to_owned(), + iat: now, + exp: now + 60 * 60 * 24, + is_admin: false + }; + + let token= encode(&Header::default(), &claims, &state.api_key) + .map_err(|_| RouteError::Internal("failed to encode jwt".into()))?; + + Ok(Json(LoginResponse { token })) +} + + |
