Compare commits

..

No commits in common. "master" and "0.1.0" have entirely different histories.

7 changed files with 88 additions and 124 deletions

View File

@ -26,7 +26,7 @@ You'll then need to configure the following:
DB_CONNECTION=sqlite # Your databsae configuration (must the same as SQRL_DATABASE below)
...
SQRL_DATABASE=sqlite # Points to the SQRL database connection
SQRL_URL_LOGIN=https://site/sqrl/login # URL to the page after successful authentication
SQRL_URL_LOGIN=https://site/sqrl/login # URL to your login page (not used with LUMEN
SQRL_KEY_DOMAIN=site # URL to yours SQRL Server without http:// and https:// SQRL_API_ROUTE=/index.php/api/sqrl # Route to SQRL Server API
SQRL_NONCE_MAX_AGE_MINUTES=5 # Max age in minutes of the valid nonce
SQRL_NONCE_SALT=RANDOM # Generate a random salt value to calculate the nonce

View File

@ -51,7 +51,7 @@ class SQRL
*
* @return array
*/
public static function authNonce(int $size=100): array
public static function authNonce(): array
{
$url = config('sqrl.url');
@ -67,7 +67,7 @@ class SQRL
'check_state_on'=>$route,
'sqrl_url'=>$sqrl_url,
'sqrl_url_encoded'=>self::base64_encode_url(sprintf('%s&can=%s',$sqrl_url,$o->can)),
'qrcode'=>SQRL::qrCode($sqrl_url,$size),
'qrcode'=>SQRL::qrCode($sqrl_url,100),
];
}

View File

