mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-23 07:46:23 -04:00
create webauthn 2fa page
if user has `webauthn_auth` as their auth type, then redirect them to an interstitial that prompts them to click on a button which right now just logs to the JS console, but in a future commit will open up the webauthn browser prompt content is unsurprisingly not final.
This commit is contained in:
13
app/assets/javascripts/authenticateSecurityKey.js
Normal file
13
app/assets/javascripts/authenticateSecurityKey.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
(function (window) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
window.GOVUK.Modules.AuthenticateSecurityKey = function () {
|
||||||
|
this.start = function (component) {
|
||||||
|
$(component)
|
||||||
|
.on('click', function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
console.log('pretend you just logged in okay');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})(window);
|
||||||
@@ -50,7 +50,7 @@ def sign_in():
|
|||||||
if user.email_auth:
|
if user.email_auth:
|
||||||
return redirect(url_for('.two_factor_email_sent', next=redirect_url))
|
return redirect(url_for('.two_factor_email_sent', next=redirect_url))
|
||||||
if user.webauthn_auth:
|
if user.webauthn_auth:
|
||||||
raise NotImplementedError('webauthn not supported yet')
|
return redirect(url_for('.two_factor_webauthn', next=redirect_url))
|
||||||
|
|
||||||
# Vague error message for login in case of user not known, locked, inactive or password not verified
|
# Vague error message for login in case of user not known, locked, inactive or password not verified
|
||||||
flash(Markup(
|
flash(Markup(
|
||||||
|
|||||||
@@ -82,6 +82,13 @@ def two_factor():
|
|||||||
return render_template('views/two-factor.html', form=form, redirect_url=redirect_url)
|
return render_template('views/two-factor.html', form=form, redirect_url=redirect_url)
|
||||||
|
|
||||||
|
|
||||||
|
@main.route('/two-factor-webauthn', methods=['GET'])
|
||||||
|
@redirect_to_sign_in
|
||||||
|
def two_factor_webauthn():
|
||||||
|
redirect_url = request.args.get('next')
|
||||||
|
return render_template('views/two-factor-webauthn.html', redirect_url=redirect_url)
|
||||||
|
|
||||||
|
|
||||||
@main.route('/re-validate-email', methods=['GET'])
|
@main.route('/re-validate-email', methods=['GET'])
|
||||||
def revalidate_email_sent():
|
def revalidate_email_sent():
|
||||||
title = 'Email resent' if request.args.get('email_resent') else 'Check your email'
|
title = 'Email resent' if request.args.get('email_resent') else 'Check your email'
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ class HeaderNavigation(Navigation):
|
|||||||
'two_factor_email',
|
'two_factor_email',
|
||||||
'two_factor_email_sent',
|
'two_factor_email_sent',
|
||||||
'two_factor_email_interstitial',
|
'two_factor_email_interstitial',
|
||||||
|
'two_factor_webauthn',
|
||||||
'verify',
|
'verify',
|
||||||
'verify_email',
|
'verify_email',
|
||||||
},
|
},
|
||||||
|
|||||||
33
app/templates/views/two-factor-webauthn.html
Normal file
33
app/templates/views/two-factor-webauthn.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{% extends "withoutnav_template.html" %}
|
||||||
|
{% from "components/page-header.html" import page_header %}
|
||||||
|
{% from "components/button/macro.njk" import govukButton %}
|
||||||
|
|
||||||
|
{% set page_title = 'Security keys' %}
|
||||||
|
|
||||||
|
{% block per_page_title %}
|
||||||
|
{{ page_title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
{{ page_header(
|
||||||
|
page_title,
|
||||||
|
back_link=url_for('.user_profile')
|
||||||
|
) }}
|
||||||
|
|
||||||
|
<div class="govuk-grid-row">
|
||||||
|
<div class="govuk-grid-column-five-sixths">
|
||||||
|
<p class="govuk-body">Security key</p>
|
||||||
|
<p class="govuk-body">When you are ready to authenticate, press the button below.</p>
|
||||||
|
|
||||||
|
{{ govukButton({
|
||||||
|
"element": "button",
|
||||||
|
"text": "Webauthn authenticate click me click me",
|
||||||
|
"classes": "govuk-button--secondary",
|
||||||
|
"attributes": {
|
||||||
|
"data-module": "authenticate-security-key",
|
||||||
|
"data-csrf-token": csrf_token(),
|
||||||
|
}
|
||||||
|
}) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -180,6 +180,7 @@ const javascripts = () => {
|
|||||||
paths.src + 'javascripts/collapsibleCheckboxes.js',
|
paths.src + 'javascripts/collapsibleCheckboxes.js',
|
||||||
paths.src + 'javascripts/radioSlider.js',
|
paths.src + 'javascripts/radioSlider.js',
|
||||||
paths.src + 'javascripts/registerSecurityKey.js',
|
paths.src + 'javascripts/registerSecurityKey.js',
|
||||||
|
paths.src + 'javascripts/authenticateSecurityKey.js',
|
||||||
paths.src + 'javascripts/updateStatus.js',
|
paths.src + 'javascripts/updateStatus.js',
|
||||||
paths.src + 'javascripts/homepage.js',
|
paths.src + 'javascripts/homepage.js',
|
||||||
paths.src + 'javascripts/main.js',
|
paths.src + 'javascripts/main.js',
|
||||||
|
|||||||
@@ -160,6 +160,34 @@ def test_process_email_auth_sign_in_return_2fa_template(
|
|||||||
mock_verify_password.assert_called_with(api_user_active_email_auth['id'], 'val1dPassw0rd!')
|
mock_verify_password.assert_called_with(api_user_active_email_auth['id'], 'val1dPassw0rd!')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('redirect_url', [
|
||||||
|
None,
|
||||||
|
f'/services/{SERVICE_ONE_ID}/templates',
|
||||||
|
])
|
||||||
|
def test_process_webauthn_auth_sign_in_redirects_to_webauthn_with_next_redirect(
|
||||||
|
client,
|
||||||
|
api_user_active,
|
||||||
|
mocker,
|
||||||
|
mock_verify_password,
|
||||||
|
redirect_url
|
||||||
|
):
|
||||||
|
api_user_active['auth_type'] = 'webauthn_auth'
|
||||||
|
mock_get_user_by_email = mocker.patch('app.user_api_client.get_user_by_email', return_value=api_user_active)
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for(
|
||||||
|
'main.sign_in', next=redirect_url
|
||||||
|
),
|
||||||
|
data={
|
||||||
|
'email_address': 'valid@example.gov.uk',
|
||||||
|
'password': 'val1dPassw0rd!'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
mock_get_user_by_email.assert_called_once_with('valid@example.gov.uk')
|
||||||
|
assert response.status_code == 302
|
||||||
|
assert response.location == url_for('.two_factor_webauthn', _external=True, next=redirect_url)
|
||||||
|
|
||||||
|
|
||||||
def test_should_return_locked_out_true_when_user_is_locked(
|
def test_should_return_locked_out_true_when_user_is_locked(
|
||||||
client,
|
client,
|
||||||
mock_get_user_by_email_locked,
|
mock_get_user_by_email_locked,
|
||||||
|
|||||||
@@ -258,15 +258,25 @@ def test_two_factor_returns_error_when_user_is_locked(
|
|||||||
assert 'Code not found' in response.get_data(as_text=True)
|
assert 'Code not found' in response.get_data(as_text=True)
|
||||||
|
|
||||||
|
|
||||||
def test_two_factor_should_redirect_to_sign_in_if_user_not_in_session(
|
def test_two_factor_post_should_redirect_to_sign_in_if_user_not_in_session(
|
||||||
client,
|
client_request,
|
||||||
api_user_active,
|
|
||||||
mock_get_user,
|
|
||||||
):
|
):
|
||||||
response = client.post(url_for('main.two_factor'),
|
client_request.post(
|
||||||
data={'sms_code': '12345'})
|
'main.two_factor',
|
||||||
assert response.status_code == 302
|
_data={'sms_code': '12345'},
|
||||||
assert response.location == url_for('main.sign_in', _external=True)
|
_expected_redirect=url_for('main.sign_in', _external=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('endpoint', ['main.two_factor_webauthn', 'main.two_factor'])
|
||||||
|
def test_two_factor_get_should_redirect_to_sign_in_if_user_not_in_session(
|
||||||
|
client_request,
|
||||||
|
endpoint,
|
||||||
|
):
|
||||||
|
client_request.get(
|
||||||
|
endpoint,
|
||||||
|
_expected_redirect=url_for('main.sign_in', _external=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@freeze_time('2020-01-27T12:00:00')
|
@freeze_time('2020-01-27T12:00:00')
|
||||||
|
|||||||
@@ -293,6 +293,7 @@ EXCLUDED_ENDPOINTS = tuple(map(Navigation.get_endpoint_with_blueprint, {
|
|||||||
'two_factor_email',
|
'two_factor_email',
|
||||||
'two_factor_email_interstitial',
|
'two_factor_email_interstitial',
|
||||||
'two_factor_email_sent',
|
'two_factor_email_sent',
|
||||||
|
'two_factor_webauthn',
|
||||||
'update_email_branding',
|
'update_email_branding',
|
||||||
'update_letter_branding',
|
'update_letter_branding',
|
||||||
'upload_a_letter',
|
'upload_a_letter',
|
||||||
|
|||||||
Reference in New Issue
Block a user