Skip to content
Snippets Groups Projects
Commit 951630c2 authored by f00wl's avatar f00wl
Browse files

Add 2FA settings in user space

New routes to setup, delete and view TOTP.
Print a QR code for TOTP setup with smart phone app
Log TOTP key changes in History
Added necessary libs
parent e9978913
No related branches found
No related tags found
No related merge requests found
......@@ -2,9 +2,13 @@ from hiboo.account import blueprint, forms
from hiboo import models, security
from wtforms import fields
from flask_babel import lazy_gettext as _
from io import BytesIO
import flask
import flask_login
import pyotp
import qrcode
import base64
@blueprint.route("/password", methods=["GET", "POST"])
......@@ -25,6 +29,58 @@ def password():
return flask.render_template("account_password.html", form=form)
@blueprint.route("/totp", methods=["GET", "POST"])
@security.authentication_required()
def totp():
user = flask_login.current_user
infos = {}
if "totp" in user.auths:
key = user.auths["totp"].value
issuer = flask.current_app.config['WEBSITE_NAME']
totp_uri = pyotp.totp.TOTP(key).provisioning_uri(
name=user.username,
issuer_name=issuer)
img = qrcode.make(totp_uri).get_image()
buffered = BytesIO()
img.save(buffered, format="PNG")
qr = base64.b64encode(buffered.getvalue()).decode('ascii')
infos = {
"key": key,
"name": user.username,
"issuer": issuer,
"qr": qr
}
return flask.render_template("account_totp.html", infos=infos)
@blueprint.route("/totp/setup", methods=["GET", "POST"])
@security.authentication_required()
@security.confirmation_required("Setup 2FA with TOTP")
def totp_setup():
user = flask_login.current_user
auth = models.Auth("totp")
auth.set_otp_key()
user.auths["totp"] = auth
models.log(models.History.MFA, user=flask_login.current_user)
models.db.session.add(auth)
models.db.session.commit()
flask.flash(_("Successfully setup 2FA"), "success")
return flask.redirect(flask.url_for(".totp"))
@blueprint.route("/totp/delete", methods=["GET", "POST"])
@security.authentication_required()
@security.confirmation_required("Delete 2FA with TOTP")
def totp_delete():
user = flask_login.current_user
auth = user.auths["totp"]
models.log(models.History.MFA, user=flask_login.current_user)
models.db.session.delete(auth)
models.db.session.commit()
flask.flash(_("Successfully deleted 2FA"), "success")
return flask.redirect(flask.url_for(".totp"))
@blueprint.route("/contact", methods=["GET", "POST"])
@security.authentication_required()
def contact():
......
{% extends "base.html" %}
{% block title %}{% trans %}Two-factor authentication with Time-based One-Time Password (TOTP){% endtrans %}{% endblock %}
{% block content %}
{% if infos == {} %}
<p>{% trans %}Two-factor authentication with Time-based One-Time Passowrd is not setup.{% endtrans %}</p>
{% else %}
<p>{% trans %}Scan this QR code or use text informations{% endtrans %}</p>
<img src="data:image/png;base64,{{ infos.qr }}" width=300 height=300>
<pre>
{% trans %}Secret key:{% endtrans %} <code>{{ infos.key }}</code>
{% trans %}Name:{% endtrans %} <code>{{ infos.name }}</code>
{% trans %}Issuer:{% endtrans %} <code>{{ infos.issuer }}</code>
</pre>
{% endif %}
{% endblock %}
{% block actions %}
{% if infos == {} %}
<a href="{{ url_for(".totp_setup") }}" class="btn btn-info">{% trans %}Setup 2FA{% endtrans %}</a>
{% else %}
<a href="{{ url_for(".totp_delete") }}" class="btn btn-info">{% trans %}Delete 2FA{% endtrans %}</a>
{% endif %}
{% endblock %}
......@@ -322,11 +322,13 @@ class History(db.Model):
STATUS = "status"
TRANSITION = "transition"
PASSWORD = "password"
MFA = "2FA"
DESCRIPTION = {
SIGNUP: _("signed up for this account"),
CREATE: _("created the profile {this.profile.username} on {this.service.name}"),
PASSWORD: _("changed this account password"),
MFA: _("alter this account two-factor authentication settings"),
STATUS: _("set the {this.service.name} profile {this.profile.username} as {this.value}"),
TRANSITION: _("did {this.transition.label} the profile {this.profile.username} on {this.service.name}")
}
......
......@@ -20,6 +20,11 @@
<i class="nav-icon fas fa-lock"></i> <p>{% trans %}Change password{% endtrans %}</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for("account.totp") }}">
<i class="nav-icon fas fa-qrcode"></i> <p>{% trans %}Two-factor authentication{% endtrans %}</p>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for("account.signout") }}">
<i class="nav-icon fas fa-sign-out-alt"></i> <p>{% trans %}Sign out{% endtrans %}</p>
......
......@@ -25,4 +25,6 @@ terminaltables
Werkzeug==0.16.0
Pillow
email_validator
pyotp
qrcode
git+https://forge.tedomum.net/tedomum/axon.git
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment