support skipping of 2FA

This commit is contained in:
Roland Gruber 2019-12-09 21:35:37 +01:00
parent 9086f5847e
commit 9208cb2349
5 changed files with 44 additions and 4 deletions

View File

@ -11,6 +11,7 @@ use \htmlStatusMessage;
use \htmlDiv;
use \LAMException;
use Webauthn\PublicKeyCredentialCreationOptions;
use function LAM\LOGIN\WEBAUTHN\hasTokensRegistered;
use function LAM\LOGIN\WEBAUTHN\storeNewRegistration;
/*
@ -547,6 +548,15 @@ class WebauthnProvider extends BaseProvider {
$row->add($loginButton, 12);
$errorMessage = new htmlStatusMessage('ERROR', '', _('This service requires a browser with "WebAuthn" support.'));
$row->add(new htmlDiv(null, $errorMessage, array('hidden webauthn-error')), 12);
if ($this->config->twoFactorAuthenticationOptional === true) {
include_once __DIR__ . '/webauthn.inc';
$hasTokens = hasTokensRegistered($userDn);
if (!$hasTokens) {
$skipButton = new htmlButton('skip_webauthn', _('Skip'));
$skipButton->setCSSClasses(array('fullwidth'));
$row->add($skipButton, 12);
}
}
$row->add(new htmlJavaScript('window.lam.webauthn.start(\'' . $pathPrefix . '\');'), 0);
}
@ -556,8 +566,12 @@ class WebauthnProvider extends BaseProvider {
*/
public function verify2ndFactor($user, $password, $serial, $twoFactorInput) {
logNewMessage(LOG_DEBUG, 'WebauthnProvider: Checking 2nd factor for ' . $user);
$response = base64_decode($_POST['sig_response']);
include_once __DIR__ . '/webauthn.inc';
logNewMessage(LOG_ERR, $user);
if ($this->config->twoFactorAuthenticationOptional && !hasTokensRegistered($user) && ($_POST['sig_response'] === 'skip')) {
return true;
}
$response = base64_decode($_POST['sig_response']);
$registrationObject = PublicKeyCredentialCreationOptions::createFromString($_SESSION['webauthn_registration']);
if (storeNewRegistration($registrationObject, $response)) {
return true;
@ -634,6 +648,7 @@ class TwoFactorProviderService {
$tfConfig->isSelfService = true;
$tfConfig->twoFactorAuthentication = $profile->twoFactorAuthentication;
$tfConfig->twoFactorAuthenticationInsecure = $profile->twoFactorAuthenticationInsecure;
$tfConfig->twoFactorAuthenticationOptional = $profile->twoFactorAuthenticationOptional;
if ($tfConfig->twoFactorAuthentication == TwoFactorProviderService::TWO_FACTOR_YUBICO) {
$tfConfig->twoFactorAuthenticationURL = explode("\r\n", $profile->twoFactorAuthenticationURL);
}
@ -673,6 +688,7 @@ class TwoFactorProviderService {
$tfConfig->isSelfService = false;
$tfConfig->twoFactorAuthentication = $conf->getTwoFactorAuthentication();
$tfConfig->twoFactorAuthenticationInsecure = $conf->getTwoFactorAuthenticationInsecure();
$tfConfig->twoFactorAuthenticationOptional = $conf->getTwoFactorAuthenticationOptional();
if ($tfConfig->twoFactorAuthentication == TwoFactorProviderService::TWO_FACTOR_YUBICO) {
$tfConfig->twoFactorAuthenticationURL = explode("\r\n", $conf->getTwoFactorAuthenticationURL());
}
@ -741,4 +757,9 @@ class TwoFactorConfiguration {
*/
public $twoFactorAuthenticationSerialAttributeName = null;
/**
* @var bool 2FA is optional
*/
public $twoFactorAuthenticationOptional = false;
}

View File

@ -2480,7 +2480,7 @@ class LAMConfig {
* @return bool 2nd factor is optional
*/
public function getTwoFactorAuthenticationOptional() {
return $this->twoFactorAuthenticationOptional;
return boolval($this->twoFactorAuthenticationOptional);
}
/**

View File

@ -274,6 +274,19 @@ function getExtensionOutputChecker() {
return new ExtensionOutputCheckerHandler();
}
/**
* Returns if there are any tokens registered for the given DN.
*
* @param string $dn user DN
* @return bool at least one token is registered
*/
function hasTokensRegistered($dn) {
$repository = new PublicKeyCredentialSourceRepositorySQLite();
$userEntity = getUserEntity($dn);
$tokens = $repository->findAllForUserEntity($userEntity);
return !empty($tokens);
}
/**
* Stores the public key credentials in the SQLite database.
*

View File

@ -479,7 +479,7 @@ if (extension_loaded('curl')) {
TwoFactorProviderService::TWO_FACTOR_YUBICO => array('twoFactorURL', 'twoFactorAttribute', 'twoFactorDomain'),
TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURLs', 'twoFactorOptional', 'twoFactorInsecure', 'twoFactorLabel', 'twoFactorDomain'),
TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorURL', 'twoFactorURLs', 'twoFactorInsecure', 'twoFactorLabel',
'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'),
'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'),
));
$twoFactorSelect->setTableRowsToShow(array(
TwoFactorProviderService::TWO_FACTOR_PRIVACYIDEA => array('twoFactorURL', 'twoFactorInsecure', 'twoFactorLabel',
@ -488,7 +488,7 @@ if (extension_loaded('curl')) {
'twoFactorOptional', 'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey'),
TwoFactorProviderService::TWO_FACTOR_DUO => array('twoFactorURL', 'twoFactorLabel',
'twoFactorCaption', 'twoFactorClientId', 'twoFactorSecretKey', 'twoFactorAttribute'),
TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorDomain')
TwoFactorProviderService::TWO_FACTOR_WEBAUTHN => array('twoFactorDomain', 'twoFactorOptional')
));
$row->add($twoFactorSelect, 12);
$twoFactorAttribute = new htmlResponsiveInputField(_("User name attribute"), 'twoFactorAttribute', $conf->getTwoFactorAuthenticationAttribute(), '528');

View File

@ -1377,6 +1377,12 @@ window.lam.webauthn.start = function(prefix) {
* @param prefix path prefix for Ajax endpoint
*/
window.lam.webauthn.run = function(prefix) {
jQuery('#btn_skip_webauthn').click(function () {
let form = jQuery("#2faform");
form.append('<input type="hidden" name="sig_response" value="skip"/>');
form.submit();
return;
});
var token = jQuery('#sec_token').val();
// check for webauthn support
if (!navigator.credentials || (typeof(PublicKeyCredential) === "undefined")) {