Skip to main content

Server Side

The server side needs to take care of 4 major tasks:

  1. Generate QR String
  2. Webhook to receive authorization request
  3. Change user status
  4. Verify user status

How it works

Server Diagram

The server generates a QR code that the client will present and which our user will scan with their ByteWallet app. Once the user scans this QR code successfully, then the server will receive the response from the scanned QR code at its webhook route. The response thus received holds the key to allow the user to authenticate successfully inside the app. The response contains a signed version of the QR code (signed by the user's wallet's private key). If the signed message corresponds to the public key submitted then the user is authenticated successfuly and our server must redirect the user's session to the correct client side page to which access permission has thus been granted.

info

Display Client: Kiosks and/or Web Interface displaying QR code

info

Scan Client: ByteWallet app

Session Table (Optional)

In our examples we use this table to store sessions that we generate when a user loads the login page. The webhook function is then able to check the latest session challenge looking up the past few challenges to make sure the request is recent.

info

Security feature includes challenge phrase valid for 30 seconds-2 minutes to prevent snapshot of QR

There are many different session systems available, so feel free to use your own system.

create_bf_fast_sessions.sql
CREATE TABLE `bf_fast_sessions` (
`id` INT(10) NOT NULL AUTO_INCREMENT,
`sid` VARCHAR(150) NULL DEFAULT NULL,
`v` TEXT(65535) NULL DEFAULT NULL,
`s` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `sid` (`sid`) USING BTREE
)
ENGINE=InnoDB
AUTO_INCREMENT=1;

Dependencies

The only server side dependency necessary is an implementation of Bitcoin ECDSA functions to verify a digital signature. See our examples for various libraries that accomplish this.

The followoing two libraries handle the math needed for ECDSA key pair handling:

sudo apt-get install php[8.x]-gmp

and

sudo apt-get install php[8.x]-bcmath

(depending on your apache configuration you may have to specify your php version number)

Install bitcoin signing package using composer


composer require bitcoin-php/bitcoin-ecdsa

use BitcoinPHP\BitcoinECDSA\BitcoinECDSA;

1. Generate QR Code

The QR code is the base of the authentication system. The QR code is a basic string that must contain the webhook (redirect) URL, sesion_id, challenge and timestamp. This QR Code is generated on the server side and is implemented and shown on the client side of the application - typically on a login page. For example like this:

Server Diagram

The QR Code is comprised of the following parameters:

Parameters


Request

  • session_id: string => unique token that a web server assigns a user for their session

  • challenge: string => mutual key phrase shared between server and client

  • timestamp: string => hex encoded utc-timestamp of moment qr is generated


Response

JSON

We return the qr code string for the webhook redirect URL including our current session_id as sid as well as our current challenge c and the hex encoded timestamp t.

  qr: "https://my.website.com/api/fastbyte?sid=-ioAzu3w6AJaZ_uu7SnT7HTvJ6ddtFNQ_1660924354&c=xGOxY1TYHK8X0tiDuXl9XPDS3stIZzok_1660924504&t=62ffb258"
sid: "-ioAzu3w6AJaZ_uu7SnT7HTvJ6ddtFNQ_1660924354"
status: 0


Example

A client side script in the login page calls the server side webhook URL string generating function.

