Integrating keybase with a php website
Integrating keybase with a php website


Today's article will be a bit different from most of my articles. Its going to explain how to integrate keybase verification with a php website. This article is specifically for people who use php as a programming language and it assumes you have coding experience and knowledge. 

 I am creating this because it took me a while to understand the api instructions from their website as a php developer and my hope is it will help others who are also seeking to allow users to verify profiles with keybase.

There are some prerequisites needed to make this work. 

Keybase integration prerequisites

        1. A table in your database called keybase
        2. 5 columns in that table:
             a. id -> integer(11) -> auto incriment -> primary key
             b. username -> varchar(255)
             c. kb_username -> varchar(255)
             d. token -> varchar(512)
             e. kb_ua -> varchar(512)
        3. 32x32 px full color logo
        4. 16x16 px black and white logo

Keybase controller

The keybase controller is a flat json file that tells keybase where to find all other files needed to make keybase work. 

This file instructs keybase about where to find your logo's, what your site name is, what your site description is, where to locate users profiles, where to get the proof of ownerships, where o send user to prove ownership, where their avatars can be found, who to contact if their are problems, and several other details. 

For simplicity you can just copy and modify the details below. The file can be named whatever you like but mus end in '.json'. We named our file 'kb_proof.json'. We place all files in a folder called '.well-known/'.

When modifying this file, you will want to change domain, displayname, logos, description, prefille_url, profile_url, checkurl, avatar_path, and contact. Make sure when modifying you leave the variables on the urls as they are. The url variables are needed by keybase.

For example: %{username} represents the username of your user and should not be changed.

{
"version": 1,
"domain": "your.website.url",
"display_name": "Title to use on keybase (1 to 2 words)",
"username": {
"re": "^[a-zA-Z0-9_]{2,20}$",
"min": 3,
"max": 32
},
"brand_color": "#FFB800",
"logo": {
"svg_black": "https://your.website.url/.well-known/t16.svg",
"svg_full": "https://your.website.url/.well-known/t32.svg"
},
"description": "A very brief description of your website so users of keybase can know what your site is about.",

"prefill_url": "https://your.website.url/.well-known/new-profile-proof.php?kb_username=%{kb_username}&username=%{username}&token=%{sig_hash}&kb_ua=%{kb_ua}",

"profile_url": "https://your.website.url/%{username}",

"check_url": "https://your.website.url/.well-known/kb_proofs_users.php?username=%{username}",

"check_path": ["signatures"],

"avatar_path": "https://your.website.url/.well-known/profile_image.php?username=%{username}",

"contact": ["admin@your.website.url", "admin"]
}

Profile Proof

After we have the controller file setup we have to set things up to make them work. The first step in this is generating a profile proofing file. This is the webpage a user will be sent to when then click to prove ownership on keybase. 

This file needs to gather the data sent by keybase, it needs to get the users permission to verify their ownership (for security reasons), i needs to ensure the correct keybase user is matching the correct profile, and it needs to either insert or update this information in the database.

We use php PDO functions to do this. We assume you have a configuration file that makes the database connection and sets the variable for the username of the user to $username and has a function to check if users are logged in. In our files this is done through an include of config.php

For our example we called this file 'new-profile-proof.php'.

First we include our configuration file:

<?php
// database connection config files

include $_SERVER['DOCUMENT_ROOT'] . '/includes/config.php';

Next we use a function to verify the user is logged in. If not we send them to the login page of our website and also forward all the variables sent to us from keybase to our login page. We will explain more about handling the login page further in the article.

