summaryrefslogtreecommitdiffstats
path: root/Bugzilla/MFA/TOTP.pm
diff options
context:
space:
mode:
Diffstat (limited to 'Bugzilla/MFA/TOTP.pm')
-rw-r--r--Bugzilla/MFA/TOTP.pm79
1 files changed, 79 insertions, 0 deletions
diff --git a/Bugzilla/MFA/TOTP.pm b/Bugzilla/MFA/TOTP.pm
new file mode 100644
index 000000000..95e3d89aa
--- /dev/null
+++ b/Bugzilla/MFA/TOTP.pm
@@ -0,0 +1,79 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::MFA::TOTP;
+use strict;
+use parent 'Bugzilla::MFA';
+
+use Auth::GoogleAuth;
+use Bugzilla::Error;
+use Bugzilla::Token qw( issue_session_token );
+use Bugzilla::Util qw( template_var generate_random_password );
+use GD::Barcode::QRcode;
+use MIME::Base64 qw( encode_base64 );
+
+sub _auth {
+ my ($self) = @_;
+ return Auth::GoogleAuth->new({
+ secret => $self->property_get('secret') // $self->property_get('secret.temp'),
+ issuer => template_var('terms')->{BugzillaTitle},
+ key_id => $self->{user}->login,
+ });
+}
+
+sub enroll {
+ my ($self) = @_;
+
+ # create a new secret for the user
+ # store it in secret.temp to avoid overwriting a valid secret
+ $self->property_set('secret.temp', generate_random_password(16));
+
+ # build the qr code
+ my $auth = $self->_auth();
+ my $otpauth = $auth->qr_code(undef, undef, undef, 1);
+ my $png = GD::Barcode::QRcode->new($otpauth, { Version => 10, ModuleSize => 3 })->plot()->png();
+ return { png => encode_base64($png), secret32 => $auth->secret32 };
+}
+
+sub enrolled {
+ my ($self) = @_;
+
+ # make the temporary secret permanent
+ $self->property_set('secret', $self->property_get('secret.temp'));
+ $self->property_delete('secret.temp');
+}
+
+sub prompt {
+ my ($self, $params) = @_;
+ my $template = Bugzilla->template;
+
+ my $vars = {
+ user => $params->{user},
+ token => scalar issue_session_token('mfa', $params->{user}),
+ type => $params->{type},
+ };
+
+ print Bugzilla->cgi->header();
+ $template->process('mfa/totp/verify.html.tmpl', $vars)
+ || ThrowTemplateError($template->error());
+}
+
+sub check {
+ my ($self, $code) = @_;
+ $self->_auth()->verify($code)
+ || ThrowUserError('mfa_totp_bad_code');
+}
+
+sub check_login {
+ my ($self, $user) = @_;
+ my $cgi = Bugzilla->cgi;
+
+ $self->check($cgi->param('code') // '');
+ $user->authorizer->mfa_verified($user, $cgi->param('type'));
+}
+
+1;