From c3b940f89523d54d2e2baf436122a30fc0aafdd9 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 3 Jun 2024 11:49:12 -0500 Subject: Handle unverified emails If a user provides the correct credentials to login, but they are unverified, redirect them to the email verification page, where they are provided with a chance to verify their email, or send a new verification code. --- gn_auth/auth/authentication/oauth2/views.py | 5 +- gn_auth/auth/authorisation/users/views.py | 34 ++++++++++++++ gn_auth/templates/users/unverified-user.html | 69 ++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 gn_auth/templates/users/unverified-user.html diff --git a/gn_auth/auth/authentication/oauth2/views.py b/gn_auth/auth/authentication/oauth2/views.py index 7600ada..cf815ea 100644 --- a/gn_auth/auth/authentication/oauth2/views.py +++ b/gn_auth/auth/authentication/oauth2/views.py @@ -53,7 +53,7 @@ def authorise(): redirect_uri=request.args["redirect_uri"]) form = request.form - def __authorise__(conn: db.DbConnection) -> Response: + def __authorise__(conn: db.DbConnection): email_passwd_msg = "Email or password is invalid!" redirect_response = redirect(url_for("oauth2.auth.authorise", response_type=form["response_type"], @@ -64,6 +64,9 @@ def authorise(): form.get("user:email"), check_deliverability=False) user = user_by_email(conn, email["email"]) if valid_login(conn, user, form.get("user:password", "")): + if not user.verified: + return redirect(url_for( + "oauth2.users.handle_unverified"), code=307) return server.create_authorization_response(request=request, grant_user=user) flash(email_passwd_msg, "alert-danger") return redirect_response # type: ignore[return-value] diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py index 638f0df..cb6775f 100644 --- a/gn_auth/auth/authorisation/users/views.py +++ b/gn_auth/auth/authorisation/users/views.py @@ -35,6 +35,7 @@ from gn_auth.auth.errors import ( UserVerificationError, UserRegistrationError) +from gn_auth.auth.authentication.users import valid_login, user_by_email from gn_auth.auth.authentication.oauth2.resource_server import require_oauth from gn_auth.auth.authentication.users import User, save_user, set_user_password from gn_auth.auth.authentication.oauth2.models.oauth2token import ( @@ -275,3 +276,36 @@ def list_all_users() -> Response: with require_oauth.acquire("profile group") as _the_token: return jsonify(tuple( asdict(user) for user in with_db_connection(list_users))) + +@users.route("/handle-unverified", methods=["POST"]) +def handle_unverified(): + """Handle case where user tries to login but is unverified""" + form = request.form + # TODO: Maybe have a GN2_URI setting here? + # or pass the client_id here? + return render_template( + "users/unverified-user.html", email=form.get("user:email")) + +@users.route("/send-verification", methods=["POST"]) +def send_verification_code(): + """Send verification code email.""" + form = request.form + with (db.connection(current_app.config["AUTH_DB"]) as conn, + db.cursor(conn) as cursor): + user = user_by_email(conn, form["user_email"]) + if valid_login(conn, user, form.get("user_password", "")): + cursor.execute( + "DELETE FROM user_verification_codes WHERE user_id=:user_id", + {"user_id": str(user.user_id)}) + send_verification_email(conn, user) + return jsonify({ + "status": "success", + "message": "Sent a verification code to your email." + }) + + resp = jsonify({ + "error": "InvalidLogin", + "error-description": "Invalid email or password." + }) + resp.code = 400 + return resp diff --git a/gn_auth/templates/users/unverified-user.html b/gn_auth/templates/users/unverified-user.html new file mode 100644 index 0000000..b2c6992 --- /dev/null +++ b/gn_auth/templates/users/unverified-user.html @@ -0,0 +1,69 @@ +{%extends "base.html"%} + +{%block title%}gn-auth: Verify Email{%endblock%} + +{%block pagetitle%}Verify Email{%endblock%} + +{%block content%} +{{flash_messages()}} + +