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

Forget password Server page added #63

Merged
merged 1 commit into from
Jul 24, 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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"./Cargo.toml",
],
"cSpell.words": [
"blazingly",
"bson",
"chrono",
"deks",
Expand Down
9 changes: 8 additions & 1 deletion src/core/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ impl User {
&user.name,
&user.email,
&"Reset Password",
&format!("Please click on the link to reset your password: http://localhost:8080/forget-password-reset/{}", new_doc.id),
&format!("Please click on the link to reset your password: http://localhost:8080/forget-reset/{}", new_doc.id),
).send().await;

Ok("Forget password request sent to email successfully".to_string())
Expand All @@ -702,6 +702,10 @@ impl User {
let forget_password_requests_collection: Collection<ForgetPasswordRequest> =
db.collection("forget_password_requests");

println!("Forget Password Request ID {:?}", req_id);
println!("Forget Password Request Email {:?}", email);
println!("Forget Password Request New Password {:?}", new_password);

// find the dek with the email
let dek_data = match Dek::get(&mongo_client, &email).await {
Ok(dek) => dek,
Expand All @@ -717,6 +721,8 @@ impl User {
.unwrap()
.unwrap();

println!("Forget Password Request {:?}", forget_password_request);

if forget_password_request.is_used {
return Err(Error::ResetPasswordLinkExpired {
message: "The link has already been used. Please request a new link.".to_string(),
Expand Down Expand Up @@ -789,6 +795,7 @@ impl User {
).send().await;

Ok("Password updated successfully".to_string())

}

pub async fn delete(mongo_client: &Client, email: &str) -> Result<String> {
Expand Down
93 changes: 90 additions & 3 deletions src/handlers/password_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use crate::{
AppState,
};
use axum::{
extract::{Path, State},
Json,
extract::{Path, State}, response::{Html, IntoResponse}, Json
};
use axum_macros::debug_handler;
use bson::doc;
Expand Down Expand Up @@ -83,7 +82,7 @@ pub async fn forget_password_reset_handler(
// check if payload is valid
if payload.email.is_empty() | payload.password.is_empty() {
return Err(Error::InvalidPayload {
message: "Email is required.".to_string(),
message: "Invalid Payload".to_string(),
});
}
match User::forget_password_reset(&state.mongo_client, &id, &payload.email, &payload.password)
Expand All @@ -97,3 +96,91 @@ pub async fn forget_password_reset_handler(
Err(e) => return Err(e),
}
}

#[debug_handler] //forget_password_form
pub async fn forget_password_form(Path(id): Path<String>) -> impl IntoResponse {
Html(format!(r#"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reset Password</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 0; background-color: #060A13 }}
.navbar {{ background-color: #060A13; overflow: hidden; border-bottom: 0.5px solid #1E293B; }}
.navbar a {{ float: left; display: block; color: #f2f2f2; text-align: center; padding: 14px 16px; text-decoration: none; }}
.navbar a:hover {{ background-color: #ddd; color: black; }}
.form-div {{ margin: 0 auto; display: flex; justify-content: center; align-items: center }}
form {{ margin-top: 20px; display: flex; flex-direction: column; align-items: left; width: 40%; }}
label {{ display: block; margin-top: 10px; text-align: left; color: #f2f2f2; }}
input {{ width: 100%; padding: 8px; margin-top: 5px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }}
button {{ padding: 10px 20px; background-color: #3B81F6; color: #f2f2f2; border: none; cursor: pointer; border-radius: 5px; width: 100%; }}
h1 {{ color: #f2f2f2; text-align: center; padding: 14px 0px; }}
h2 {{ text-align: center; color: #f2f2f2; }}
p {{ color: #f2f2f2; margin-top: 30px; }}
</style>
</head>
<body>
<div class='navbar'>
<h1>FlexAuth</h1>
</div>
<h2>Reset Password</h2>
<div class='form-div'>
<form id="resetForm">
<label for="email">Enter Email:</label>
<input type="email" id="email" name="email" required placeholder="Enter email">
<label for="password">Enter Password:</label>
<input type="password" id="password" name="password" required placeholder="Enter new password">
<label for="confirm_password">Confirm Password:</label>
<input type="password" id="confirm_password" name="confirm_password" required placeholder="Confirm new password">
<br />
<button type="submit">Submit</button>
<p><b>Note:</b> Password must be at least 8 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character.</p>
</form>

</div>
<script>
document.getElementById('resetForm').addEventListener('submit', function(event) {{
event.preventDefault();

const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm_password').value;
const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{{8,}}$/;

if (!passwordPattern.test(password)) {{
alert('Password must be at least 8 characters long and include at least one uppercase letter, one lowercase letter, one number, and one special character.');
}} else if (password !== confirmPassword) {{
alert('Passwords do not match.');
}} else {{
fetch(`/api/password/forget-reset/{id}`, {{
method: 'POST',
headers: {{
'Content-Type': 'application/json',
'x-api-key': '{api_key}'
}},
body: JSON.stringify({{
email: email,
password: password
}})
}}).then(response => {{
if (response.ok) {{
alert('Password reset successful!');
}} else {{
response.json().then(res => {{
alert('Error: ' + res.error.type);
}}).catch(error => {{
alert('An error occurred: ' + error.message);
}});
}}
}}).catch(error => {{
alert('An error occurred: ' + error.message);
}});
}}
}});
</script>
</body>
</html>
"#, id = id, api_key = dotenv::var("X_API_KEY").unwrap()))
}
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use axum::response::Html;
use axum::routing::get;
use axum::{middleware, Router};
use dotenv::dotenv;
use handlers::password_handler::forget_password_form;
use middlewares::res_log::main_response_mapper;
use middlewares::with_api_key::with_api_key;
use mongodb::Client;
Expand Down Expand Up @@ -43,6 +44,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Define routes where middleware is not applied
let public_routes = Router::new()
.route("/", get(root_handler))
.route("/forget-reset/:id", get(forget_password_form))
.merge(routes::health_check_routes::routes())
.layer(middleware::map_response(main_response_mapper));

Expand Down
3 changes: 1 addition & 2 deletions src/routes/auth_routes.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use axum::{extract::State, routing::post, Router};

use crate::{
handlers::auth_handler::{signin_handler, signout_handler, signup_handler},
AppState,
handlers::auth_handler::{signin_handler, signout_handler, signup_handler}, AppState
};

pub fn routes(State(state): State<AppState>) -> Router {
Expand Down
3 changes: 2 additions & 1 deletion src/routes/password_routes.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use axum::{extract::State, routing::post, Router};

use crate::{handlers::password_handler::{forget_password_reset_handler, forget_password_request_handler, reset_password_handler}, AppState};
use crate::{handlers::password_handler::{forget_password_request_handler, forget_password_reset_handler, reset_password_handler}, AppState};

pub fn routes(State(state): State<AppState>) -> Router {
let password_rotes = Router::new()
.route("/reset", post(reset_password_handler))
.route("/forget-request", post(forget_password_request_handler))
.route("/forget-reset/:id", post(forget_password_reset_handler));


Router::new().nest("/password", password_rotes).with_state(state)
}
5 changes: 2 additions & 3 deletions src/routes/session_routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use axum::{extract::State, routing::{get, post}, Router};

use crate::{
handlers::session_handler::{
delete_handler, delete_all_handler, get_all_from_uid_handler, get_details_handler, refresh_session_handler, revoke_handler, revoke_all_handler, verify_session_handler, get_all_handler
},
AppState,
delete_all_handler, delete_handler, get_all_from_uid_handler, get_all_handler, get_details_handler, refresh_session_handler, revoke_all_handler, revoke_handler, verify_session_handler
}, AppState
};

pub fn routes(State(state): State<AppState>) -> Router {
Expand Down
7 changes: 2 additions & 5 deletions src/routes/user_routes.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use axum::{
extract::State,
routing::{get, post},
Router,
extract::State, routing::{get, post}, Router
};

use crate::{
handlers::user_handler::{
delete_user_handler, get_all_users_handler, get_recent_users_handler, get_user_email_handler, get_user_id_handler, toggle_user_activation_status, update_user_handler, update_user_role_handler
},
AppState,
}, AppState
};

pub fn routes(State(state): State<AppState>) -> Router {
Expand Down