diff --git a/app/config.py b/app/config.py
index 89bd3dc6..77dcb641 100644
--- a/app/config.py
+++ b/app/config.py
@@ -113,3 +113,4 @@ AVATAR_URL_EXPIRATION = 3600 * 24 * 7 # 1h*24h/d*7d=1week
# session key
HIGHLIGHT_GEN_EMAIL_ID = "highlight_gen_email_id"
+MFA_USER_ID = "mfa_user_id"
diff --git a/app/dashboard/__init__.py b/app/dashboard/__init__.py
index f8576933..8af6f572 100644
--- a/app/dashboard/__init__.py
+++ b/app/dashboard/__init__.py
@@ -9,4 +9,5 @@ from .views import (
api_key,
custom_domain,
alias_contact_manager,
+ mfa_setup,
)
diff --git a/app/dashboard/templates/dashboard/mfa_setup.html b/app/dashboard/templates/dashboard/mfa_setup.html
new file mode 100644
index 00000000..55def8fb
--- /dev/null
+++ b/app/dashboard/templates/dashboard/mfa_setup.html
@@ -0,0 +1,51 @@
+{% extends 'default.html' %}
+
+{% block title %}
+ MFA Setup
+{% endblock %}
+
+{% block head %}
+
+{% endblock %}
+
+{% block default_content %}
+
+
Two Factor Authentication
+
Please open a TOTP application (Google Authenticator, Authy, etc)
+ on your smartphone and scan the following QR Code:
+
+
+
+
+
+
+
+ Or you can use the manual entry with the following key:
+
+
+
+ {{ current_user.otp_secret }}
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/app/dashboard/views/mfa_setup.py b/app/dashboard/views/mfa_setup.py
new file mode 100644
index 00000000..0d17de2e
--- /dev/null
+++ b/app/dashboard/views/mfa_setup.py
@@ -0,0 +1,49 @@
+import pyotp
+from flask import render_template, flash, redirect, url_for
+from flask_login import login_required, current_user
+from flask_wtf import FlaskForm
+from wtforms import StringField, validators
+
+from app.dashboard.base import dashboard_bp
+from app.extensions import db
+from app.log import LOG
+
+
+class OtpTokenForm(FlaskForm):
+ token = StringField("Token", validators=[validators.DataRequired()])
+
+
+@dashboard_bp.route("/mfa_setup", methods=["GET", "POST"])
+@login_required
+def mfa_setup():
+ if current_user.enable_otp:
+ flash("you have already enabled MFA", "warning")
+ return redirect(url_for("dashboard.index"))
+
+ otp_token_form = OtpTokenForm()
+
+ if not current_user.otp_secret:
+ LOG.d("Generate otp_secret for user %s", current_user)
+ current_user.otp_secret = pyotp.random_base32()
+ db.session.commit()
+
+ totp = pyotp.TOTP(current_user.otp_secret)
+
+ if otp_token_form.validate_on_submit():
+ token = otp_token_form.token.data
+
+ if totp.verify(token):
+ current_user.enable_otp = True
+ db.session.commit()
+ flash("2FA has been activated", "success")
+ return redirect(url_for("dashboard.index"))
+ else:
+ flash("Incorrect token", "warning")
+
+ otp_uri = pyotp.totp.TOTP(current_user.otp_secret).provisioning_uri(
+ name=current_user.email, issuer_name="SimpleLogin"
+ )
+
+ return render_template(
+ "dashboard/mfa_setup.html", otp_token_form=otp_token_form, otp_uri=otp_uri
+ )