Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debajyotisaha14/refactoring #35

Merged
merged 3 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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