Skip to content

Commit

Permalink
Merge pull request #2865 from HyphaApp/enhancement/gh-2828-improve-2fa
Browse files Browse the repository at this point in the history
Improve 2FA workflow usability
  • Loading branch information
frjo authored Jun 17, 2022
2 parents 61602d3 + cdfcddd commit 701b17f
Show file tree
Hide file tree
Showing 18 changed files with 231 additions and 44 deletions.
13 changes: 10 additions & 3 deletions hypha/apply/users/templates/two_factor/_wizard_actions.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
{% load i18n %}

<button type="submit" class="button button--primary">{% trans "Next" %}</button>
{% if wizard.steps.current == 'token' %}
{% trans "Login" as button_text %}
{% elif wizard.steps.current == 'generator' %}
{% trans "Enable Two-Factor Authentication" as button_text %}
{% else %}
{% trans "Next" as button_text %}
{% endif %}

<button type="submit" class="button button--primary">{{ button_text }}</button>

{% if cancel_url %}
<a href="{% url 'users:account' %}"
class="link link--bold link--left-space">{% trans "Cancel" %}</a>
<a href="{% url 'users:account' %}" class="link link--bold link--left-space">{% trans "Cancel" %}</a>
{% endif %}
30 changes: 17 additions & 13 deletions hypha/apply/users/templates/two_factor/core/backup_tokens.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
{% extends "two_factor/_base_focus.html" %}
{% load i18n %}
{% load i18n users_tags %}

{% block content %}
<p><a href="{% url 'two_factor:profile'%}"
class="btn btn-link">{% trans "Back" %}</a></p>
<h1>{% block title %}{% trans "Backup Tokens" %}{% endblock %}</h1>
<p>{% blocktrans trimmed %}Backup tokens can be used when your primary and backup
phone numbers aren't available. The backup tokens below can be used
for login verification. If you've used up all your backup tokens, you
can generate a new set of backup tokens. Only the backup tokens shown
below will be valid.{% endblocktrans %}</p>
<p><a href="{% url 'users:account'%}"
class="btn btn-link">{% trans "Back to account" %}</a></p>
<h1>{% block title %}{% trans "Backup Codes" %}{% endblock %}</h1>
<p>{% blocktrans trimmed %}These codes should be kept in a safe,
private place for when you need them. When they are used up,
you can generate a new set of backup codes.{% endblocktrans %}</p>

{% if device.token_set.count %}
<ul>
{% for token in device.token_set.all %}
<li>{{ token.token }}</li>
{% endfor %}
</ul>
<p>{% blocktrans %}Print these tokens and keep them somewhere safe.{% endblocktrans %}</p>
<p>{% blocktrans %}You should now download, or print these codes,
and keep them somewhere safe.{% endblocktrans %}</p>
<form method="post">{% csrf_token %}{{ form }}
<button class="btn btn-primary" type="submit">{% trans "Regenerate Codes" %}</button>
<a class="btn btn-link link--left-space" href="data:text/plain;charset=UTF-8,{% tokens_text device.token_set.all %}" download="backup_codes.txt">
{% trans "Save Codes" %}</a>
</form>
{% else %}
<p>{% trans "You don't have any backup codes yet." %}</p>
<form method="post">{% csrf_token %}{{ form }}
<button class="btn btn-primary" type="submit">{% trans "Generate Codes" %}</button>
</form>
{% endif %}

<form method="post">{% csrf_token %}{{ form }}
<button class="btn btn-primary" type="submit">{% trans "Generate Tokens" %}</button>
</form>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
{% load i18n %}

