diff options
author | Matthew Somerville <matthew-github@dracos.co.uk> | 2018-02-07 13:09:45 +0000 |
---|---|---|
committer | Matthew Somerville <matthew-github@dracos.co.uk> | 2018-02-07 13:09:45 +0000 |
commit | 7361782de3d072f8d09442e33aa9c42a7c181c4c (patch) | |
tree | 5b8f49a13eb6f3aeb152262cbe66d55a48c4924d /perllib/Catalyst/Authentication/Credential/2FA.pm | |
parent | 6879af98d0246b6973affff08a4e078206bb5dfc (diff) | |
parent | 3e721ddf5d9809c9f44d7dedcf2083a544e6e148 (diff) |
Merge branch '2fa-superuser'
Diffstat (limited to 'perllib/Catalyst/Authentication/Credential/2FA.pm')
-rw-r--r-- | perllib/Catalyst/Authentication/Credential/2FA.pm | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/perllib/Catalyst/Authentication/Credential/2FA.pm b/perllib/Catalyst/Authentication/Credential/2FA.pm new file mode 100644 index 000000000..6cb1dd297 --- /dev/null +++ b/perllib/Catalyst/Authentication/Credential/2FA.pm @@ -0,0 +1,141 @@ +package Catalyst::Authentication::Credential::2FA; + +use strict; +use warnings; +use Auth::GoogleAuth; + +our $VERSION = "0.01"; + +sub new { + my ($class, $config, $c, $realm) = @_; + my $self = { %$config }; + bless $self, $class; + return $self; +} + +sub authenticate { + my ( $self, $c, $realm, $authinfo ) = @_; + + my $userfindauthinfo = {%{$authinfo}}; + delete($userfindauthinfo->{password}); + + my $user_obj = $realm->find_user($userfindauthinfo, $c); + if (ref($user_obj)) { + # We don't care unless user is a superuser and has a 2FA secret + return $user_obj unless $user_obj->is_superuser; + return $user_obj unless $user_obj->get_extra_metadata('2fa_secret'); + + $c->stash->{token} = $c->get_param('token'); + + if ($self->check_2fa($c, $user_obj)) { + if ($c->stash->{token}) { + my $token = $c->forward('/tokens/load_auth_token', [ $c->stash->{token}, '2fa' ]); + # Will contain a detach_to and report/update data + $c->stash($token->data); + } + return $user_obj; + } + + if ($c->stash->{tfa_data}) { + my $token = $c->model("DB::Token")->create( { + scope => '2fa', + data => $c->stash->{tfa_data}, + }); + $c->stash->{token} = $token->token; + } + + $c->stash->{template} = 'auth/2faform.html'; + $c->detach; + } +} + +sub check_2fa { + my ($self, $c, $user) = @_; + + if (my $code = $c->get_param('2fa_code')) { + my $auth = Auth::GoogleAuth->new; + my $secret32 = $user->get_extra_metadata('2fa_secret'); + return 1 if $auth->verify($code, 1, $secret32); + $c->stash->{incorrect_code} = 1; + } + return 0; +} + +__PACKAGE__; + +__END__ + +=pod + +=head1 NAME + +Catalyst::Authentication::Credential::2FA - Authenticate a user +with a two-factor authentication code. + +=head1 SYNOPSIS + + use Catalyst qw/ + Authentication + /; + + package MyApp::Controller::Auth; + + sub login : Local { + my ( $self, $c ) = @_; + + $c->authenticate( { username => $c->req->param('username'), + password => $c->req->param('password') }); + } + +=head1 DESCRIPTION + +This authentication credential checker takes authentication information +(most often a username), and only passes if a valid 2FA code is then +entered. It only works for Users that have an is_superuser flag set, +plus store the 2FA secret in a FixMyStreet::Role::Extra metadata key. + +=head1 CONFIGURATION + + # example + 'Plugin::Authentication' => { + default => { + credential => { + class => 'MultiFactor', + factors => [ + { + class => 'Password', + password_field => 'password', + password_type => 'self_check', + }, + { + class => '2FA', + }, + ], + }, + store => { + class => 'DBIx::Class', + user_model => 'DB::User', + }, + }, + + +=over 4 + +=item class + +The classname used for Credential. This is part of +L<Catalyst::Plugin::Authentication> and is the method by which +Catalyst::Authentication::Credential::2FA is loaded as the +credential validator. For this module to be used, this must be set to +'2FA'. + +=back + +=head1 USAGE + +Once configured as indicated above, authenticating using this module is a +matter of calling $c->authenticate() as normal. If you wish to use it in +combination with e.g. password authentication as well (so it actually is +two-factor!), check out Catalyst::Authentication::Credential::MultiFactor. + +=cut |