diff --git a/views/bootstrap/class.Login.php b/views/bootstrap/class.Login.php index 68aecf303..597007c64 100644 --- a/views/bootstrap/class.Login.php +++ b/views/bootstrap/class.Login.php @@ -97,6 +97,115 @@ $(document).ready( function() { }, }); }); +function webauthnAuthenticate(key, cb){ + var pk = JSON.parse(key); + var originalChallenge = pk.challenge; + pk.challenge = new Uint8Array(pk.challenge); + pk.allowCredentials.forEach(function(k, idx){ + pk.allowCredentials[idx].id = new Uint8Array(k.id); + }); + /* ask the browser to prompt the user */ + navigator.credentials.get({publicKey: pk}) + .then(function(aAssertion) { + // console.log("Credentials.Get response: ", aAssertion); + var ida = []; + (new Uint8Array(aAssertion.rawId)).forEach(function(v){ ida.push(v); }); + var cd = JSON.parse(String.fromCharCode.apply(null, + new Uint8Array(aAssertion.response.clientDataJSON))); + var cda = []; + (new Uint8Array(aAssertion.response.clientDataJSON)).forEach(function(v){ cda.push(v); }); + var ad = []; + (new Uint8Array(aAssertion.response.authenticatorData)).forEach(function(v){ ad.push(v); }); + var sig = []; + (new Uint8Array(aAssertion.response.signature)).forEach(function(v){ sig.push(v); }); + var info = { + type: aAssertion.type, + originalChallenge: originalChallenge, + rawId: ida, + response: { + authenticatorData: ad, + clientData: cd, + clientDataJSONarray: cda, + signature: sig + } + }; + cb(true, JSON.stringify(info)); + }) + .catch(function (aErr) { + if (("name" in aErr) && (aErr.name == "AbortError" || aErr.name == "NS_ERROR_ABORT" || + aErr.name == "NotAllowedError")) { + cb(false, 'abort'); + } else { + cb(false, aErr.toString()); + } + }); +} + + $(function(){ + $('#webauthnlogin').click(function(ev){ + var self = $(this); + ev.preventDefault(); + + $.ajax({url: '../op/op.Login.php', + method: 'POST', + data: {action: 'preparelogin', login: $('#loginusername').val()}, + dataType: 'json', + success: function(j){ + /* activate the key and get the response */ + webauthnAuthenticate(j.challenge, function(success, info){ + if (success) { + $.ajax({url: '../op/op.Login.php', + method: 'POST', + data: {action: 'login', login: $('#loginusername').val(), logininfo: info}, + dataType: 'json', + success: function(j){ + noty({ + text: 'login completed successfully', + type: 'success', + dismissQueue: true, + layout: 'topRight', + theme: 'defaultTheme', + _timeout: 1500, + }); + window.location = j; + }, + error: function(xhr, status, error){ + noty({ + text: 'login failed: '+error+": "+xhr.responseText, + type: 'error', + dismissQueue: true, + layout: 'topRight', + theme: 'defaultTheme', + _timeout: 1500, + }); + } + }); + } else { + noty({ + text: info, + type: 'error', + dismissQueue: true, + layout: 'topRight', + theme: 'defaultTheme', + _timeout: 1500, + }); + } + }); + }, + + error: function(xhr, status, error){ + noty({ + text: "couldn't initiate login: "+error+": "+xhr.responseText, + type: 'error', + dismissQueue: true, + layout: 'topRight', + theme: 'defaultTheme', + _timeout: 1500, + }); + } + }); + }); + }); errorMsg(htmlspecialchars($msg)); ?> contentContainerStart(); ?> -
+ "; @@ -131,7 +240,7 @@ $(document).ready( function() { array( 'element'=>'input', 'type'=>'text', - 'id'=>'login', + 'id'=>'loginusername', 'name'=>'login', 'placeholder'=>'login', 'autocomplete'=>'off', @@ -187,10 +296,15 @@ $(document).ready( function() { $html ); } + echo '
'; $this->formSubmit(getMLText('submit_login')); + echo '
'; ?>
'; + $this->formSubmit(getMLText('submit_webauthn_login'), 'webauthnlogin'); + echo ''; $this->contentContainerEnd(); $tmpfoot = array(); if ($enableguestlogin)