actionGetqrcode.php
  /**
* QR Code generation
*
* Your login page javascript calls this QR-code-string generating API function every ~30 seconds to generate a new
* challenge for your webhook URL which will be displayed in the login page using a javascript QR code library
* converting the string returned from this function into a scannable QR code. The returned URL contains a link to
* your webhook as well as the current session ID, current challenge and a timestamp. The timestamp allows ByteWallet
* to check the request as being current. Use the session to store past challenges. In this example we limit the
* session to no more than 4 past challenges (allowing for ~2 mins between scanning and webook request).
*
* GET
* @link /api/getqrcode?sid=x
* @param string $sid your session token
*
* @return echo qr-code string.
*/
public function actionGetqrcode()
{
$status = 0;
if(Yii::$app->user->isGuest)
{
$bf_session = new Fast; //loading session table
$session_string = Yii::$app->getRequest()->getQueryParam('sid'); //if session already exists, reuse
if($session_string == 'null' || $session_string == "undefined" //get a new session if not
|| empty($session_string) || is_null($session_string)){
$session_string = Yii::$app->security->generateRandomString()
. '_' . time(); //generating session string
$bf_session->sid = $session_string;
$bf_session->save(); //storing new session
}else{
$bf_session = Fast::find()->where(['sid' => $session_string])->one(); //existing session located
}

$challenge = Yii::$app->security->generateRandomString() . '_' . time(); //generate new challenge

$clist = $bf_session->v; //get session challenge list
if(!empty($clist))
$clist .= "," . $challenge; //add new challenge
else
$clist = $challenge;

$cs = explode(",", $clist); //remove the oldest challenges
if(count($cs)>4) //keeping 2 minutes worth of
{ //challenges at 30 sec QR code
$a = array_slice($cs, -4); //requests
$clist = implode(",", $a);
}
$bf_session->v = $clist; //store updated challenge list
$bf_session->save();

$qr_image = "https://portal.bytefederal.com/api/fastbyte?sid="
. $session_string . "&c=" . $challenge . "&t=" . dechex(time()); //return webhook URL with session string
//and latest challenge
}else{
$status = 1; //user is already logged in
}
$json = json_encode(array("qr" => $qr_image, "sid" => $session_string,
"status" => $status)); //encode json...
Yii::$app->response->headers->add('Content-Type', 'application/json');
Yii::$app->response->format = \yii\web\Response::FORMAT_RAW;
echo $json; //and return.
}
info

Make sure to store a buffer with the last few valid client/server challenges. This accounts for any lag time between client and server, and will ensure the user is able to authenticate.

2. Webhook Request

This function serves to receive and authenticate the signed message provided by ByteWallet. Once the ByteWallet user scans the QR code generated in Generate QR Code then a request will be sent to the endpoint created here so that your server can authenticate if the payload is signed with the corresponding private key.

Parameters


Request

msg: string => The end result of the QR code. The redirect URL is the webhook needed to post the authorization request to. This string contains the query parameters below.

  • session_id: string => unique token that a web server assigns a user for their session

  • challenge: string => mutual key phrase shared between server and client

  • timestamp: string => utc-timestamp of moment qr is generated

pubk: string => user's provided public key

sig: string => signed msg with the users private key

addr: string => user's public address


Response

response: string => success/error message


Example

actionFastByte.php
     /**
* Fast Byte Webhook.
*
* Your Webhook URL that receives a signed message from Bytewallet. It checks the current challenge (and optionally time)
* and verifies that the incoming request was properly signed by a user who claims ownership over a certain public address.
* When challenge matches and digital signature of the webhook URL itself is confirmed the user is authenticated. In this
* example the webhook uses a session table to store recent challenges and upon successful authentication saves user's
* public address to allow a frontend script checking status of the session to trigger a successful redirect.
*
* GET
* @link /api/fastbyte?sid=x&c=y&t=z
* @param string $c your challenge token
* @param string $sid your session token
* @param string $t utctime in hex
*
* POST
* @param string $pubk user's public key (optional).
* @param string $msg this url itself
* @param string $sig signed message of this URL
* @param string $addr user's public address
*
* @return echo success/error message.
*/
public function actionFastbyte()
{
try
{
$challenge = Yii::$app->getRequest()->getQueryParam('c'); //the current challenge
$session_string = Yii::$app->getRequest()->getQueryParam('sid'); //the session id
$pubk = Yii::$app->request->post('pubk'); //user's pubkey
$msg = Yii::$app->request->post('msg'); //this being this URL
$sig = Yii::$app->request->post('sig'); //the signed message
$address = Yii::$app->request->post('addr'); //user's public address

$j = json_decode(Yii::$app->request->getRawBody()); //in case the content
if(empty($msg)) //is posted raw
{
$j = json_decode(Yii::$app->request->getRawBody());
$msg = $j->data->msg;
$sig = $j->data->sig;
$address = $j->data->addr;
}
$bf_session = new Fast; //loading our session table
$bf_session = Fast::find()->where(['sid' => $session_string])->one(); //locating the session
$clist = $bf_session->v; //loading past 4 challenges
$all_challenges = explode(",", $clist);
if (in_array($challenge, $all_challenges)) //is current challenge in
{ //session challenge list?
echo "found challenge...";
$bitcoinECDSA = new BitcoinECDSA(); //loading Bitcoin crypto library
if($bitcoinECDSA->checkSignatureForMessage($address, $sig, $msg)) //verifying signature
{
$bf_session->s = $address; //storing successfully signed
$bf_session->save(); //user's address in session
echo "User has been successfully authenticated";
}
else
{
echo "Couldn't verify message";
}
}
else
{
echo "no challenge found, sorry";
}
}
catch (Exception $e)
{
echo "Error: " . $e;
}
}
caution