{% block content %}
<p><a href="{% url 'two_factor:profile'%}"
<p><a href="{% url 'users:account'%}"
class="btn btn-link">{% trans "Back to account" %}</a></p>
<h1>{% block title %}{% trans "Backup Tokens" %}{% endblock %}</h1>
<p>{% blocktrans trimmed %}Backup tokens can be used when your primary and backup
phone numbers aren't available. The backup tokens below can be used
for login verification.
<h1>{% block title %}{% trans "Backup Codes" %}{% endblock %}</h1>
<p>{% blocktrans trimmed %}If you loose your smartphone, or your Authenticator app is not available,
you can use a backup code along with your username and password to login until you recover your smartphone.
Each backup code can be used only once.
</br>
If you've used up all your backup tokens, you
can generate a new set of backup tokens. Only the backup tokens shown
below will be valid.{% endblocktrans %}</p>
</br>
These codes should be kept in a secure, private place for when you need them.
When they are used up, you can generate a new set of backup codes.{% endblocktrans %}</p>
<div class="wrapper wrapper--small wrapper--inner-space-medium">
<form class="form" action="" method="POST" novalidate>
{% if form.non_field_errors %}
Expand Down
62 changes: 62 additions & 0 deletions hypha/apply/users/templates/two_factor/core/setup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{% extends "two_factor/_base_focus.html" %}
{% load i18n %}

{% block content %}
<h1>{% block title %}{% trans "Two-Factor Authentication (2FA)" %}{% endblock %}</h1>
{% if wizard.steps.current == 'welcome' %}
<p>{% blocktrans trimmed %}You are about to take your account security to the
next level.{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}To start using 2FA, you need to install an Authenticator app on your smartphone or computer. With Safari on Apple devices you can also use a built in system.{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}Install the app you choose then continue to Enable Two-Factor Authentication. {% endblocktrans %}</p>
{% elif wizard.steps.current == 'method' %}
<p>{% blocktrans trimmed %}Please select which authentication method you would
like to use.{% endblocktrans %}</p>
{% elif wizard.steps.current == 'generator' %}
<p>{% blocktrans trimmed %}To start using a token generator, please use your
smartphone to scan the QR code below. For example, use Google
Authenticator. Then, enter the token generated by the app.
{% endblocktrans %}</p>
<p><img src="{{ QR_URL }}" alt="QR Code" class="bg-white" width="200" height="200" /></p>
<details>
<summary>Advanced</summary>
<p>For advanced users, here is the otpauth url in string format.</p>
<p><a href="{{ otpauth_url }}">{{ otpauth_url }}</a></p>
</details>
{% elif wizard.steps.current == 'sms' %}
<p>{% blocktrans trimmed %}Please enter the phone number you wish to receive the
text messages on. This number will be validated in the next step.
{% endblocktrans %}</p>
{% elif wizard.steps.current == 'call' %}
<p>{% blocktrans trimmed %}Please enter the phone number you wish to be called on.
This number will be validated in the next step. {% endblocktrans %}</p>
{% elif wizard.steps.current == 'validation' %}
{% if challenge_succeeded %}
{% if device.method == 'call' %}
<p>{% blocktrans trimmed %}We are calling your phone right now, please enter the
digits you hear.{% endblocktrans %}</p>
{% elif device.method == 'sms' %}
<p>{% blocktrans trimmed %}We sent you a text message, please enter the tokens we
sent.{% endblocktrans %}</p>
{% endif %}
{% else %}
<p class="alert alert-warning" role="alert">{% blocktrans trimmed %}We've
encountered an issue with the selected authentication method. Please
go back and verify that you entered your information correctly, try
again, or use a different authentication method instead. If the issue
persists, contact the site administrator.{% endblocktrans %}</p>
{% endif %}
{% elif wizard.steps.current == 'yubikey' %}
<p>{% blocktrans trimmed %}To identify and verify your YubiKey, please insert a
token in the field below. Your YubiKey will be linked to your
account.{% endblocktrans %}</p>
{% endif %}

<form action="" method="post">{% csrf_token %}
{% include "two_factor/_wizard_forms.html" %}

