From 052c43d645a0cf5ae89a822ce04c4475da2b807f Mon Sep 17 00:00:00 2001
From: f00wl <f00wl@felinn.org>
Date: Sat, 21 Aug 2021 21:27:02 +0200
Subject: [PATCH] Update login workflow to use 2FA

---
 hiboo/account/forms.py                        |  5 +++++
 hiboo/account/login.py                        | 20 +++++++++++++++++++
 .../templates/account_totp_verify.html        | 14 +++++++++++++
 3 files changed, 39 insertions(+)
 create mode 100644 hiboo/account/templates/account_totp_verify.html

diff --git a/hiboo/account/forms.py b/hiboo/account/forms.py
index 7aa6b6f7..4c05b54e 100644
--- a/hiboo/account/forms.py
+++ b/hiboo/account/forms.py
@@ -11,6 +11,11 @@ class LoginForm(flask_wtf.FlaskForm):
     submit = fields.SubmitField(_('Sign in'))
 
 
+class TotpForm(flask_wtf.FlaskForm):
+    totp = fields.PasswordField(_('Time-based One-Time Password'), [validators.DataRequired()])
+    submit = fields.SubmitField(_('Validate'))
+
+
 class SignupForm(flask_wtf.FlaskForm):
     username = fields.StringField(_('Username'), [
         validators.DataRequired(),
diff --git a/hiboo/account/login.py b/hiboo/account/login.py
index 066dbbbc..9c4a3fa4 100644
--- a/hiboo/account/login.py
+++ b/hiboo/account/login.py
@@ -14,6 +14,9 @@ def signin():
     if form.validate_on_submit():
         user = models.User.login(form.username.data, form.password.data)
         if user:
+            if "totp" in user.auths:
+                session["username"] = user.username
+                return flask.redirect(flask.url_for(".totp_verify"))
             flask_login.login_user(user)
             if form.remember_me.data == True:
                 session.permanent = True
@@ -24,6 +27,23 @@ def signin():
         action=utils.url_for(".signin"))
 
 
+@blueprint.route("/totp/verify", methods=["GET", "POST"])
+def totp_verify():
+    form = forms.TotpForm()
+    if "username" not in session:
+        return flask.redirect(flask.url_for(".signin"))
+    if form.validate_on_submit():
+        user = models.User.query.filter_by(username=session["username"]).first()
+        if user and user.auths["totp"].check_totp(form.totp.data):
+            flask_login.login_user(user)
+            session.pop("username")
+            return flask.redirect(utils.url_or_intent(".home"))
+        else:
+            flask.flash(_("Wrong password"), "danger")
+    return flask.render_template("account_totp_verify.html", form=form,
+        action=utils.url_for(".totp_verify"))
+
+
 @blueprint.route("/signout")
 @security.authentication_required()
 def signout():
diff --git a/hiboo/account/templates/account_totp_verify.html b/hiboo/account/templates/account_totp_verify.html
new file mode 100644
index 00000000..47a1513c
--- /dev/null
+++ b/hiboo/account/templates/account_totp_verify.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block title %}{% trans %}Time-based One-Time Password (TOTP) verify{% endtrans %}{% endblock %}
+{% block subtitle %}{% trans %}to access your account{% endtrans %}{% endblock %}
+
+{% block content %}
+
+{{ macros.form(form) }}
+
+{% endblock %}
+
+{% block actions %}
+<a href="{{ utils.url_for(".signup") }}" class="btn btn-success">{% trans %}Sign up{% endtrans %}</a>
+{% endblock %}
-- 
GitLab