Skip to content

Instantly share code, notes, and snippets.

@sahilkashyap64
Last active December 23, 2022 15:00
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sahilkashyap64/b2d2db0999b3d540d2578da9a0f728c0 to your computer and use it in GitHub Desktop.
Save sahilkashyap64/b2d2db0999b3d540d2578da9a0f728c0 to your computer and use it in GitHub Desktop.
Laravel QR login using polling like whatsapp with memecache
<?php
//api doc : https://documenter.getpostman.com/view/6553325/UUy39SJv
//article: https://dev.to/sahilkashyap64/qr-login-in-php-2pgf
Route::post('/login/create/qrcode', 'Admin\QRLoginTwoController@CreateQrcodeAction');//creates QR and save it as png
Route::post('/login/mobile/scan/qrcode', 'Admin\QRLoginTwoController@mobileScanQrcodeAction');//gets scaned by phone with key passed in header
Route::post('/login/qrcodedoLogin', 'Admin\QRLoginTwoController@qrcodeDoLoginAction'); //this url is used when qr code is scanned successfully
Route::post('/login/scan/qrcode', 'Admin\QRLoginTwoController@isScanQrcodeAction'); //Check whether the code has been scanned
?>
@extends('frontend/partials/master')
@section('top-stylesheets')
@include('frontend.partials.sidenavRegisterAndLoginCSS')
@endsection
@section('stylesheets')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="{{ asset('frontend/qr/jquery.qrcode-0.11.0.min.js') }}" ></script>
<style>
.alert.alert-position {
position: absolute;
width: 100%;
z-index: 1040;
}
</style>
@endsection
@section('stylesheets-bottom')
@include('frontend.partials.sidenavRegisterAndLoginJS')
<script type="text/javascript">
// Check whether the login has been confirmed
function is_loginfun(){
var key = $('#key').val();
console.log("is_login called");
$.ajax({
type: "POST" ,
dataType: "json" ,
url: "web/login/entry/login" ,
data:{
key:key ,
"_token":"{{ csrf_token() }}"
},
headers: {'x-csrf-token': '{{ csrf_token() }}'},
success:function(data) {
if (data.status==1 ){
var uid = data.jwt;
var user = data.user;
console.log("user",user);
// var sign = data.sign;
// Cancel timed tasks and clear cookies
clearInterval($('#is_login' ).val ());
$( '#is_login').val('' );
// Hide the successful scanning code
// $('.barcode-container.scanned .status.scanned, .barcode-container.scanned .mask' ).hide();
//
// Confirmed pop-up $('.confirmed,.mask' ).show();
// Confirm login at a time of 1 second
console.log("login successfull",uid);
$('.qrcode-img').attr('src','');
alert("login successfull",uid);
$('#thelogindata').text(uid)
window.location.href = '/';
} else if (data.status==2 ){
// Cancel timed tasks and clear cookies
clearInterval($('#is_login').val());
$('#is_login').val('' );
alert(data.msg);
}
}
});
}
// Check whether the code has been scanned
function is_sacn_qrcode(){
var key = $('#key').val();
console.log("is_sacn_qrcode called");
//return;
$.ajax({
type: "POST" ,
dataType: "json" ,
url: "api/login/scan/qrcode" ,
data:{
key:key
},
success: function(data){
if(data.status==1){
// Scan code successfully
// $('.barcode-container.scanned .status.scanned, .barcode-container.scanned .mask' ).show() ;
// Cancel timing tasks and clear cookies
clearInterval($('#timing').val());
$( '#timing').val('');
// // close the pop-up window at a timing of 2 seconds
// setTimeout(function(){
// $('.qrcode').hide( );
// }, 2000);
// Check whether the login has been confirmed
var is_login = setInterval(function() {
is_loginfun();
},3000);
$('#is_login').val(is_login);
// $.cookie('is_login', is_login);
} else if(data.status==2 ){
$('.timeout,.mask').show();
// Cancel timing tasks and clear cookies
clearInterval($('#timing').val());
$('#timing').val('');
}
}
});
}
$(document).ready(function() {
$('.scanqrcode').click(function(){
// Request QR code
$.ajax({
type: "POST" ,
dataType: "json" ,
url: "api/login/create/qrcode" ,
headers: {'x-csrf-token': '{{ csrf_token() }}'},
success:function(data){
if(data.status == 1) {
var qrcodeimg=data.msg;
// of Put in hidden The Key Domain
$('#key').val(data.key);
console.log("key",data.key);
// Replace the QR code
$('.qrcode-img').attr('src',qrcodeimg);
//Trigger timed task, check whether it has been Scan code
var inter = setInterval(function() {
is_sacn_qrcode();
},3000);
$('#timing').val(inter);
}
}
});
});
});
$("#my_form").submit(function(event){
event.preventDefault(); //prevent default action
var zipnumber = $("[name='zip']");
// alert(zip); exit;
$.ajax({
url:"{{ route('ajax-check') }}",
type:"GET",
data:{zip: zipnumber.val()},
dataType: 'json',
success: function (data) {
$('#card-button').prop('disabled', false);
$('#ziphelp').html('<p style="color:green">'+data.message+' <a href="javascript:void(0)" onclick="openNavReg();"style="padding: 0;">Start Your Trial Now!</a></p>');
},
error:function(xhr, status, error){
if(xhr.status==404) {
// alert('status:' + xhr.status + ', status text: ' + xhr.responseText);
$('#card-button').prop('disabled', true);
$('#ziphelp').html('<p style="color:red">'+xhr.responseJSON.message+' Our apologies, your location is outside of the streamly service area. Please call 1111111111 for more information.</p>');
}
// coupon was invalid, clear input so it doesn't get posted if user doesn't input a new valid coupon
}
});
});
</script>
<style>
nav#mainNav {
background-color: #004E92;
}
body{
background: #F9F9F9 0% 0% no-repeat padding-box;
}
.page-section {
padding: 8rem 0;
}
ul.setting-card {
margin: 0 auto;
list-style: none;
box-shadow: 0px 3px 6px #00000029;
padding: 0;
}
ul.setting-card li:first-child {
font-weight: bold;
background: #004E92 0% 0% no-repeat padding-box;
padding: 12px 34px;
letter-spacing: 0.24px;
color: #FFFFFF;
border-top-right-radius: 6px;
border-top-left-radius: 6px;
}
ul.setting-card {
margin: 0 auto;
color: #5E5E5E;
}
.col-centered {
margin: 0 auto;
position: relative;
}
ul.setting-card li:not(:first-child) {
padding: 12px 34px;
letter-spacing: 0.24px;
}
.light-brown{
color: #989898;
}
.account-save-btn {
background: #C4C4C4 0% 0% no-repeat padding-box;
border-radius: 6px;
}
.account-cancel-btn {
border: 1px solid #707070;
border-radius: 6px;
}
.cancel-subscription {
display: block;
text-align: right;
color: #CF0000;
}
i.fa.fa-trash {
cursor: pointer;
}
i.fa.fa-edit {
cursor: pointer;
}
.modal-dialog {
min-height: calc(100vh - 60px);
display: flex;
flex-direction: column;
justify-content: center;
overflow: auto;
}
.modal-body {
position: relative;
flex: 1 1 auto;
padding: 3rem 5rem;
}
.modal-body p{
letter-spacing: 0.24px;
color: #5E5E5E;
}
</style>
@endsection
@section('content')
@if(session()->has('message'))
<div class="alert alert-success alert-dismissible ">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ session()->get('message') }}
</div>
@endif
@include('frontend.partials.sidenavRegisterAndLogin')
<section class="page-section">
<div class="container">
<h2 class="section-heading text-center">QR code</h2>
<div class="row setting-cards">
<div class="col-centered col-md-8">
<ul class="setting-card">
<li class="text-center"><button class="scanqrcode">Login with QR</button></li>
<li class="text-center">
<p id="thelogindata"></p>
<img class="qrcode-img" src="" />
<input type="hidden" id="key">
<input type="hidden" id="timing" value="timing">
<input type="hidden" id="is_login" value="is_login "></li>
</ul>
</div>
</div>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
</div>
</section><section class="page-section">
</section>
@endsection
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Cache;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
use Memcache;
use Illuminate\Support\Facades\Auth;
class QRLoginTwoController extends Controller
{
// QR code should be only avilable for non logged in user
public function qr()
{
if (Auth::check())
{
return "you are already logged in, open it in incognito mode";
}
else
{
return view('frontend.drm.qr');
}
}
//QR scanner only avilable for logged in user
public function qrscanner()
{
if (Auth::check())
{
$login = true;
return view('frontend.drm.qrscanner', compact('login'));
}
return redirect()->route('home');
}
public function CreateQrcodeAction()
{
$url = 'http://' . $_SERVER['HTTP_HOST']; // Get the current url
// dd($url);
$http = $url . '/api/login/mobile/scan/qrcode'; // Verify the url method of scanning code
$key = Str::random(30); //$this->getRandom(30); // The key value stored in memcache, a random 32-bit string
$random = mt_rand(1000000000, 9999999999); //random integer
$_SESSION['qrcode_name'] = $key; // Save the key as the name of the picture in the session
$forhash = substr($random, 0, 2);
$sgin_data = HashUserID($forhash); // The basic algorithm for generating the sign string
$sgin = strrev(substr($key, 0, 2)) . $sgin_data; // Intercept the first two digits and reverse them
$value = $http . '?key=' . $key . '&type=1'; // Two-dimensional Code content
$pngImage = QrCode::format('png')
// ->merge(public_path('frontend/img/streamly-logo.png'), 0.3, true)
->size(300)
->errorCorrection('H')
->generate($value, public_path('assets/img/qrcodeimg/' . $key . '.png'));
$return = array(
'status' => 0,
'msg' => ''
);
$qr = public_path('assets/img/qrcodeimg/' . $key . '.png');
if (!file_exists($qr))
{
$return = array(
'status' => 0,
'msg' => ''
);
return response()->json($return, 404);
// return "no found qr img";
}
$qr = asset('assets/img/qrcodeimg/' . $key . '.png');
$mem = new \Memcache();
$mem->connect('127.0.0.1', 11211);
$res = json_encode(array(
'sign' => $sgin,
'type' => 0
));
// store in memcache, expiration time is three minutes
$mem->set($key, $res, 0, 180); // 180
$return = array(
'status' => 1,
'msg' => $qr,
'key' => $key
);
return response()->json($return, 200);
}
// Mobile device scan code
public function mobileScanQrcodeAction(Request $request)
{
$key = $_GET['key'];
$url = 'http://' . $_SERVER['HTTP_HOST'];
$agent = $_SERVER["HTTP_USER_AGENT"];
$headerpasscode = $request->header('userpasscode');
$http = $url . '/api/login/qrcodedoLogin'; // Return to confirm the login link
$mem = new \Memcache();
$mem->connect('127.0.0.1', 11211);
$data = json_decode($mem->get($key) , true);
if (empty($data))
{
$return = array(
'status' => 2,
'msg' => 'expired'
);
return response()->json($return, 200);
}
$data['type'] = 1; // Increase the type value to determine whether the code has been scanned
$res = json_encode($data);
$mem->set($key, $res, 0, 180);
$http = $http . '?key=' . $key . '&type=scan&login=' . $headerpasscode . '&sign=' . $data['sign'];
$return = array(
'status' => 1,
'msg' => $http
);
return response()->json($return, 200);
}
/* *
* Log in after the client scans the code
* $sign passes the identification when the client scans the code, and compares it with the memcache
* $key is the key passed in the QR code on the web page
* $uid The user id sent by scanning the code after the client logs in
* @return void
*/
public function qrcodeDoLoginAction(Request $request)
{
$login = $_GET['login']; //jwt or passcode
$key = $_GET['key'];
$sign = $_GET['sign'];
$mem = new \Memcache();
$mem->connect('127.0.0.1', 11211);
$data = json_decode($mem->get($key) , true); // Remove the value of memcache
if (empty($data))
{
$return = array(
'status' => 2,
'msg' => 'expired'
);
return response()->json($return, 200);
}
else
{
if (!isset($data['sign']))
{
$return = array(
'status' => 0,
'msg' => 'Sign notset'
);
}
if ($data['sign'] != $sign)
{ // Verify delivery Sign
$return = array(
'status' => 0,
'msg' => 'Verification Error'
);
// return $this ->createJsonResponse( $return );
return response()->json($return, 403);
}
else
{
if ($login)
{ // Mobile phone scan code webpage login, save the user name in memcache
$data['jwt'] = $login;
$res = json_encode($data);
$mem->set($key, $res, 0, 180);
$return = array(
'status' => 1,
'msg' => 'Login successful'
);
// return $this ->createJsonResponse( $return );
return response()->json($return, 200);
}
else
{
$return = array(
'status' => 0,
'msg' => 'Please pass the correct user information'
);
// return $this ->createJsonResponse( $return );
return response()->json($return, 401);
}
}
}
}
/* *
* Check whether the code has been scanned, this checked by polling,
* it looks for the key, the filename,
*/
public function isScanQrcodeAction(Request $request)
{
$key = $request['key'];
$mem = new \Memcache();
$mem->connect('127.0.0.1', 11211);
$data = json_decode($mem->get($key) , true);
if (empty($data))
{
$return = array(
'status' => 2,
'msg' => 'expired'
);
}
else
{
if ($data['type'])
{
$return = array(
'status' => 1,
'msg' => 'success'
);
}
else
{
$return = array(
'status' => 0,
'msg' => ''
);
}
}
return response()->json($return, 200);
// return $this->createJsonResponse( $return );
}
public function loginEntry(Request $request)
{
$key=$request['key'];
if (empty($key)){
$return = array ('status'=>2,'msg'=>'key not provided' );
return response()->json($return, 200);
}
$mem = new \Memcache();
$mem->connect('127.0.0.1',11211);
$data = json_decode($mem->get($key),true);
// $passcode=$data['login'];
if (empty($data)){
$return = array ('status'=>2,'msg'=>'expired' );
return response()->json($return, 200);
} else {
if (isset($data['jwt'])){
$userid=UnHashUserID($data['jwt']);
$user = Auth::loginUsingId($userid, true);
$return = array ('status'=>1,'msg'=>'success','jwt'=>$data['jwt'],'user'=>$user );
return response()->json($return, 200);
} else {
$return = array ('status'=>0,'msg'=>'','data'=>$data );
return response()->json($return, 400);
}
}
// Use passcode to login and return the object in reposne or jWT
// extract memcache and the login var jwt/passcode
//use passcode to login and return the jwt
}
}
?>
@extends('frontend/partials/master')
@section('top-stylesheets')
@include('frontend.partials.sidenavRegisterAndLoginCSS')
@endsection
@section('stylesheets')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<style>
.alert.alert-position {
position: absolute;
width: 100%;
z-index: 1040;
}
</style>
@endsection
@section('stylesheets-bottom')
@include('frontend.partials.sidenavRegisterAndLoginJS')
<script type="text/javascript">
</script>
<style>
nav#mainNav {
background-color: #004E92;
}
body{
background: #F9F9F9 0% 0% no-repeat padding-box;
}
.page-section {
padding: 8rem 0;
}
ul.setting-card {
margin: 0 auto;
list-style: none;
box-shadow: 0px 3px 6px #00000029;
padding: 0;
}
ul.setting-card li:first-child {
font-weight: bold;
background: #004E92 0% 0% no-repeat padding-box;
padding: 12px 34px;
letter-spacing: 0.24px;
color: #FFFFFF;
border-top-right-radius: 6px;
border-top-left-radius: 6px;
}
ul.setting-card {
margin: 0 auto;
color: #5E5E5E;
}
.col-centered {
margin: 0 auto;
position: relative;
}
ul.setting-card li:not(:first-child) {
padding: 12px 34px;
letter-spacing: 0.24px;
}
.light-brown{
color: #989898;
}
.account-save-btn {
background: #C4C4C4 0% 0% no-repeat padding-box;
border-radius: 6px;
}
.account-cancel-btn {
border: 1px solid #707070;
border-radius: 6px;
}
.cancel-subscription {
display: block;
text-align: right;
color: #CF0000;
}
i.fa.fa-trash {
cursor: pointer;
}
i.fa.fa-edit {
cursor: pointer;
}
.modal-dialog {
min-height: calc(100vh - 60px);
display: flex;
flex-direction: column;
justify-content: center;
overflow: auto;
}
.modal-body {
position: relative;
flex: 1 1 auto;
padding: 3rem 5rem;
}
.modal-body p{
letter-spacing: 0.24px;
color: #5E5E5E;
}
</style>
@endsection
@section('content')
@if(session()->has('message'))
<div class="alert alert-success alert-dismissible ">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ session()->get('message') }}
</div>
@endif
@include('frontend.partials.sidenavRegisterAndLogin')
<section class="page-section">
<div class="container">
<h2 class="section-heading text-center">QR code scanner</h2>
<div class="row setting-cards">
<div class="col-centered col-md-8">
<ul class="setting-card">
<li class="text-center">
<?php $hashedid= HashUserID(Auth::user()->id); ?>
<p>passcode: {{$hashedid}}</p>
<p>Name: {{Auth::user()->name}}</p>
<p>Email: {{Auth::user()->email}}</p>
</li>
<li class="text-center">
<div id="qr-reader" ></div>
<div id="qr-reader-results"></div>
<p id="login_mobile_scan_qrcode"></p>
<p id="qrcodedoLogin"></p></li>
</ul>
</div>
</div>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
</div>
</section><section class="page-section">
</section>
@endsection
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="{{ asset('frontend/qr/html5-qrcode.min.js') }}" ></script>
<script>
function qrcodedoLogin(param){
var url = param;
console.log("qrcodedoLogin called",url);
$.ajax({
type: "POST" ,
dataType: "json" ,
url: url ,
data:{
//key:key
},
success:function(data) {
if (data.status==1 ){
var qrcodeloginurl = data.msg;
//scan successfull url recieved
$('#qrcodedoLogin').text("QR Loggin successfully");
// console.log("qrcodeloginurl",qrcodeloginurl);
//qrcodedoLogin(qrcodeloginurl);
} else if (data.status==2 ){
//couldn't do login
// alert(data.msg);
$('#qrcodedoLogin').text(data.msg);
}
}
});
}
function login_mobile_scan_qrcode(param){
var url = param;
// console.log("login_mobile_scan_qrcode called",url);
$.ajax({
type: "POST" ,
dataType: "json" ,
url: url ,
headers: { 'userpasscode': '{{$hashedid}}' },
data:{
//key:key
},
success:function(data) {
if (data.status==1 ){
var qrcodeloginurl = data.msg;
//scan successfull url recieved
console.log("qrcodeloginurl",qrcodeloginurl);
$('#login_mobile_scan_qrcode').text("QR scanned successfully");
qrcodedoLogin(qrcodeloginurl);
} else if(data.status==2 ){
// error found
$('#login_mobile_scan_qrcode').text(data.msg);
}
}
});
}
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete"
|| document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
docReady(function () {
var resultContainer = document.getElementById('qr-reader-results');
var lastResult, countResults = 0;
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
lastResult = decodedText;
// Handle on success condition with the decoded message.
console.log(`Scan result ${decodedText}`, decodedResult);
login_mobile_scan_qrcode(decodedText);
}
}
var html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", { fps: 10, qrbox: 250 });
html5QrcodeScanner.render(onScanSuccess);
});
</script>
<?php
Route::get('/qrtesting2', 'Admin\QRLoginTwoController@qr'); //shows the QR cde on screen
Route::get('/qrscanner', 'Admin\QRLoginTwoController@qrscanner');// scan the qr code
Route::post('web/login/entry/login', 'Admin\QRLoginTwoController@loginEntry');//Check whether the login has been confirmed ,and return the token in response and redirect
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment