lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/2F/WebAuthn.pm

122 lines
3.2 KiB
Perl

# WebAuthn second factor authentication
#
# This plugin handle authentications to ask WebAuthn second factor for users that
# have registered their WebAuthn authenticators
package Lemonldap::NG::Portal::2F::WebAuthn;
use strict;
use Mouse;
use JSON qw(from_json to_json);
use MIME::Base64 qw(encode_base64url decode_base64url);
use Crypt::URandom;
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_ERROR
PE_SENDRESPONSE
PE_BADCREDENTIALS
);
our $VERSION = '2.0.15';
extends 'Lemonldap::NG::Portal::Main::SecondFactor';
with 'Lemonldap::NG::Portal::Lib::WebAuthn';
# INITIALIZATION
has rule => ( is => 'rw' );
has prefix => ( is => 'ro', default => 'webauthn' );
has logo => ( is => 'rw', default => 'webauthn.png' );
sub init {
my ($self) = @_;
# If self registration is enabled and "activation" is just set to
# "enabled", replace the rule to detect if user has registered its key
if ( $self->conf->{webauthn2fSelfRegistration}
and $self->conf->{webauthn2fActivation} eq '1' )
{
$self->conf->{webauthn2fActivation} = 'has2f("WebAuthn")';
}
return 0
unless ( $self->Lemonldap::NG::Portal::Main::SecondFactor::init() );
return 1;
}
# RUNNING METHODS
# Main method
sub run {
my ( $self, $req, $token ) = @_;
my $user = $req->user;
my $checkLogins = $req->param('checkLogins');
$self->logger->debug("WebAuthn: checkLogins set") if $checkLogins;
my $stayconnected = $req->param('stayconnected');
$self->logger->debug("WebAuthn: stayconnected set") if $stayconnected;
my $request = $self->generateChallenge( $req, $req->sessionInfo );
unless ($request) {
$self->logger->error(
"No registered WebAuthn devices for " . $req->user );
return PE_ERROR;
}
$self->ott->updateToken( $token, _webauthn_request => $request );
my $tmp = $self->p->sendHtml(
$req,
'webauthn2fcheck',
params => {
MAIN_LOGO => $self->conf->{portalMainLogo},
SKIN => $self->p->getSkin($req),
DATA => to_json( { request => $request } ),
TOKEN => $token,
CHECKLOGINS => $checkLogins,
STAYCONNECTED => $stayconnected
}
);
$req->response($tmp);
return PE_SENDRESPONSE;
}
sub verify {
my ( $self, $req, $session ) = @_;
my $user = $session->{ $self->conf->{whatToTrace} };
my $credential_json = $req->param('credential');
unless ($credential_json) {
$self->logger->error('Missing signature parameter');
return PE_ERROR;
}
my $signature_options = $session->{_webauthn_request};
delete $session->{_webauthn_request};
my $validation_result = eval {
$self->validateAssertion( $req, $session, $signature_options,
$credential_json );
};
if ($@) {
$self->logger->error("Webauthn validation error for $user: $@");
return PE_ERROR;
}
if ( $validation_result->{success} == 1 ) {
return PE_OK;
}
else {
$self->logger->error(
"Webauthn validation did not return success for $user");
return PE_ERROR;
}
}
1;