Implement login and genkey routes
This commit is contained in:
parent
fe62b28a03
commit
78f450fbef
2 changed files with 89 additions and 7 deletions
|
@ -1,3 +1,5 @@
|
|||
use std::time::SystemTime;
|
||||
|
||||
use crate::env;
|
||||
use jsonwebtoken::{TokenData, Header, Validation};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
@ -17,3 +19,10 @@ 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
|
||||
}
|
|
@ -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) => {
|
||||
|
|
Loading…
Reference in a new issue