{# hidden submit button to enable [enter] key #}
<input type="submit" value="" class="d-none" />

{% include "two_factor/_wizard_actions.html" %}
</form>
{% endblock %}
25 changes: 25 additions & 0 deletions hypha/apply/users/templates/two_factor/core/setup_complete.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{% extends "two_factor/_base_focus.html" %}
{% load i18n %}

{% block content %}
<h1>{% block title %}{% trans "Two-Factor Authentication(2FA)" %}{% endblock %}</h1>

<p>{% blocktrans trimmed %}Congratulations, you've successfully enabled two-factor
authentication.{% endblocktrans %}</p>
<p>{% blocktrans trimmed %}We strongly recommend you to save the backup codes.
To get the backup codes you can continue to Show Codes.{% endblocktrans %}</p>

{% if not phone_methods %}
<a href="{% url 'users:backup_tokens_password' %}" class="btn btn-link">Show Codes</a>
<a href="{% url 'users:account' %}" class="link link--bold link--left-space">{% trans "Back to Account" %}</a>
{% else %}
<p>{% blocktrans trimmed %}However, it might happen that you don't have access to
your primary token device. To enable account recovery, add a phone
number.{% endblocktrans %}</p>

<p><a href="{% url 'users:backup_tokens_password' %}" class="btn btn-block">Show Codes</a></p>
<p><a href="{% url 'two_factor:phone_create' %}"
class="btn btn-success">{% trans "Add Phone Number" %}</a></p>
{% endif %}

{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
<h1>{% block title %}{% trans "Permission Denied" %}{% endblock %}</h1>

<p>{% blocktrans trimmed %}The page you requested, enforces users to verify using
two-factor authentication for security reasons. You need to enable these
security features in order to access this page. Without enabling these security features,
two-factor authentication for security reasons. You need to set up these
security features in order to access this page. Without setting up these security features,
You can only access the account(Profile section) or can logout from the system.{% endblocktrans %}</p>

<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
account. Enable two-factor authentication for enhanced account
<p>{% blocktrans trimmed %}Two-factor authentication is not already set up for your
account. Set up two-factor authentication for enhanced account
security.{% endblocktrans %}</p>

<p>
<a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
{% trans "Enable Two-Factor Authentication" %}</a>
{% trans "Set up Two-Factor Authentication (2FA)" %}</a>
</p>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% load i18n %}

{% block content %}
<p><a href="{% url 'two_factor:profile'%}"
<p><a href="{% url 'users:account'%}"
class="btn btn-link">{% trans "Back to account" %}</a></p>
<h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1>
<p>{% blocktrans trimmed %}Disabling Two-factor authentication weakens your account security.
Expand Down
10 changes: 5 additions & 5 deletions hypha/apply/users/templates/two_factor/profile/profile.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "two_factor/_base.html" %}
{% load i18n two_factor %}
{% load i18n %}

{% block content %}
<p><a href="{% url 'users:account' %}" class="btn btn-primary">
Expand All @@ -12,7 +12,7 @@ <h1>{% block title %}{% trans "Two-Factor Authentication" %}{% endblock %}</h1>
{% if default_device_type == 'TOTPDevice' %}
<p>{% trans "Tokens will be generated by your token generator." %}</p>
{% elif default_device_type == 'PhoneDevice' %}
<p>{% blocktrans with primary=default_device|device_action %}Primary method: {{ primary }}{% endblocktrans %}</p>
<p>{% blocktrans with primary=default_device.generate_challenge_button_title %}Primary method: {{ primary }}{% endblocktrans %}</p>
{% elif default_device_type == 'RemoteYubikeyDevice' %}
<p>{% blocktrans %}Tokens will be generated by your YubiKey.{% endblocktrans %}</p>
{% endif %}
Expand All @@ -24,11 +24,11 @@ <h2>{% trans "Backup Phone Numbers" %}</h2>
<ul>
{% for phone in backup_phones %}
<li>
{{ phone|device_action }}
{{ phone.generate_challenge_button_title }}
<form method="post" action="{% url 'two_factor:phone_delete' phone.id %}"
onsubmit="return confirm('Are you sure?')">
onsubmit="return confirm({% trans 'Are you sure?' %})">
{% csrf_token %}
<button class="btn btn-xs btn-warning"
<button class="btn btn-sm btn-warning"
type="submit">{% trans "Unregister" %}</button>
</form>
</li>
Expand Down
19 changes: 15 additions & 4 deletions hypha/apply/users/templates/users/account.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% block title %}Account{% endblock %}

