Browse Source

Add auth method selection step

This has dual purpose:

* if user has both WebAuthn and TOTP set up, they can choose
  between the two as equal options.
* we initiate WebAuthn flow only after an explicit user action
  (button press). This may help with authentication failures
  on recent MacOS, iOS and iPadOS versions [1]

[1] https://support.yubico.com/hc/en-us/articles/360022004600-No-reaction-when-using-WebAuthn-on-macOS-iOS-and-iPadOS
pull/551/head
Pēteris Caune 3 years ago
parent
commit
ca3afa33f9
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
4 changed files with 27 additions and 15 deletions
  1. +2
    -2
      hc/accounts/tests/test_login_webauthn.py
  2. +2
    -3
      static/js/login_tfa.js
  3. +2
    -2
      templates/accounts/login_totp.html
  4. +21
    -8
      templates/accounts/login_webauthn.html

+ 2
- 2
hc/accounts/tests/test_login_webauthn.py View File

@ -21,7 +21,7 @@ class LoginWebAuthnTestCase(BaseTestCase):
def test_it_shows_form(self): def test_it_shows_form(self):
r = self.client.get(self.url) r = self.client.get(self.url)
self.assertContains(r, "Waiting for security key") self.assertContains(r, "Waiting for security key")
self.assertNotContains(r, "Use the authenticator app instead?")
self.assertNotContains(r, "Use authenticator app")
# It should put a "state" key in the session: # It should put a "state" key in the session:
self.assertIn("state", self.client.session) self.assertIn("state", self.client.session)
@ -31,7 +31,7 @@ class LoginWebAuthnTestCase(BaseTestCase):
self.profile.save() self.profile.save()
r = self.client.get(self.url) r = self.client.get(self.url)
self.assertContains(r, "Use the authenticator app instead?")
self.assertContains(r, "Use authenticator app")
def test_it_requires_unauthenticated_user(self): def test_it_requires_unauthenticated_user(self):
self.client.login(username="[email protected]", password="password") self.client.login(username="[email protected]", password="password")


+ 2
- 3
static/js/login_tfa.js View File

@ -9,6 +9,7 @@ $(function() {
} }
function authenticate() { function authenticate() {
$("#pick-method").addClass("hide");
$("#waiting").removeClass("hide"); $("#waiting").removeClass("hide");
$("#error").addClass("hide"); $("#error").addClass("hide");
@ -30,8 +31,6 @@ $(function() {
}); });
} }
$("#use-key-btn").click(authenticate);
$("#retry").click(authenticate); $("#retry").click(authenticate);
authenticate();
}); });

+ 2
- 2
templates/accounts/login_totp.html View File

@ -17,8 +17,8 @@
type="text" type="text"
name="code" name="code"
pattern="[0-9]{6}" pattern="[0-9]{6}"
title="six-digit code"
placeholder="123456"
title="6-digit code"
placeholder="6-digit code"
class="form-control input-lg" /> class="form-control input-lg" />
{% if form.code.errors %} {% if form.code.errors %}
<div class="help-block"> <div class="help-block">


+ 21
- 8
templates/accounts/login_webauthn.html View File

@ -12,6 +12,27 @@
encrypt="multipart/form-data"> encrypt="multipart/form-data">
<h1>Two-factor Authentication</h1> <h1>Two-factor Authentication</h1>
<div id="pick-method">
{% if offer_totp %}
<p>Please select how you want to authenticate.</p>
{% else %}
<p>
Please authenticate using your security key.<br />
When you are ready, press the button below.
</p>
{% endif %}
<button
id="use-key-btn"
type="button"
class="btn btn-primary">Use security key</button>
{% if offer_totp %}
<a href="{% url 'hc-login-totp' %}" class="btn btn-default">
Use authenticator app
</a>
{% endif %}
</div>
{% csrf_token %} {% csrf_token %}
<input id="credential_id" type="hidden" name="credential_id"> <input id="credential_id" type="hidden" name="credential_id">
<input id="authenticator_data" type="hidden" name="authenticator_data"> <input id="authenticator_data" type="hidden" name="authenticator_data">
@ -44,14 +65,6 @@
</div> </div>
</div> </div>
{% if offer_totp %}
<p>
<a href="{% url 'hc-login-totp' %}">
Use the authenticator app instead?
</a>
</p>
{% endif %}
<div id="success" class="hide"> <div id="success" class="hide">
<div class="alert alert-success"> <div class="alert alert-success">
<strong>Success!</strong> <strong>Success!</strong>


Loading…
Cancel
Save