aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Somerville <matthew@mysociety.org>2019-10-25 15:01:56 +0100
committerMatthew Somerville <matthew@mysociety.org>2019-10-30 15:16:02 +0000
commit03390054664ca11ce1db178dff5065ce8f545925 (patch)
tree519f103ba47bf2017344e6ce8aadc0eb292cea27
parent0a48f7e50d4157adc5a6888285f7f07bade5d20f (diff)
Fix password reset on 2FA accounts.
-rw-r--r--perllib/FixMyStreet/App/Controller/Auth.pm25
-rw-r--r--t/app/controller/auth.t23
-rw-r--r--templates/web/base/auth/2fa/form.html2
3 files changed, 45 insertions, 5 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Auth.pm b/perllib/FixMyStreet/App/Controller/Auth.pm
index 2aa1144a0..ecca92bd3 100644
--- a/perllib/FixMyStreet/App/Controller/Auth.pm
+++ b/perllib/FixMyStreet/App/Controller/Auth.pm
@@ -241,11 +241,11 @@ sub token : Path('/M') : Args(1) {
&& (!$c->user_exists || $c->user->id ne $data->{old_user_id});
my $type = $data->{login_type} || 'email';
- $c->detach( '/auth/process_login', [ $data, $type ] );
+ $c->detach( '/auth/process_login', [ $data, $type, $url_token ] );
}
sub process_login : Private {
- my ( $self, $c, $data, $type ) = @_;
+ my ( $self, $c, $data, $type, $url_token ) = @_;
# sign out in case we are another user
$c->logout();
@@ -257,8 +257,8 @@ sub process_login : Private {
$c->detach( '/page_error_403_access_denied', [] )
if FixMyStreet->config('SIGNUPS_DISABLED') && !$user->in_storage && !$data->{old_user_id};
- # People using 2FA can not log in by code
- $c->detach( '/page_error_403_access_denied', [] ) if $user->has_2fa;
+ # People using 2FA need to supply a code
+ $c->forward( 'token_2fa', [ $user, $url_token ] ) if $user->has_2fa;
if ($data->{old_user_id}) {
# Were logged in as old_user_id, want to switch to $user
@@ -303,6 +303,23 @@ sub process_login : Private {
$c->detach( 'redirect_on_signin', [ $data->{r}, $data->{p} ] );
}
+=head2 token_2fa
+
+Used after clicking an email token link to request a 2FA code
+
+=cut
+
+sub token_2fa : Private {
+ my ($self, $c, $user, $url_token) = @_;
+
+ return if $c->check_2fa($user->has_2fa);
+
+ $c->stash->{form_action} = $c->req->path;
+ $c->stash->{token} = $url_token;
+ $c->stash->{template} = 'auth/2fa/form.html';
+ $c->detach;
+}
+
=head2 redirect_on_signin
Used after signing in to take the person back to where they were.
diff --git a/t/app/controller/auth.t b/t/app/controller/auth.t
index cc40bd2b0..652a4b293 100644
--- a/t/app/controller/auth.t
+++ b/t/app/controller/auth.t
@@ -343,4 +343,27 @@ subtest "Test enforced two-factor authentication" => sub {
};
};
+subtest "Check two-factor log in by email works" => sub {
+ use Auth::GoogleAuth;
+ my $auth = Auth::GoogleAuth->new;
+ my $code = $auth->code;
+
+ my $user = FixMyStreet::App->model('DB::User')->find( { email => $test_email } );
+ $user->set_extra_metadata('2fa_secret', $auth->secret32);
+ $user->update;
+
+ $mech->clear_emails_ok;
+ $mech->get_ok('/auth');
+ $mech->submit_form_ok({
+ fields => { username => $test_email, password_register => $test_password },
+ button => 'sign_in_by_code',
+ }, "log in by email");
+
+ my $link = $mech->get_link_from_email;
+ $mech->get_ok($link);
+ $mech->content_contains('Please generate a two-factor code');
+ $mech->submit_form_ok({ with_fields => { '2fa_code' => $code } }, "provide correct 2FA code" );
+ $mech->logged_in_ok;
+};
+
done_testing();
diff --git a/templates/web/base/auth/2fa/form.html b/templates/web/base/auth/2fa/form.html
index bd6a7bd18..e093f6554 100644
--- a/templates/web/base/auth/2fa/form.html
+++ b/templates/web/base/auth/2fa/form.html
@@ -8,7 +8,7 @@
<h1>[% loc("Nearly done! Now check your phone&hellip;") %]</h1>
<p>[% loc("Please generate a two-factor code and enter it below:") %]</p>
[% END %]
- <form action="/auth" method="post">
+ <form action="/[% form_action OR 'auth' %]" method="post">
<input type="hidden" name="username" value="[% c.get_param('username') | html %]">
<input type="hidden" name="password_sign_in" value="[% c.get_param('password_sign_in') | html %]">
<input type="hidden" name="r" value="[% c.get_param('r') | html %]">