Add basic register and login
All checks were successful
Build Crate / build (push) Successful in 1m45s

This commit is contained in:
2024-08-28 16:53:08 -04:00
parent f4f491085d
commit 5af626b746
23 changed files with 397 additions and 31 deletions

View File

@@ -1,24 +1,60 @@
use crate::entity::prelude::*;
use crate::models::auth::{Claims, LoginInfo, LoginResponse};
use crate::{
models::auth::{Claims, LoginInfo, LoginResponse},
entity::user::{self, Entity},
models::auth::RegisterRequest,
server::server_state::ServerState,
};
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::SaltString;
use argon2::Argon2;
use argon2::PasswordHash;
use argon2::PasswordHasher;
use argon2::PasswordVerifier;
use axum::{
extract::{Json, State},
http::{HeaderMap, StatusCode},
};
use futures::sink::Fanout;
use sea_orm::ColumnTrait;
use sea_orm::EntityTrait;
use sea_orm::InsertResult;
use sea_orm::QueryFilter;
use sea_orm::Set;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
pub fn is_valid_user(username: &str, password: &str) -> bool {
return true;
pub async fn credentials_are_correct(username: &str, password: &str, state: &ServerState) -> bool {
// Get user
let existing_user: Option<user::Model> = User::find()
.filter(user::Column::Username.eq(username))
.one(&state.db_conn)
.await
.unwrap();
let hash_to_check: String = match existing_user {
Some(user) => user.password_hash_and_salt,
None => {
// @todo make dummy password hash
return false;
}
};
return Argon2::default()
.verify_password(
password.as_bytes(),
&PasswordHash::new(hash_to_check.as_str()).unwrap(),
)
.is_ok();
}
pub async fn post_login_user(
State(state): State<ServerState>,
Json(login_info): Json<LoginInfo>,
) -> Result<Json<LoginResponse>, StatusCode> {
let user_authenticated = is_valid_user(&login_info.username, &login_info.password);
let user_authenticated =
credentials_are_correct(&login_info.username, &login_info.password, &state);
match user_authenticated {
match user_authenticated.await {
false => Err(StatusCode::UNAUTHORIZED),
true => {
let expires = match login_info.remember_me {

View File

@@ -1,2 +1,3 @@
pub mod forgot_password;
pub mod login;
pub mod register;

View File

@@ -0,0 +1,89 @@
use crate::entity::prelude::*;
use crate::models::generic::GenericResponse;
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::SaltString;
use argon2::Argon2;
use argon2::PasswordHash;
use argon2::PasswordHasher;
use axum::{extract::State, http::StatusCode, Json};
use chrono::Utc;
use sea_orm::ColumnTrait;
use sea_orm::EntityTrait;
use sea_orm::InsertResult;
use sea_orm::QueryFilter;
use sea_orm::Set;
use crate::{
entity::user::{self, Entity},
models::auth::RegisterRequest,
server::server_state::ServerState,
};
pub async fn post_register_user(
State(state): State<ServerState>,
Json(register_info): Json<RegisterRequest>,
) -> (StatusCode, Json<GenericResponse>) {
// TODO -> update to use env, maybe prevent brute force too
if register_info.registration_code != "ferris" {
return (
StatusCode::UNAUTHORIZED,
Json(GenericResponse::err("Incorrect registration code")),
);
}
// See if username already exists
let username = register_info.username;
let existing_user: Option<user::Model> = User::find()
.filter(user::Column::Username.eq(username.clone()))
.one(&state.db_conn)
.await
.unwrap();
if existing_user.is_some() {
return (
StatusCode::BAD_REQUEST,
Json(GenericResponse::err("Username already exists")),
);
}
// Generate password
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let password_hash = argon2
.hash_password(register_info.password.as_bytes(), &salt)
.unwrap()
.to_string();
let phc_string = PasswordHash::new(&password_hash).unwrap().to_string();
// If the username doen't exist, create the user
let new_user = user::ActiveModel {
username: Set(username),
password_hash_and_salt: Set(phc_string),
nickname: Set({
if register_info.nickname == "" {
None
} else {
Some(register_info.nickname)
}
}),
creation_time: Set(Utc::now().naive_utc()),
last_active_time: Set(Utc::now().naive_utc()),
is_admin: Set(false),
email: Set({
if register_info.email == "" {
None
} else {
Some(register_info.email)
}
}),
avatar: Set(None),
forgot_password_request: Set(None),
..Default::default()
};
// TODO -> error handling
let db_resp = user::Entity::insert(new_user)
.exec(&state.db_conn)
.await
.unwrap();
return (StatusCode::OK, Json(GenericResponse::ok()));
}

View File

@@ -1,5 +1,5 @@
// (Server only) Routes
use crate::endpoints::{FORGOT_PASSWORD, LOGIN, LOGIN_TEST};
use crate::endpoints::{FORGOT_PASSWORD, LOGIN, LOGIN_TEST, REGISTER};
use axum::routing::{post, Router};
use futures::executor::block_on;
use sea_orm::Database;
@@ -8,12 +8,14 @@ use super::{
auth::{
forgot_password::post_forgot_password,
login::{post_login_user, post_test_login},
register::post_register_user,
},
server_state::ServerState,
};
pub fn get_api_router(state: ServerState) -> Router {
Router::new()
.route(REGISTER, post(post_register_user))
.route(LOGIN, post(post_login_user))
.route(LOGIN_TEST, post(post_test_login))
.route(FORGOT_PASSWORD, post(post_forgot_password))