@ -52,7 +52,6 @@ class Nonce
$o->can = SQRL::base64_encode_url($can);
$o->save();
Log::debug(sprintf('NUT [%s] created for (%s)',$o->nonce,$o->ip));
return $o;
}
@ -71,6 +70,7 @@ class Nonce
return NULL;
}
Log::debug(sprintf('A: %s, B: %s',$o->created_at->diffInMinutes(Carbon::now()),config('sqrl.nonce_age')));
// Delete the old nonce
if ($o->created_at->diffInMinutes(Carbon::now()) > config('sqrl.nonce_age')) {
$o->delete();

View File

@ -18,8 +18,8 @@ use Leenooks\SQRL\SQRL as SQRLAuth;
* Class SQRLController
* @package Leenooks\SQRL
* @todo CHECK THAT WE ARE RECEIVING BACK WHAT WE GIVE TO THE CLIENT
* + QUERY: = sqrl url, eg: sqrl://domain/api/sqrl?nut=84cc3ef3b58b01dbe22931d1ceabdd6be2c27a481516755757c53b0162287bb8
* + IDENT: = RESPONSE TO QUERY
* * QUERY: = sqrl url, eg: sqrl://domain/api/sqrl?nut=84cc3ef3b58b01dbe22931d1ceabdd6be2c27a481516755757c53b0162287bb8
* * IDENT: = RESPONSE TO QUERY
* @todo JOB TO DELETE OLD NONCES
*/
@ -35,29 +35,7 @@ class SQRLController extends Controller
{
// Validate the nonce if it has been given.
if ($request->get('nut')) {
Log::debug(sprintf('Got a NUT [%s]',$request->get('nut')));
//Get the user by the original nonce
$o = SQRLAuth\Nonce::check($request->get('nut'),'orig_nonce');
Log::debug(sprintf('User [%s]',serialize($o ? $o->getAttributes() : NULL)));
if ($o && $o->verified) {
if ($o->pubkey && ! $o->pubkey->disabled)
// For JSON we just need the SQRL login
return $request->expectsJson() ? $o->pubkey->public_key : $o->pubkey;
else
return response()->json([
'isReady'=>FALSE,
'msg'=>'Not Verified'
],404);
} else {
return response()->json([
'isReady'=>FALSE,
'msg'=>'Not Ready'
],404);
}
}
// If this laravel, check if the user has been logged in
@ -101,13 +79,13 @@ class SQRLController extends Controller
$decode_request = SQRL::decodeData($validatedData);
$sqrl_nonce = SQRLAuth\Nonce::checkNonceValid($validatedData['nut']);
$tif = in_array('noiptest',Arr::get($decode_request,'client.opt',[])) ? 0 : SQRL::tifcode('IP_MATCH');
$tif = in_array('noiptest',Arr::get($decode_request,'client.opt')) ? 0 : SQRL::tifcode('IP_MATCH');
if (! $sqrl_nonce) {
Log::error('API:Nonce not valid',['n'=>$validatedData['nut'],'tif'=>SQRL::tifcode('CLIENT_FAILURE')]);
$response = SQRLAuth\Response::problem($validatedData['nut'],SQRL::tifcode('CLIENT_FAILURE'));
} elseif (($sqrl_nonce->ip !== $request->ip()) && (! in_array('noiptest',Arr::get($decode_request,'client.opt',[])))) {
} elseif (($sqrl_nonce->ip !== $request->ip()) && (! in_array('noiptest',Arr::get($decode_request,'client.opt')))) {
Log::error('API::IP Doesnt Match',['n'=>$validatedData['nut'],'tif'=>SQRL::tifcode('COMMAND_FAILED')]);
$response = SQRLAuth\Response::problem($sqrl_nonce->nonce,SQRL::tifcode('COMMAND_FAILED'));
@ -122,12 +100,11 @@ class SQRLController extends Controller
} else {
foreach (['ver','cmd'] as $y)
Log::debug(sprintf('API-client-%s [%s]',str_pad($y,5,' '),Arr::get($decode_request,'client.'.$y)));
Log::debug(sprintf('API-client-opt [%s]',join('|',Arr::get($decode_request,'client.opt'))));
Log::debug(sprintf('API-client-idk [%s]',base64_encode(Arr::get($decode_request,'client.idk'))));
Log::debug(sprintf('API-server [%s]',serialize(Arr::get($decode_request,'server'))));
Log::debug(sprintf('API-client-opt [%s]',join('|',Arr::get($decode_request,'client.opt',[]))));
Log::debug(sprintf('API-client-idk [%s]',base64_encode(Arr::get($decode_request,'client.idk'))));
Log::debug(sprintf('API-server [%s]',serialize(Arr::get($decode_request,'server'))));
Log::debug(sprintf('API-type [%s]',$sqrl_nonce->type));
Log::debug(sprintf('API-type [%s]',$sqrl_nonce->type));
switch ($sqrl_nonce->type) {
case 'auth':
@ -161,8 +138,6 @@ class SQRLController extends Controller
// If the nonce is old or doesnt exist.
if (! $o) {
Log::debug(sprintf('isReady: Invalid Nonce [%s]',$request->get('nut')));
return response()->json([
'isReady'=>FALSE,
'msg'=>'Invalid Nonce, or Nonce expired'
@ -171,8 +146,6 @@ class SQRLController extends Controller
// Validate the IP matches - since the request would come from the same device client
if ($o->ip !== $request->ip()) {
Log::debug(sprintf('isReady: IP Mismatch [%s] != [%s]',$o->ip,$request->ip));
return response()->json([
'isReady'=>FALSE,
'msg' => 'IP Mismatch',
@ -181,17 +154,13 @@ class SQRLController extends Controller
// Has the nonce be validated
if ($o->verified != 1) {
Log::debug(sprintf('isReady: Not Verified [%s]',$o->verified));
return response()->json([
'isReady'=>FALSE,
'msg'=>'Not Ready'
],404);
],200);
}
if ($o->pubkey && $o->pubkey->disabled) {
Log::debug(sprintf('isReady: SQRL Disabled [%s]',$o->pubkey));
return response()->json([
'isReady'=>FALSE,
'msg'=>'SQRL disabled for user'
@ -200,8 +169,6 @@ class SQRLController extends Controller
switch ($o->type) {
case 'auth':
Log::debug(sprintf('isReady: Authenticated [%s]',$o->pubkey));
return response()->json([
'isReady'=>TRUE,
'msg'=>'SQRL authenticated',
@ -213,12 +180,10 @@ class SQRLController extends Controller
}
} else {
Log::debug(sprintf('isReady: Not Nut?',$request->get('nut')));
return response()->json([
'isReady'=>FALSE,
'msg'=>'Not Found!'
],200);
],404);
}
}
}

View File

@ -8,7 +8,7 @@ if (app() instanceof \Illuminate\Foundation\Application) {
Route::get('/login','SQRLController@auth')->name('login');
// Perform login
Route::post('/login','SQRLController@login');
Route::post('/login','SQRLController@@login');
});
Route::group(['prefix'=>'api','namespace'=>'Leenooks\SQRL'], function() {

View File

@ -78,9 +78,80 @@
</div>
</body>
@include('sqrl::login_js')
<script>
var syncQuery = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('MSXML2.XMLHTTP.3.0');
var encodedSqrlUrl = false, sqrlScheme = true;
var gifProbe = new Image(); // create an instance of a memory-based probe image
var localhostRoot = 'http://localhost:25519/'; // the SQRL client listener
const poll = 500;
// define our load-success function
gifProbe.onload = function() {
sqrlScheme = false; // prevent retriggering of the SQRL QR code.
document.location.href = localhostRoot+encodedSqrlUrl;
};
// define our load-failure function
gifProbe.onerror = function() {
setTimeout(function() {
gifProbe.src = localhostRoot+Date.now()+'.gif';
},250);
};
// Poll to see if authentication has proceeded
function pollForNextPage() {
if (document.hidden) { // before probing for any page change, we check to
setTimeout(pollForNextPage,poll); // see whether the page is visible. If the user is
return; // not viewing the page, check again in 5 seconds.
}
syncQuery.open('GET','{{ $check_state_on }}'); // the page is visible, so let's check for any update
syncQuery.onreadystatechange = function() {
if (syncQuery.readyState === 4 ) {
if (syncQuery.status === 200 ) {
var response = JSON.parse(syncQuery.response);
if (response.isReady) {
document.location.href = response.nextPage;
} else {
if (response.msg === "Invalid Nonce, or Nonce expired"
|| response.msg === "IP Mismatch"
|| response.msg === "SQRL disabled for user")
{
var div = document.getElementById('error_message');
div.innerHTML = "<string>"+response.msg+"</strong><br><small>QR Code needs to be refresh - reload the page.<small>";
div.removeAttribute("hidden");
} else {
setTimeout(pollForNextPage,poll);
}
}
} else {
setTimeout(pollForNextPage,poll);
}
}
};
// Send our request to check authenticated status
syncQuery.send();
}
// if we have an encoded URL to jump to, initiate our GIF probing before jumping
function sqrlLinkClick(e) {
encodedSqrlUrl = e.getAttribute('encoded-sqrl-url');
if (encodedSqrlUrl) {
gifProbe.onerror();
}
}
pollForNextPage();
</script>
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.0/js/bootstrap.min.js" integrity="sha384-3qaqj0lc6sV/qpzrc1N5DC6i1VRn/HyX4qdPaiEFbn54VjQBEU341pvjz7Dv3n6P" crossorigin="anonymous"></script>
</html>
</html>

View File

@ -1,72 +0,0 @@
<script>
var syncQuery = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('MSXML2.XMLHTTP.3.0');
var encodedSqrlUrl = false, sqrlScheme = true;
var gifProbe = new Image(); // create an instance of a memory-based probe image
var localhostRoot = 'http://localhost:25519/'; // the SQRL client listener
const poll = 500;
// define our load-success function
gifProbe.onload = function() {
sqrlScheme = false; // prevent retriggering of the SQRL QR code.
document.location.href = localhostRoot+encodedSqrlUrl;
};
// define our load-failure function
gifProbe.onerror = function() {
setTimeout(function() {
gifProbe.src = localhostRoot+Date.now()+'.gif';
},250);
};
// Poll to see if authentication has proceeded
function pollForNextPage() {
if (document.hidden) { // before probing for any page change, we check to
setTimeout(pollForNextPage,poll); // see whether the page is visible. If the user is
return; // not viewing the page, check again in 5 seconds.
}
syncQuery.open('GET','{{ $check_state_on }}'); // the page is visible, so let's check for any update
syncQuery.onreadystatechange = function() {
if (syncQuery.readyState === 4 ) {
if (syncQuery.status === 200 ) {
var response = JSON.parse(syncQuery.response);
if (response.isReady) {
document.location.href = response.nextPage;
} else {
if (response.msg === "Invalid Nonce, or Nonce expired"
|| response.msg === "IP Mismatch"
|| response.msg === "SQRL disabled for user")
{
var div = document.getElementById('error_message');
div.innerHTML = "<string>"+response.msg+"</strong><br><small>QR Code needs to be refresh - reload the page.<small>";
div.removeAttribute("hidden");
} else {
setTimeout(pollForNextPage,poll);
}
}
} else {
setTimeout(pollForNextPage,poll);
}
}
};
// Send our request to check authenticated status
syncQuery.send();
}
// if we have an encoded URL to jump to, initiate our GIF probing before jumping
function sqrlLinkClick(e) {
encodedSqrlUrl = e.getAttribute('encoded-sqrl-url');
if (encodedSqrlUrl) {
gifProbe.onerror();
}
}
pollForNextPage();
</script>