centralize Argon2 setup
This commit is contained in:
@@ -24,6 +24,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
database::migrations()?;
|
database::migrations()?;
|
||||||
|
users::auth::init_password_dummies();
|
||||||
users::setup::initialise_reserved_users_if_needed()?;
|
users::setup::initialise_reserved_users_if_needed()?;
|
||||||
|
|
||||||
let port = match std::env::var("PORT") {
|
let port = match std::env::var("PORT") {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString};
|
use argon2::{PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString};
|
||||||
use axum::{
|
use axum::{
|
||||||
http::{
|
http::{
|
||||||
HeaderMap, StatusCode,
|
HeaderMap, StatusCode,
|
||||||
@@ -15,8 +15,9 @@ use crate::{
|
|||||||
users::{
|
users::{
|
||||||
User,
|
User,
|
||||||
auth::{
|
auth::{
|
||||||
AuthError, COOKIE_NAME, SessionAuthRequired, SessionAuthenticate, TokenSize,
|
AuthError, COOKIE_NAME, DUMMY_PASSWORD, DUMMY_PASSWORD_PHC, SHARED_ARGON,
|
||||||
UserAuthDummyData, UserAuthRequired, UserAuthenticate, UserPasswordHashing,
|
SessionAuthRequired, SessionAuthenticate, TokenSize, UserAuthRequired,
|
||||||
|
UserAuthenticate, UserPasswordHashing,
|
||||||
},
|
},
|
||||||
sessions::Session,
|
sessions::Session,
|
||||||
},
|
},
|
||||||
@@ -75,29 +76,18 @@ impl SessionAuthRequired for Option<Session> {
|
|||||||
impl UserPasswordHashing for User {
|
impl UserPasswordHashing for User {
|
||||||
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error> {
|
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error> {
|
||||||
use rand08::rngs::OsRng as ArgonOsRng;
|
use rand08::rngs::OsRng as ArgonOsRng;
|
||||||
let argon = Argon2::default();
|
|
||||||
let passw = passw.as_bytes();
|
let passw = passw.as_bytes();
|
||||||
let salt = SaltString::generate(&mut ArgonOsRng);
|
let salt = SaltString::generate(&mut ArgonOsRng);
|
||||||
|
|
||||||
Ok(argon.hash_password(passw, &salt)?.to_string())
|
Ok(SHARED_ARGON.hash_password(passw, &salt)?.to_string())
|
||||||
}
|
}
|
||||||
fn match_hash_password(passw: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
|
fn match_hash_password(passw: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
|
||||||
let argon = Argon2::default();
|
|
||||||
let passw = passw.as_bytes();
|
let passw = passw.as_bytes();
|
||||||
let hash = PasswordHash::try_from(hash)?;
|
let hash = PasswordHash::try_from(hash)?;
|
||||||
Ok(argon.verify_password(passw, &hash).is_ok())
|
Ok(SHARED_ARGON.verify_password(passw, &hash).is_ok())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: generate these at startup using predefined Argon2 params if
|
|
||||||
// these ever change from ::Default - the PHC must have the same factors as real hashes.
|
|
||||||
impl UserAuthDummyData for User {
|
|
||||||
/// This PHC generated for b"password"
|
|
||||||
const DUMMY_PASSWORD_PHC: &str = "$argon2id$v=19$m=19456,t=2,p=1$PXcTKpFhLRB70fVF35XYDQ$QOW2IxdPUvqD38+ScqX5SgO+jwweaMO9DUGqmkTeofQ";
|
|
||||||
/// Different than the input password of the PHC
|
|
||||||
const DUMMY_PASSWORD: &str = "different_password";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<argon2::password_hash::Error> for AuthError {
|
impl From<argon2::password_hash::Error> for AuthError {
|
||||||
fn from(err: argon2::password_hash::Error) -> Self {
|
fn from(err: argon2::password_hash::Error) -> Self {
|
||||||
AuthError::PassHashError(err)
|
AuthError::PassHashError(err)
|
||||||
@@ -213,7 +203,7 @@ pub fn authenticate_via_credentials(
|
|||||||
false => Err(AuthError::InvalidCredentials),
|
false => Err(AuthError::InvalidCredentials),
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let _ = User::match_hash_password(User::DUMMY_PASSWORD, User::DUMMY_PASSWORD_PHC)?;
|
let _ = User::match_hash_password(DUMMY_PASSWORD, &*DUMMY_PASSWORD_PHC)?;
|
||||||
Err(AuthError::InvalidCredentials)
|
Err(AuthError::InvalidCredentials)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
use argon2::{Argon2, PasswordHasher, password_hash::SaltString};
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use rand08::{RngCore, rngs::OsRng};
|
use rand08::{RngCore, rngs::OsRng};
|
||||||
|
|
||||||
@@ -29,9 +32,19 @@ pub trait UserPasswordHashing {
|
|||||||
/// Returns whether the password matches the hash
|
/// Returns whether the password matches the hash
|
||||||
fn match_hash_password(passw: &str, hash: &str) -> Result<bool, argon2::password_hash::Error>;
|
fn match_hash_password(passw: &str, hash: &str) -> Result<bool, argon2::password_hash::Error>;
|
||||||
}
|
}
|
||||||
pub trait UserAuthDummyData {
|
|
||||||
const DUMMY_PASSWORD_PHC: &str;
|
pub static SHARED_ARGON: LazyLock<Argon2> = LazyLock::new(|| Argon2::default());
|
||||||
const DUMMY_PASSWORD: &str;
|
pub const DUMMY_PASSWORD: &str = "PASSWORD";
|
||||||
|
pub static DUMMY_PASSWORD_PHC: LazyLock<String> = LazyLock::new(|| {
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
SHARED_ARGON
|
||||||
|
.hash_password(DUMMY_PASSWORD.as_bytes(), &salt)
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
});
|
||||||
|
pub fn init_password_dummies() {
|
||||||
|
let _ = &*DUMMY_PASSWORD_PHC;
|
||||||
|
println!("Password hashing setup finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
|||||||
Reference in New Issue
Block a user