From 1c2db3df6d8417cb927e0b47774dcdd6397f5469 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 19 Aug 2024 17:01:03 -0500 Subject: Generate token and send out "Forgot Password" email * Generate token to use for verifying the password-change request * Send out email with token --- gn_auth/auth/authorisation/users/views.py | 72 ++++++++++++++++++++++ gn_auth/templates/emails/forgot-password.html | 30 +++++++++ gn_auth/templates/emails/forgot-password.txt | 10 +++ .../users/forgot-password-token-send-success.html | 21 +++++++ gn_auth/templates/users/forgot-password.html | 35 +++++++++++ 5 files changed, 168 insertions(+) create mode 100644 gn_auth/templates/emails/forgot-password.html create mode 100644 gn_auth/templates/emails/forgot-password.txt create mode 100644 gn_auth/templates/users/forgot-password-token-send-success.html create mode 100644 gn_auth/templates/users/forgot-password.html diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py index 4b56c3d..84d7ef0 100644 --- a/gn_auth/auth/authorisation/users/views.py +++ b/gn_auth/auth/authorisation/users/views.py @@ -366,3 +366,75 @@ def send_verification_code(): }) resp.code = 400 return resp + + +def send_forgot_password_email(conn, user: User): + """Send the 'forgot-password' email.""" + subject="GeneNetwork: Change Your Password" + token = secrets.token_urlsafe(64) + generated = datetime.now() + expiration_minutes = 15 + def __render__(template): + return render_template(template, + subject=subject, + forgot_password_uri=urljoin( + request.url, + url_for("oauth2.users.change_password", + forgot_password_token=token)), + expiration_minutes=expiration_minutes) + + with db.cursor(conn) as cursor: + cursor.execute( + ("INSERT INTO " + "forgot_password_tokens(user_id, token, generated, expires) " + "VALUES (:user_id, :token, :generated, :expires) " + "ON CONFLICT (user_id) REPLACE"), + { + "user_id": str(user.user_id), + "token": token, + "generated": int(generated.timestamp()), + "expires": int( + (generated + + timedelta( + minutes=expiration_minutes)).timestamp()) + }) + send_message(smtp_user=current_app.config["SMTP_USER"], + smtp_passwd=current_app.config["SMTP_PASSWORD"], + message=build_email_message( + from_address=current_app.config["EMAIL_ADDRESS"], + to_addresses=(user_address(user),), + subject=subject, + txtmessage=__render__("emails/forgot-password.txt"), + htmlmessage=__render__("emails/forgot-password.html")), + host=current_app.config["SMTP_HOST"], + port=current_app.config["SMTP_PORT"]) + + +@users.route("/forgot-password", methods=["GET", "POST"]) +def forgot_password(): + """Enable user to request password change.""" + if request.method == "GET": + return render_template("users/forgot-password.html") + + form = request.form + email = form.get("email", "").strip() + if not bool(email): + flash("You MUST provide an email.", "alert-danger") + return redirect(url_for("oauth2.users.forgot_password")) + + with (db.connection(current_app.config["AUTH_DB"]) as conn, + db.cursor(conn) as cursor): + user = user_by_email(conn, form["email"]) + if not bool(user): + flash("We could not find an account with that email.", + "alert-danger") + return redirect(url_for("oauth2.users.forgot_password")) + + send_forgot_password_email(conn, user) + return render_template("users/forgot-password-token-send-success.html") + + +@users.route("/change-password/", methods=["GET", "POST"]) +def change_password(forgot_password_token): + """Enable user to perform password change.""" + return "Would change password..." diff --git a/gn_auth/templates/emails/forgot-password.html b/gn_auth/templates/emails/forgot-password.html new file mode 100644 index 0000000..18321d5 --- /dev/null +++ b/gn_auth/templates/emails/forgot-password.html @@ -0,0 +1,30 @@ + + + + {{subject}} + + +

+ You (or someone pretending to be you) made a request to change your + password. Please follow the link below to change it. +

+ +

+ Click the button below to change your password + Change my Password.

+ +

+ Or copy the link below onto your browser's address bar:

+ {{forgot_password_uri}} +

+ +

+ If you did not request to change your password, simply ignore this email. +

+ +

+ The link will expire in {{expiration_minutes}} minutes. +

+ + diff --git a/gn_auth/templates/emails/forgot-password.txt b/gn_auth/templates/emails/forgot-password.txt new file mode 100644 index 0000000..7eda908 --- /dev/null +++ b/gn_auth/templates/emails/forgot-password.txt @@ -0,0 +1,10 @@ +{{subject}} +=============== + +You (or someone pretending to be you) made a request to change your password. Please copy the link below onto your browser to change your password: + +{{forgot_password_uri}} + +If you did not request to change your password, simply ignore this email. + +The link will expire {{expiration_minutes}} minutes. diff --git a/gn_auth/templates/users/forgot-password-token-send-success.html b/gn_auth/templates/users/forgot-password-token-send-success.html new file mode 100644 index 0000000..ab8a741 --- /dev/null +++ b/gn_auth/templates/users/forgot-password-token-send-success.html @@ -0,0 +1,21 @@ +{%extends "base.html"%} + +{%block title%}gn-auth: Forgot Password{%endblock%} + +{%block pagetitle%}Forgot Password{%endblock%} + +{%block content%} +{{flash_messages()}} + +
+

Forgot Password

+ +
+

+ We have sent an email to '{{email}}'. Please log in to your email and + click the URL to change your password. +

+
+ +
+{%endblock%} diff --git a/gn_auth/templates/users/forgot-password.html b/gn_auth/templates/users/forgot-password.html new file mode 100644 index 0000000..94fcc68 --- /dev/null +++ b/gn_auth/templates/users/forgot-password.html @@ -0,0 +1,35 @@ +{%extends "base.html"%} + +{%block title%}gn-auth: Forgot Password{%endblock%} + +{%block pagetitle%}Forgot Password{%endblock%} + +{%block content%} +{{flash_messages()}} + +
+

Forgot Password

+ +
+
+
+ + Provide you email below, and we will send you a link you can use to + change your password. + +
+ +
+ + +
+ +
+ +
+
+
+ +
+{%endblock%} -- cgit v1.2.3