// verify user is logged in or login
if(!$user->is_logged_in()){
    header('Location: /login.php?kb_username='.$_GET['kb_username'].'&username='.$_GET['username'].'&token='.$_GET['token'].'&kb_ua='.$_GET['kb_ua']);
} else {

For security reasons we need to now secure the parameters sent to us by keybase as well as verify the username sent by keybase is in fact the same user that is logged in. 

// secure paramaers passed by keybase
if (isset($_GET['kb_username']) && isset($_GET['username']) && isset($_GET['token']) && isset($_GET['kb_ua'])) {
    $kb_username = strip_tags($_GET['kb_username']);
    $us_name = strip_tags($_GET['username']);
    $token_hash = strip_tags($_GET['token']);
    $user_agent = strip_tags($_GET['kb_ua']);
    // verify the username passed by keybase is the username of logged in user
    if ($us_name != $username) {
        die;
    }
}

We then need to check if this is a POST or not. If it is a POST it means the user has authorized they are the owner, so we need to secure the posted data and prepare it to be stored in the database. 

// check if this is a post
if (isset($_POST['kb_username']) && isset($_POST['token']) && isset($_POST['kb_ua'])) {
    // secure POST details
    $kb_username = strip_tags($_POST['kb_username']);
    $token_hash = strip_tags($_POST['token']);
    $user_agent = strip_tags($_POST['kb_ua']);
    // do a check to see if we should insert or update
    $stmt=$db->prepare('SELECT COUNT(*) FROM keybase WHERE kb_username = :kb_username');
    $stmt->bindParam(':kb_username', $kb_username);
    $stmt->execute();
    $check = $stmt->fetchColumn();

I should mention here that the website this was generated for only allows a user to have 1 profile. So whereas a user can have multiple keybase accounts and verify their profile on multiple keybase accounts, this does not allow them to have multiple profiles on our site. So they can only claim one profile per keybase account. 

This is different than twitter for example where one can have 5 twitter accounts and verify all 5 twitter accounts on one keybase account. 

So continuing with the code, if the check from above is less than 1 that means its a new proof and we need to insert into our database. If the check is not less than 1 we need to do an update the existing data in our database with the new proof.

After we have added the data to the database, we redirect the user back to keybase and include our website url, the username, the and all other data that was sent to us by keybase. They will then verify the proof on their end. Note in the below code this means you need to change the domain to match your own website.

if ($check < 1) {
    // INSERT into the database
    $stmt = $db->prepare('INSERT INTO keybase (username, kb_username, token, kb_ua) VALUES (:username, :kb_username, :token, :kb_ua)');
    $stmt->bindParam(':username', $username);
    $stmt->bindParam(':kb_username', $kb_username);
    $stmt->bindParam(':token', $token_hash);
    $stmt->bindParam(':kb_ua', $user_agent);
    $stmt->execute();
    header('Location: https://keybase.io/_/proof_creation_success?domain=your.website.url&kb_username='.$kb_username.'&username='.$username.'&sig_hash='.$token_hash.'&kb_ua='.$user_agent);
} else {
    // UPDATE the database
    $stmt = $db->prepare('UPDATE keybase SET kb_username = :kb_username, token = :token , kb_ua = :kb_ua WHERE username = :username');
    $stmt->bindParam(':username', $username);
    $stmt->bindParam(':kb_username', $kb_username);
    $stmt->bindParam(':token', $token_hash);
    $stmt->bindParam(':kb_ua', $user_agent);
    $stmt->execute();
    header('Location: https://keybase.io/_/proof_creation_success?domain=your.website.url&kb_username='.$kb_username.'&username='.$username.'&sig_hash='.$token_hash.'&kb_ua='.$user_agent);
}
}

In the final step of this file we need to generate the form. This is displayed to the user so they can verify ownership and save the information to the database. In our example we do this by adding an overlay form to the users profile. There are many ways this can be handled as its mostly just html.

// echo html form for user to verify. This prevents malicious link from being sent to user
    $profile = file_get_contents('https://your.website.url/'.$username);
    echo $profile;
    echo '<link rel="stylesheet" href="keybase.css">';
    echo '<div class="kb_container"><h3 class="kb_header">Verify Ownership of this Profile on Keybase</h3>';

    // echo users primary keybase photo
    $string = file_get_contents('https://keybase.io/_/api/1.0/user/lookup.json?usernames='.$kb_username);
    $user = json_decode($string, true);
    $photo = $user['them'][0]['pictures']['primary']['url'];
    if ($photo == '') {
        $photo = 'https://keybase.io/images/no-photo/placeholder-avatar-180-x-180@2x.png';
    }
    echo '<div class="kb_content">';
    echo '<div class="img-container"><img class="lazyload" data-src="'.$photo.'" class="photo"><br><br>'.$kb_username.'<br>on keybase</div>';
    echo '<img class="lazyload" data-src="https://i.ibb.co/bXSCwjt/left-right-arrow-icon-1.jpg" class="arrow">';
    echo '<div class="img-container"><img class="lazyload" data-src="https://your.website.url/.well-known/profile_image.php?username='.$username.'" class="photo"><br><br>'.$username.'<br>on website name</div>';
    echo '</div>';
    echo '<div class="form-container">';
    echo '<button class="btn-warning deny">DENY</button>';
    echo '<form method="POST"><input type="hidden" name="kb_username" value="'.$kb_username.'"><input type="hidden" name="token" value="'.$token_hash.'"><input type="hidden" name="kb_ua" value="'.$user_agent.'">';
    echo '<button type="submit" class="btn-success approve">APPROVE</button></form>';
    echo '</div></div>';
    echo '<script>$("body").css("overflow", "hidden"); $(".deny").on("click", function() { window.location.href = "/'.$username.'" });</script>';
    echo '<div class="overlay"></div>';
}

If you combine all the above code and modify the website url's and html - you should have a perfectly working profile proof page. At this point there are only three more things left to do.

We still need to create a verification page for keybase to ensure the data, we need to setup our login page to correct work when a logged out user tries to verify their profile, and we still need to create a badge to display on a users profile after proofing has taken place.

CSS

The css file for the above html is as follows:

