Skip to content

Commit

Permalink
feat: password hashing using argon2
Browse files Browse the repository at this point in the history
  • Loading branch information
kentSarmiento committed Dec 26, 2023
1 parent 06e91ac commit acd5939
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions link-for-later/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ repository = "https://github.com/kentSarmiento/link-for-later-service"
publish = false

[dependencies]
argon2 = "0.5.2"
axum = "0.7.2"
axum-extra = { version = "0.9.0", default-features = false, features=["typed-header"] }
bson = "2.8.1"
Expand Down
38 changes: 31 additions & 7 deletions link-for-later/src/service/users.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
use axum::async_trait;
use chrono::{DateTime, Duration, Utc};
use jsonwebtoken::{encode, EncodingKey, Header};
Expand Down Expand Up @@ -33,8 +37,16 @@ impl UsersService for ServiceProvider {
};

let now = Utc::now().to_rfc3339();
// TODO: secure password
let registered_user_info = UserInfoBuilder::from(user_info.clone())

let password_hash = Argon2::default()
.hash_password(
user_info.password().as_bytes(),
&SaltString::generate(&mut OsRng),
)
.map_err(|e| AppError::ServerError(format!("hash_password() {e:?}")))?
.to_string();

let registered_user_info = UserInfoBuilder::new(user_info.email(), &password_hash)
.created_at(&now)
.updated_at(&now)
.verified(true)
Expand All @@ -51,9 +63,11 @@ impl UsersService for ServiceProvider {
let user_query = UserQueryBuilder::new(user_info.email()).build();
let retrieved_user_info = users_repo.get(&user_query).await?;

if retrieved_user_info.password() != user_info.password() {
return Err(AppError::IncorrectPassword(user_info.email().to_owned()));
}
let parsed_hash = PasswordHash::new(retrieved_user_info.password())
.map_err(|e| AppError::ServerError(format!("PasswordHash::new() {e:?}")))?;
Argon2::default()
.verify_password(user_info.password().as_bytes(), &parsed_hash)
.map_err(|_| AppError::IncorrectPassword(user_info.email().to_owned()))?;

let timestamp = |timestamp: DateTime<Utc>| -> Result<usize> {
let timestamp: usize = timestamp
Expand Down Expand Up @@ -199,9 +213,14 @@ mod tests {
async fn test_login_user() {
let repo_query = UserQueryBuilder::new("[email protected]").build();
let user_to_login = UserInfoBuilder::new("[email protected]", "test").build();
let registered_user = UserInfoBuilder::new("[email protected]", "test").build();
let request_item = user_to_login.clone();

let password_hash = Argon2::default()
.hash_password(b"test", &SaltString::generate(&mut OsRng))
.unwrap()
.to_string();
let registered_user = UserInfoBuilder::new("[email protected]", &password_hash).build();

let mut mock_users_repo = MockUsersRepo::new();
mock_users_repo
.expect_get()
Expand Down Expand Up @@ -245,9 +264,14 @@ mod tests {
async fn test_login_user_incorrect_password() {
let repo_query = UserQueryBuilder::new("[email protected]").build();
let user_to_login = UserInfoBuilder::new("[email protected]", "incorrect").build();
let registered_user = UserInfoBuilder::new("[email protected]", "test").build();
let request_item = user_to_login.clone();

let password_hash = Argon2::default()
.hash_password(b"test", &SaltString::generate(&mut OsRng))
.unwrap()
.to_string();
let registered_user = UserInfoBuilder::new("[email protected]", &password_hash).build();

let mut mock_users_repo = MockUsersRepo::new();
mock_users_repo
.expect_get()
Expand Down
27 changes: 23 additions & 4 deletions link-for-later/tests/users.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use argon2::{
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
Argon2,
};
use axum::{
body::Body,
http::{Request, StatusCode},
Expand Down Expand Up @@ -49,7 +53,7 @@ async fn test_register_user(#[values(DatabaseType::MongoDb)] db_type: DatabaseTy

let db_item = repository.get_user("[email protected]").await;
assert!(db_item.email == "[email protected]");
assert!(db_item.password == "test");
assert!(db_item.password != "test");
}

#[rstest]
Expand Down Expand Up @@ -132,7 +136,12 @@ async fn test_register_user_already_registered(
async fn test_login_user(#[values(DatabaseType::MongoDb)] db_type: DatabaseType) {
let repository = repository::new(&db_type);

repository.add_user("[email protected]", "test").await;
let password_hash = Argon2::default()
.hash_password(b"test", &SaltString::generate(&mut OsRng))
.unwrap()
.to_string();
repository.add_user("[email protected]", &password_hash).await;

let request = r#"{
"email": "[email protected]",
"password": "test"
Expand Down Expand Up @@ -196,7 +205,12 @@ async fn test_login_user_invalid_email(#[values(DatabaseType::MongoDb)] db_type:
async fn test_login_user_not_found(#[values(DatabaseType::MongoDb)] db_type: DatabaseType) {
let repository = repository::new(&db_type);

repository.add_user("[email protected]", "test").await;
let password_hash = Argon2::default()
.hash_password(b"test", &SaltString::generate(&mut OsRng))
.unwrap()
.to_string();
repository.add_user("[email protected]", &password_hash).await;

let request = r#"{
"email": "[email protected]",
"password": "test"
Expand Down Expand Up @@ -230,7 +244,12 @@ async fn test_login_user_incorrect_password(
) {
let repository = repository::new(&db_type);

repository.add_user("[email protected]", "test").await;
let password_hash = Argon2::default()
.hash_password(b"test", &SaltString::generate(&mut OsRng))
.unwrap()
.to_string();
repository.add_user("[email protected]", &password_hash).await;

let request = r#"{
"email": "[email protected]",
"password": "incorrect"
Expand Down

0 comments on commit acd5939

Please sign in to comment.