{% block content %}
<div class="admin-bar">
<div class="admin-bar" xmlns="http://www.w3.org/1999/html">
<div class="admin-bar__inner admin-bar__inner--with-button">
<h3 class="admin-bar__heading">{% trans "Welcome" %} {{ user }}</h3>
<a href="{% url 'dashboard:dashboard' %}" class="button button--primary button--arrow-pixels-white">
Expand All @@ -27,15 +27,26 @@ <h3>Profile</h3>
</div>

{% if show_change_password and user.has_usable_password and not backends.associated %}
<br>
<hr>
<div class="profile__column">
<h3>{% trans "Change password" %}</h3>
<h3>{% trans "Account Security" %}</h3>
<h4>{% trans "Password" %}</h4>
<p><a class="button button--primary" href="{% url 'users:password_change' %}">{% trans "Update password" %}</a></p>

<h3>{% trans "Account security" %}</h3>
<p><a class="link link--button link--button--narrow" href="{% url 'two_factor:profile' %}">{% trans "Two-factor authentication settings" %}</a></p>
<h4>{% trans "Two-Factor Authentication (2FA)" %}</h4>
{% if default_device %}
<div>
<p><a class="button button--primary" href="{% url 'users:backup_tokens_password' %}">{% trans "Show backup codes" %}</a></p>
<p><a class="button button--primary" href="{% url 'two_factor:disable' %}">{% trans "Disable 2FA" %}</a></p>
</div>
{% else %}
<p><a class="button button--primary" href="{% url 'two_factor:setup' %}">{% trans "Enable 2FA" %}</a></p>
{% endif %}
</div>
{% endif %}


<div class="profile__column">
{% if swappable_form %}
<h3>{% trans "Become" %}:</h3>
Expand Down
6 changes: 3 additions & 3 deletions hypha/apply/users/templates/users/login.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends 'base.html' %}
{% load i18n two_factor wagtailcore_tags %}
{% load i18n wagtailcore_tags %}

{% block header_modifier %}header--light-bg{% endblock %}
{% block page_title %}Login{% endblock %}
Expand Down Expand Up @@ -51,15 +51,15 @@
{% for other in other_devices %}
<button name="challenge_device" value="{{ other.persistent_id }}"
class="btn btn-default btn-block" type="submit">
{{ other|device_action }}
{{ other.generate_challenge_button_title }}
</button>
{% endfor %}</p>
{% endif %}
{% if backup_tokens %}
<p>{% trans "As a last resort, you can use a backup token:" %}</p>
<p>
<button name="wizard_goto_step" type="submit" value="backup"
class="btn btn-default btn-block">{% trans "Use Backup Token" %}</button>
class="link link--button link--button-white link--button-white--narrow">{% trans "Use Backup Token" %}</button>
</p>
{% endif %}

Expand Down
8 changes: 8 additions & 0 deletions hypha/apply/users/templatetags/users_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ def user_2fa_enabled(user):
if len(list(devices_for_user(user))):
return True
return False


@register.simple_tag
def tokens_text(token_set):
tokens_string = ""
for token in token_set:
tokens_string += str(token.token) + " \n"
return tokens_string
2 changes: 2 additions & 0 deletions hypha/apply/users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
TWOFABackupTokensPasswordView,
TWOFADisableView,
TWOFARequiredMessageView,
TWOFASetupView,
become,
create_password,
oauth,
Expand Down Expand Up @@ -87,6 +88,7 @@
),
# Two factor redirect
path('two_factor/required/', TWOFARequiredMessageView.as_view(), name='two_factor_required'),
path('two_factor/setup/', TWOFASetupView.as_view(), name='setup'),
path('two_factor/backup_tokens/password/', TWOFABackupTokensPasswordView.as_view(), name='backup_tokens_password'),
path('two_factor/disable/', TWOFADisableView.as_view(), name='disable'),
path('two_factor/admin/disable/<str:user_id>/', TWOFAAdminDisableView.as_view(), name='admin_disable'),
Expand Down
Loading

0 comments on commit 701b17f

Please sign in to comment.