Skip to content

Commit

Permalink
Merge pull request #35 from Rajdip019/debajyotisaha14/refactoring
Browse files Browse the repository at this point in the history
Debajyotisaha14/refactoring
  • Loading branch information
Rajdip019 authored May 14, 2024
2 parents 6a7b46c + 9a7d3eb commit 7b32df1
Show file tree
Hide file tree
Showing 13 changed files with 785 additions and 69 deletions.
461 changes: 461 additions & 0 deletions openapi.yaml

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions src/config/init.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use mongodb::{Client, Collection};

use crate::core::{user::User, dek::Dek};
use crate::core::{dek::Dek, user::User};

struct InitUser {
name: String,
Expand Down Expand Up @@ -48,15 +48,18 @@ pub async fn init_users(mongo_client: Client) {
let new_user = User::new(&user.name, &user.email, &user.role, &user.password);
let dek = Dek::generate();
match new_user.encrypt_and_add(&mongo_client, &dek).await {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
println!(">> Error adding user: {:?}", e);
continue;
}
};

// add the dek to the deks collection
match Dek::new(&new_user.uid, &new_user.email, &dek).encrypt_and_add(&mongo_client).await {
match Dek::new(&new_user.uid, &new_user.email, &dek)
.encrypt_and_add(&mongo_client)
.await
{
Ok(_) => {}
Err(e) => {
println!(">> Error adding dek: {:?}", e);
Expand Down
25 changes: 15 additions & 10 deletions src/core/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ impl Auth {
email: &str,
role: &str,
password: &str,
user_agent: &str,
) -> Result<SignInOrSignUpResponse> {
println!(">> HANDLER: add_user_handler called");

let db = mongo_client.database("auth");

let collection: Collection<User> = db.collection("users");
Expand Down Expand Up @@ -57,10 +56,13 @@ impl Auth {
Err(e) => return Err(e),
};

let session = match Session::new(&user, "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36").encrypt_add(&mongo_client, &dek).await {
Ok(session) => session,
Err(e) => return Err(e),
};
let session = match Session::new(&user, user_agent)
.encrypt_add(&mongo_client, &dek)
.await
{
Ok(session) => session,
Err(e) => return Err(e),
};

Ok(SignInOrSignUpResponse {
message: "Signup successful".to_string(),
Expand Down Expand Up @@ -111,10 +113,13 @@ impl Auth {

// verify the password
if Password::verify_hash(password, &user.password) {
let session = match Session::new(&user, &user_agent).encrypt_add(&mongo_client, &dek_data.dek).await {
Ok(session) => session,
Err(e) => return Err(e),
};
let session = match Session::new(&user, &user_agent)
.encrypt_add(&mongo_client, &dek_data.dek)
.await
{
Ok(session) => session,
Err(e) => return Err(e),
};

// make the failed login attempts to 0
match User::reset_failed_login_attempt(&mongo_client, &user.email).await {
Expand Down
24 changes: 22 additions & 2 deletions src/core/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use crate::{
models::session_model::SessionResponse,
traits::{decryption::Decrypt, encryption::Encrypt},
utils::{
encryption_utils::Encryption,
session_utils::{IDToken, RefreshToken},
email_utils::Email, encryption_utils::Encryption, session_utils::{IDToken, RefreshToken}
},
};
use bson::{doc, DateTime};
Expand Down Expand Up @@ -128,6 +127,7 @@ impl Session {
session_id: &str,
id_token: &str,
refresh_token: &str,
user_agent: &str,
) -> Result<(String, String)> {
// verify refresh token
match RefreshToken::verify(&refresh_token) {
Expand Down Expand Up @@ -170,6 +170,26 @@ impl Session {
match session {
Some(data) => {
let decrypted_session = data.decrypt(&dek_data.dek);
if decrypted_session.user_agent != user_agent {
let user = User::get_from_email(mongo_client, &decrypted_session.email).await.unwrap();
Email::new(
&user.name,
&user.email,
&"Unauthorized Login Attempt Detected",
"We have detected an unauthorized login attempt associated with your account. For your security, we have taken action to protect your account.
If you attempted to log in, please disregard this message. However, if you did not attempt to log in, we recommend taking the following steps:
Immediately change your password to a strong, unique one.
Review your account activity for any suspicious activity.
If you have any concerns or questions, please don't hesitate to contact our support team.
Stay safe and secure,
FlexAuth Team").send().await;
return Err(Error::InvalidUserAgent {
message: "User Agent doesn't match with it's Session's User Agent".to_string(),
})
}
if decrypted_session.id_token == id_token
&& decrypted_session.refresh_token == refresh_token
{
Expand Down
71 changes: 69 additions & 2 deletions src/core/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use futures::StreamExt;
use mongodb::{Client, Collection};
use serde::{Deserialize, Serialize};

use super::dek::Dek;
use super::{dek::Dek, session::Session};

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct User {
Expand Down Expand Up @@ -107,7 +107,6 @@ impl User {
return Err(e);
}
};

match collection
.find_one(
doc! {
Expand All @@ -118,7 +117,9 @@ impl User {
.await
{
Ok(Some(user)) => {
println!("User {:?}", user);
let decrypted_user = user.decrypt(&dek_data.dek);
println!("Decrypted User {:?}", decrypted_user);
return Ok(decrypted_user);
}
Ok(None) => Err(Error::UserNotFound {
Expand Down Expand Up @@ -344,6 +345,24 @@ impl User {

if user.failed_login_attempts == 5 {
let blocked_until = DateTime::now().timestamp_millis() + 180000;

// send a email to the user to notify multiple login attempts detected
Email::new(
&user.name,
&user.email,
&"Multiple login Attempts detected",
&("We have detected an multiple unauthorized login attempt associated with your account. For your security, we have taken action to protect your account.
If you attempted to log in, please disregard this message. However, if you did not attempt to log in, we recommend taking the following steps:
Immediately change your password to a strong, unique one.
Review your account activity for any suspicious activity.
If you have any concerns or questions, please don't hesitate to contact our support team.
Stay safe and secure,
FlexAuth Team"),
).send().await;

match collection
.update_one(
doc! {
Expand All @@ -370,6 +389,24 @@ impl User {
}
} else if user.failed_login_attempts == 10 {
let blocked_until = DateTime::now().timestamp_millis() + 600000;

// send a email to the user to notify multiple login attempts detected
Email::new(
&user.name,
&user.email,
&"Multiple login Attempts detected",
&("We have detected an multiple unauthorized login attempt associated with your account. For your security, we have taken action to protect your account.
If you attempted to log in, please disregard this message. However, if you did not attempt to log in, we recommend taking the following steps:
Immediately change your password to a strong, unique one.
Review your account activity for any suspicious activity.
If you have any concerns or questions, please don't hesitate to contact our support team.
Stay safe and secure,
FlexAuth Team"),
).send().await;

match collection
.update_one(
doc! {
Expand All @@ -396,6 +433,24 @@ impl User {
}
} else if user.failed_login_attempts == 15 {
let blocked_until = DateTime::now().timestamp_millis() + 3600000;

// send a email to the user to notify multiple login attempts detected
Email::new(
&user.name,
&user.email,
&"Multiple login Attempts detected",
&("We have detected an multiple unauthorized login attempt associated with your account. For your security, we have taken action to protect your account.
If you attempted to log in, please disregard this message. However, if you did not attempt to log in, we recommend taking the following steps:
Immediately change your password to a strong, unique one.
Review your account activity for any suspicious activity.
If you have any concerns or questions, please don't hesitate to contact our support team.
Stay safe and secure,
FlexAuth Team"),
).send().await;

match collection
.update_one(
doc! {
Expand Down Expand Up @@ -731,6 +786,18 @@ impl User {
.await
{
Ok(cursor_dek) => {
// delete all sessions associated with the user
let session_collection: Collection<Session> = db.collection("sessions");
session_collection
.delete_many(
doc! {
"uid": Encryption::encrypt_data(&dek_data.uid, &kek),
},
None,
)
.await
.unwrap();

if cursor_dek.deleted_count == 0 {
// send back a 404 to
return Err(Error::UserNotFound {
Expand Down
30 changes: 20 additions & 10 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ pub enum Error {
SessionExpired { message: String },
ActiveSessionExists { message: String },


// -- Validation Errors
InvalidEmail { message: String },
InvalidUserAgent { message: String },

// -- Encryption Errors
KeyNotFound { message: String },

Expand Down Expand Up @@ -72,6 +75,15 @@ impl Error {
(StatusCode::FOUND, ClientError::USER_ALREADY_EXISTS)
}

// -- Validation Errors
Self::InvalidEmail { message: _ } => {
(StatusCode::BAD_REQUEST, ClientError::INVALID_PARAMS)
}

Self::InvalidUserAgent { message: _ } => {
(StatusCode::BAD_REQUEST, ClientError::INVALID_PARAMS)
}

// -- Password Errors
Self::InvalidPassword { message: _ } => {
(StatusCode::UNAUTHORIZED, ClientError::INVALID_PASSWORD)
Expand Down Expand Up @@ -127,21 +139,19 @@ impl Error {
StatusCode::INTERNAL_SERVER_ERROR,
ClientError::SERVICE_ERROR,
),

Self::SessionExpired { message: _ } => (
StatusCode::UNAUTHORIZED,
ClientError::SESSION_EXPIRED,
),

Self::SessionExpired { message: _ } => {
(StatusCode::UNAUTHORIZED, ClientError::SESSION_EXPIRED)
}

Self::ServerError { message: _ } => (
StatusCode::INTERNAL_SERVER_ERROR,
ClientError::SERVICE_ERROR,
),

Self::ActiveSessionExists { message: _ } => (
StatusCode::CONFLICT,
ClientError::ACTIVE_SESSION_EXISTS,
),
Self::ActiveSessionExists { message: _ } => {
(StatusCode::CONFLICT, ClientError::ACTIVE_SESSION_EXISTS)
}

_ => (
StatusCode::INTERNAL_SERVER_ERROR,
Expand Down
54 changes: 52 additions & 2 deletions src/handlers/auth_handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use axum::{extract::State, http::{header, HeaderMap}, Json};
use axum::{
extract::State,
http::{header, HeaderMap},
Json,
};
use axum_macros::debug_handler;

use crate::{
Expand All @@ -8,12 +12,14 @@ use crate::{
auth_model::{SignInOrSignUpResponse, SignInPayload, SignUpPayload},
session_model::{RevokeSessionsPayload, RevokeSessionsResult},
},
utils::validation_utils::Validation,
AppState,
};

#[debug_handler]
pub async fn signup_handler(
State(state): State<AppState>,
header: HeaderMap,
payload: Json<SignUpPayload>,
) -> Result<Json<SignInOrSignUpResponse>> {
println!(">> HANDLER: signup_handler called");
Expand All @@ -29,12 +35,37 @@ pub async fn signup_handler(
});
}

if !Validation::email(&payload.email) {
return Err(Error::InvalidEmail {
message: "Invalid Email".to_string(),
});
}

if !Validation::password(&payload.password) {
return Err(Error::InvalidPassword {
message: "The password must contain at least one alphabetic character (uppercase or lowercase), at least one digit, and must be at least 8 characters long.".to_string(),
});
}

// get user-agent form the header
let user_agent = match header.get(header::USER_AGENT) {
Some(ua) => ua.to_str().unwrap().to_string(),
None => "".to_string(),
};

if user_agent.is_empty() {
return Err(Error::InvalidUserAgent {
message: "Invalid User Agent, Can't let random user to signin".to_string(),
});
}

match Auth::sign_up(
&state.mongo_client,
&payload.name,
&payload.email,
&payload.role,
&payload.password,
&user_agent,
)
.await
{
Expand All @@ -56,13 +87,32 @@ pub async fn signin_handler(
});
}

if !Validation::email(&payload.email) {
return Err(Error::InvalidEmail {
message: "Invalid Email".to_string(),
});
}

// get user-agent form the header
let user_agent = match header.get(header::USER_AGENT) {
Some(ua) => ua.to_str().unwrap().to_string(),
None => "".to_string(),
};

match Auth::sign_in(&state.mongo_client, &payload.email, &payload.password, &user_agent).await {
if user_agent.is_empty() {
return Err(Error::InvalidUserAgent {
message: "Invalid User Agent, Can't let random user to signin".to_string(),
});
}

match Auth::sign_in(
&state.mongo_client,
&payload.email,
&payload.password,
&user_agent,
)
.await
{
Ok(res) => Ok(Json(res)),
Err(e) => Err(e),
}
Expand Down
Loading

0 comments on commit 7b32df1

Please sign in to comment.