# 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;