.overlay {
    position: absolute;
    width: 100vw;
    height: 100vh;
    background-color: black;
    top: 0;
    overflow: hidden;
    z-index: 9999;
    opacity: 0.6;
}
.kb_container {
    position: absolute;
    width: 50vw;
    min-height:50vh;
    height: auto;
    background-color: white;
    top: 20%;
    left: 25%;
    overflow: hidden;
    z-index: 99999;
}
.kb_header {
    text-align: center;
    background-color: #f2f2f2;
    padding: 25px;
    margin: 0px;
    border-bottom: 1px solid darkgray;
}
.kb_content {
    text-align: center;
    margin-top:50px;
}
form, .approve, .deny {
    padding: 10px;
    width: 135px;
    border-radius: 10px;
    font-size:16pt;
    margin: 10px;
    display:inline-block
}
.form-container {
    text-align: center;
    margin:20px;
}
.img-container {
    font-size: 16pt;
    display:inline-block;
}
.photo {
    width:128px;
    border-radius:50%
}
.arrow {
    width:100px;
    margin-left:20px;
    margin-right:20px;
    vertical-align:top;
    margin-top:50px;
}
@media (max-width: 800px) {
    .kb_container {
        position: absolute;
        width: 96vw;
        min-height:90vh;
        height: auto;
        background-color: white;
        top: 5vh;
        left: 2vw;
        overflow: hidden;
        z-index: 99999;
    }
    .kb_header {
        text-align: center;
        background-color: #f2f2f2;
        padding: 25px;
        margin: 0px;
        border-bottom: 1px solid darkgray;
    }
}

Keybase Proofs

The code below is used to allow keybase to verify that a user has authorized and that the correct security keys were used during that authorization. There are three parts to this code. 

In the first part we check in the user exist and has authorized profile proofing. If so and all is correct that data is returned back to keybase in a json file. If the user exists but has not authorized we return back with empty json data, and if the user does not exist we reply back with a 404 status.

The only details that you should need to change from this file are the paths where files are located and your website url.

<?php
    $usern = strip_tags($_GET['username']);
    include $_SERVER['DOCUMENT_ROOT'] . '/includes/config.php';

    $stmt=$db->prepare('SELECT kb_username, token AS sig_hash FROM keybase WHERE username = :username');
    $stmt->bindParam(':username', $usern);
    $stmt->execute();
    $proof = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // if user exists and has authorized
    if ($proof) {
        $arr = array("avatar_url" => "https://your.website.url/.well-known/profile_image.php?username=".$usern, "signatures" => $proof );
        echo json_encode( $arr, JSON_UNESCAPED_SLASHES );
    } else {
        $stmt=$db->prepare('SELECT count(*) FROM members WHERE username = :username');
        $stmt->bindParam(':username', $usern);
        $stmt->execute();
        $check = $stmt->fetchColumn();

        // if user exists and has not authorized
        if ($check > 0) {
            $arr = array("avatar_url" => "https://your.website.url/.well-known/profile_image.php?username=".$usern, "signatures" => $proof );
            echo json_encode( $arr, JSON_UNESCAPED_SLASHES );

            // if user does not exist
        } else {
            http_response_code(404);
        }
    }

Login Page

I am going to assume that if you are following along with the code examples that you already have a working website and that members can login to that website. In a typical login, after a user enters their login details, they are redirected to a dashboard or some sort of logged in page.

Instead of directly logging them in as you normally would, instead we need to do a check. This check will look to see if the keybase variables are included on the login page and if so we will redirect them to the keybase verification page with those varialbes instead, otherwise we will log them in as normal.

if (isset($_GET['kb_username']) && isset($_GET['username']) && isset($_GET['token']) && isset($_GET['kb_ua'])) {
    header('Location: /.well-known/new-profile-proof.php?  kb_username='.$_GET['kb_username'].'&username='.$_GET['username'].'&token='.$_GET['token'].'&kb_ua='.$_GET['token']);
} else {
    // normal code to redirect to members area
}

The login page merely needs o be modified to add these changes. It's as simple as that. 

Keybase Verification Badge

The final step is adding a verification badge to the users profile. This badge is a badge of your own creation and should link back to the users keybase profile. 

// check database to see if user has verified with keybase
$stmt = $db->prepare('SELECT kb_username, token FROM keybase WHERE username = :username ORDER BY id desc LIMIT 1');
$stmt->bindParam(':username', $u);
$stmt->execute();
$details = $stmt->fetch();

if (isset($details['kb_username'])) {
    // if verified get details
    $kb_username = $details['kb_username'];
    $token = $details['token'];

    // verify the details match what keybase says for the user and that the user has not revoked access
    $string = file_get_contents('https://keybase.io/_/api/1.0/user/lookup.json?usernames='.$kb_username);
    $user = json_decode($string, true);
    $kb_key = $user['them'][0]['proofs_summary']['by_presentation_group']['tactoken.io'][0]['sig_id'];

    if ($token == $kb_key) {
         // add your badge to the users profile
        echo '<a href="https://keybase.io/'.$kb_username.'"><img class="lazyload" data-src="profile_badge.png"></a>';
    }
}

After you have completed all the above steps and verified they are working, you need to send a link to your controller file to miles@keyba.se or to @mlsteele on keybase. 

When they have finished integration on their end, they will let you know and you can inform users that they can begin verifying their profiles on keybase. 



Everything Cryptocurrency
Everything Cryptocurrency

Everything related to cryptocurrency news, cryptocurrency projects, cryptocurrency prices and cryptocurrency speculations.

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.