Caveat: The webhook function will receive your request from a different IP than the session was iniated with (customer's desktop computer on his home network, for instance and his cell phone with ByteWallet on a cellular network). Make sure that the request can be received (cross site scripting prevention and same site stipulations might block the incoming request in your web framework).

tip

If you get 500 (Internal Server Errors) on posting to the webhook, check for one of the following:

  • Your Bitcoin ECDSA library is properly installed with all its requirements
  • Your webhook receives address/msg/signature values
  • Your own user lookup functions execute properly and don't run into database exceptions

3. Polling Authentication Status

This function serves to change the status of the user to authenticate into the site. If the public address (or optionally public key) exists for this particular session_id then the user has been successfully authenticated via the webhook. Now the user can be redirect to their access restricted page (for instance a dashboard page or account page).

Parameters


Request

  • session_id: string => unique token that a web server assigns a user for their session

Response

response: string=> login status

JSON

We return a status variable in JSON letting the client side know whether the server successfully authenticated the user in the meantime.

  sid: "-ioAzu3w6AJaZ_uu7SnT7HTvJ6ddtFNQ_1660924354"
status: 0 //when this session is still unauthenticated
status: 1 //successful authentication detected

Example

actionCheckfast.php
    /**
* Polling authentication status
*
* If the user's signature has been verified by the webhook, the current session will contain the user's public address.
* This function returns the appropriate status change when the authentication succeeds, allowing the frontend to redirect
* the user to his account/dashboard...
*
* GET
* @link /api/checkfast?sid=x
* @param string $sid your session token
*
* @return echo login status.
*/
public function actionCheckfast()
{
try
{
$status = 0;
$session_string = Yii::$app->getRequest()->getQueryParam('sid'); //retrieving the session variable
$bf_session = Fast::find()->where(['sid' => $session_string])->one(); //retrieving the session
if(!is_null($bf_session->s) && !empty($bf_session->s)) //if user authenticated successfully
$status = 1;

$json = json_encode(array("sid" => $session_string, "status" => $status)); //create json object
Yii::$app->response->headers->add('Content-Type', 'application/json');
Yii::$app->response->format = \yii\web\Response::FORMAT_RAW;
echo $json; //return.
}
catch (Exception $e)
{
echo "Error: " . $e;
}
}

4. Last but not least: Verify Status

The last piece serves to grant authentication and verify that the user is able to authenticate. A lookup is done on the user session_id. Then, the session_id is used to correspond to the public_key. If the public key matches with the signed msg then user is granted authorization and redirected to the dashboard.

Example

actionDashboard.php
     /**
* Loading a Dashboard.
*
* We check the session's authentication status and use the webframework's login function to log the user in.
*
*/
public function actionDashboard()
{
try
{
$this->layout = 'dashboard';
$session_string = Yii::$app->getRequest()->getQueryParam('sid);
if(!empty($session_string))
{
$bf_session = Fast::find()->where(['sid' => $session_string])->one();
if(!is_null($bf_session->s) && !empty($bf_session->s))
{
$identity = User:findOne(['pubk' => $bf_session->s]);
if (Yii::$app->user->login($identity))
{
$data['body'] = "Logged in successfully!";
return $this->render('dashboard', $data);
}
}
}
}
catch (Exception $e)
{
echo "Error: " . $e;
}
}

Whats Next?

Congratulations! Your user base should now be able to authenticate within your website with ByteWallet.

  • You could offer payment options for crypto (your users have a wallet!).
  • You could send rewards and bonus points to their wallet.
  • Your users can convert crypto to fiat and fiat to crypto.
  • You can add Byte Fast Auth to other pages.
  • You could create (mini) webservices that solely use Fast Auth and without user meta data.