Implement login and genkey routes

This commit is contained in:
Sofía Aritz 2024-10-15 22:22:08 +02:00
parent fe62b28a03
commit 78f450fbef
Signed by: sofia
GPG key ID: 90B5116E3542B28F
2 changed files with 89 additions and 7 deletions

View file

@ -1,3 +1,5 @@
use std::time::SystemTime;
use crate::env;
use jsonwebtoken::{TokenData, Header, Validation};
use serde::{Serialize, Deserialize};
@ -16,4 +18,11 @@ pub fn encode_jwt(claims: &JwtUser) -> jsonwebtoken::errors::Result<String> {
pub fn decode_jwt(jwt: &str) -> jsonwebtoken::errors::Result<TokenData<JwtUser>> {
jsonwebtoken::decode::<JwtUser>(jwt, &env::jwt_secret().1, &Validation::new(*env::jwt_alg()))
}
pub fn expiration_time() -> u64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("time went backwards")
.as_secs() + 180 * 24 * 3600
}

View file

@ -1,17 +1,18 @@
use std::time::SystemTime;
use argon2::{Argon2, PasswordHasher, password_hash::{rand_core::OsRng, SaltString}};
use argon2::{password_hash::{rand_core::OsRng, SaltString}, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use axum::{extract::State, http::StatusCode, routing::{get, post}, Json, Router};
use chrono::{Utc, NaiveDateTime};
use diesel::{RunQueryDsl, ExpressionMethods};
use tracing::{error, info};
use serde::{Serialize, Deserialize};
use uuid::Uuid;
use crate::{database::actions, http::extractors::auth::ExtractUser, auth::JwtUser, AppState};
use crate::{auth::{encode_jwt, expiration_time, JwtUser}, database::actions, http::extractors::auth::{ExtractJwtUser, ExtractUser}, AppState};
pub fn auth_router() -> Router<AppState> {
Router::new()
.route("/auth/account", get(account))
.route("/auth/register", post(register))
.route("/auth/login", post(login))
.route("/auth/genkey", get(genkey))
}
#[derive(Debug, Serialize)]
@ -33,6 +34,81 @@ async fn account(ExtractUser(user): ExtractUser) -> Result<Json<AccountResponse>
}))
}
#[derive(Debug, Serialize)]
struct GenkeyResponse {
session_key: String,
}
async fn genkey(State(state): State<AppState>, ExtractJwtUser(user): ExtractJwtUser) -> Result<Json<GenkeyResponse>, StatusCode> {
use crate::database::schema::session_keys::dsl::*;
if let Ok(mut conn) = state.pool.get() {
let session_key = Uuid::new_v4().to_string();
let result = diesel::insert_into(session_keys)
.values((
user_id.eq(user.uid),
key.eq(&session_key),
))
.execute(&mut conn);
if result.is_ok() {
Ok(Json(GenkeyResponse {
session_key,
}))
} else {
error!("failed to insert into session_keys");
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
} else {
error!("failed to obtain pooled connection");
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
#[derive(Debug, Deserialize)]
struct LoginRequest {
email: String,
password: String,
}
#[derive(Debug, Serialize)]
struct LoginResponse {
token: String,
}
async fn login(State(state): State<AppState>, Json(req): Json<LoginRequest>) -> Result<Json<LoginResponse>, StatusCode> {
if let Ok(mut conn) = state.pool.get() {
if let Ok(Some(user)) = actions::user_by_email(&req.email, &mut conn) {
let parsed_hash = PasswordHash::new(&user.password).expect("invalid argon2 password hash");
if Argon2::default().verify_password(req.password.as_bytes(), &parsed_hash).is_err() {
info!("failed login attempt, invalid password: {}", &req.email);
Err(StatusCode::UNAUTHORIZED)
} else {
info!("valid login attempt: {}", req.email);
match encode_jwt(&JwtUser {
uid: user.id,
email: user.email,
name: user.name,
exp: expiration_time(),
}) {
Ok(token) => Ok(Json(LoginResponse { token })),
Err(err) => {
error!("token couldn't be encoded: {:?}", err);
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
}
} else {
info!("failed login attempt, email does not exist: {}", &req.email);
Err(StatusCode::UNAUTHORIZED)
}
} else {
error!("failed to obtain pooled connection");
Err(StatusCode::INTERNAL_SERVER_ERROR)
}
}
#[derive(Debug, Deserialize)]
struct RegisterRequest {
email: String,
@ -105,10 +181,7 @@ async fn register(State(state): State<AppState>, Json(req): Json<RegisterRequest
uid: user_id,
email: req.email,
name: req.name,
exp: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("time went backwards")
.as_secs() + 180 * 24 * 3600,
exp: expiration_time(),
}) {
Ok(token) => Ok(Json(RegisterResponse { token })),
Err(err) => {