aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2024-05-30 15:14:13 -0500
committerFrederick Muriuki Muriithi2024-06-03 10:02:09 -0500
commitc462653f53858359a81cfed561f1622d7e63102b (patch)
tree57c46d594560187389c036fdfed71c9a7a753e51
parent5680eb5fd1053f695e5917891f916ae85c212d3e (diff)
downloadgn-auth-c462653f53858359a81cfed561f1622d7e63102b.tar.gz
Send verification email on registration.
-rw-r--r--gn_auth/auth/authorisation/users/views.py56
-rw-r--r--gn_auth/templates/emails/verify-email.html49
-rw-r--r--gn_auth/templates/emails/verify-email.txt12
3 files changed, 115 insertions, 2 deletions
diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py
index feacae3..8919a6d 100644
--- a/gn_auth/auth/authorisation/users/views.py
+++ b/gn_auth/auth/authorisation/users/views.py
@@ -1,11 +1,22 @@
"""User authorisation endpoints."""
+import sqlite3
+import secrets
+import datetime
import traceback
from typing import Any
from functools import partial
from dataclasses import asdict
-import sqlite3
+from email.headerregistry import Address
from email_validator import validate_email, EmailNotValidError
-from flask import request, jsonify, Response, Blueprint, current_app
+from flask import (
+ request,
+ jsonify,
+ Response,
+ Blueprint,
+ current_app,
+ render_template)
+
+from gn_auth.smtp import send_message, build_email_message
from gn_auth.auth.db import sqlite3 as db
from gn_auth.auth.db.sqlite3 import with_db_connection
@@ -86,6 +97,46 @@ def __assert_not_logged_in__(conn: db.DbConnection):
raise UserRegistrationError(
"Cannot register user while authenticated")
+def user_address(user: User) -> Address:
+ """Compute the `email.headerregistry.Address` from a `User`"""
+ return Address(display_name=user.name, addr_spec=user.email)
+
+def send_verification_email(conn, user: User) -> None:
+ """Send an email verification message."""
+ subject="GeneNetwork: Please Verify Your Email"
+ verification_code = secrets.token_urlsafe(64)
+ generated = datetime.datetime.now()
+ expiration_minutes = 15
+ def __render__(template):
+ return render_template(template,
+ subject=subject,
+ verification_code=verification_code,
+ verification_uri="https://please/change/this/",
+ expiration_minutes=expiration_minutes)
+ with db.cursor(conn) as cursor:
+ cursor.execute(
+ ("INSERT INTO "
+ "user_verification_codes(user_id, code, generated, expires) "
+ "VALUES (:user_id, :code, :generated, :expires)"),
+ {
+ "user_id": str(user.user_id),
+ "code": verification_code,
+ "generated": int(generated.timestamp()),
+ "expires": int(
+ (generated +
+ datetime.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(
+ to_addresses=(user_address(user),),
+ subject=subject,
+ txtmessage=__render__("emails/verify-email.txt"),
+ htmlmessage=__render__("emails/verify-email.html")),
+ host=current_app.config["SMTP_HOST"],
+ port=current_app.config["SMTP_PORT"])
+
@users.route("/register", methods=["POST"])
def register_user() -> Response:
"""Register a user."""
@@ -105,6 +156,7 @@ def register_user() -> Response:
cursor, save_user(
cursor, email["email"], user_name), password)
assign_default_roles(cursor, user)
+ send_verification_email(conn, user)
return jsonify(asdict(user))
except sqlite3.IntegrityError as sq3ie:
current_app.logger.debug(traceback.format_exc())
diff --git a/gn_auth/templates/emails/verify-email.html b/gn_auth/templates/emails/verify-email.html
new file mode 100644
index 0000000..08f1dfe
--- /dev/null
+++ b/gn_auth/templates/emails/verify-email.html
@@ -0,0 +1,49 @@
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <title>{{subject}}</title>
+ <style type="text/css">
+ .btn {
+ display: inline-block;
+ text-align: center;
+ vertical-align: center;
+ cursor: pointer;
+ border-radius: 4px;
+ background-color: #336699;
+ border-color: #357ebd;
+ }
+ .verification-code{
+ color: #3A3AFF;
+ font-weight: bolder;
+ }
+ .note {
+ font-weight: bold;
+ color: #ee55ee;
+ }
+ </style>
+ </head>
+ <body>
+ <p>Thank you for registering an account with GeneNetwork.</p>
+
+ <p>
+ To avoid bots, we need to verify that you are an actual human. To do so,
+ please
+ <a href="{{verification_uri}}/{{verification_code}}"
+ class="btn">
+ Click here
+ </a>.
+ </p>
+
+ <p>
+ If that does not work, please log in to GeneNetwork and copy the
+ verification code below when requested:<br /><br />
+
+ <span class="verification-code">{{verification_code}}</span>
+ </p>
+
+ <p class="note">
+ Please note that the verification code will expire in
+ <strong>{{expiration_minutes}}</strong> minutes after it was generated.
+ </p>
+ </body>
+</html>
diff --git a/gn_auth/templates/emails/verify-email.txt b/gn_auth/templates/emails/verify-email.txt
new file mode 100644
index 0000000..92a6435
--- /dev/null
+++ b/gn_auth/templates/emails/verify-email.txt
@@ -0,0 +1,12 @@
+{{subject}}
+===============
+
+Thank you for registering an account with GeneNetwork.
+
+To avoid bots, we need to verify that you are an actual human. To do so, please go to "{{verification_uri}}/{{verification_code}}".
+
+If that does not work, please log in to GeneNetwork and copy the verification code below when requested:
+
+{{verification_code}}
+
+Please note that the verification code will expire {{expiration_minutes}} minutes after it was generated.