Setup 2FA with TypingDNA Verify on a PHP web application that is using Active Directory

Goal

The aim of this tutorial is to lay out the steps for integrating TypingDNA Verify 2FA on a basic website that has a backend running in PHP. The website will use Active Directory for the username and password and TypingDNA Verify 2FA will be used as the second factor of authentication.

This is what a successful integration of TypingDNA Verify 2FA with Active Directory will look like:

Intro

In this tutorial we will build a sample web application that is using TypingDNA Verify 2FA.

We will cover all technologies that are needed to run a simple demo on a local machine. The main objective of this tutorial is to focus on the TypingDNA Verify integration. For more information on how to set up Active Directory please visit the Active Directory documentation here:

https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/active-directory-domain-services.

Prerequisites

In order to create a working example of TypingDNA Verify 2FA, the following actions need to be taken:

  1. Create an account on typingdna.com. The Verify 2FA dashboard will provide you with the clientID, secret and applicationID.

  2. Setup ngrok. Ngrok creates a public url that will link to your localhost environment. For security purposes, TypingDNA Verify 2FA will only run on a public url. Ngrok can be downloaded from the following link: https://ngrok.com/.

  3. Create an account on the Azure portal. https://portal.azure.com (a trial membership is available on Azure and it can be used to implement this tutorial).

  4. Create a localhost environment that will run php applications. For this tutorial we will use XAMPP that can be downloaded from https://www.apachefriends.org/index.html.

Active Directory Configuration

For this tutorial we will use the simple login Active Directory directory that was created by Kevin Schroeder. More information can be found on the following github page: https://github.com/magium/active-directory.

Run the following command:

            
composer require magium/active-directory
            
        

Create an index.php file in the root directory. Add the following code:

            
<?php
    require_once __DIR__ . 'vendor/autoload.php';
    session_start();

    $config = [
        'authentication' => [
            'ad' => [
                'client_id' => '',
                'client_secret' => '',
                'enabled' => '1',
                'directory' => ''
            ]
        ]
    ];

    $request = new \Zend\Http\PhpEnvironment\Request();
    $ad = new \Magium\ActiveDirectory\ActiveDirectory(
        new \Magium\Configuration\Config\Repository\ArrayConfigurationRepository($config),
        Zend\Psr7Bridge\Psr7ServerRequest::fromZend(new \Zend\Http\PhpEnvironment\Request())
    );
    $entity = $ad->authenticate();
?>
            
        

Log into the Azure portal: https://portal.azure.com/.

Select the Azure Active Directory service:

Select App registrations from the left hand side menu:

Select New registration.

Type the name of your application and under Supported account types select the option “Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype Xbox)”

Click on the Register button.

Copy the Application (client) ID to your code.

From:

To:

Select the Certificates and secrets item from the left-hand side menu:

Select New client secret button.

Select a description and an expiration period. Then copy the key value

From:

To:

Next update the directory value to be ‘common’. And add another value in the configuration called return_url that will have the location of the current index.php file.

            
    $config = [
        'authentication' => [
            'ad' => [
                'client_id' => '**********',
                'client_secret' => '********',
                'enabled' => '1',
                'directory' => '',
                'return_url' => 'http://localhost/azuread/index.php'
            ]
        ]
    ];
            
        

Select the Overview option from the left hand side menu.

Select the Add a Redirect URI link.

Set Live SDK support to No and Allow public client flows to Yes.

Select Add a platform link.

Select Web and under the Redirect URIs field add the location of your index file and select Configure button.

TypingDNA Verify 2FA Configuration

We will create a file called verify.php in the same directory.

            
<html>
    <head>
    </head>
    <body>
        <h1> TypingDNA Verify </h1>
    </body>
</html>
            
        

Then we will navigate to the ngrok location folder and we will start up the public url with the following command: ngrok.exe http 80

This will create a public url that will look something like this:

Once the ngrok link has been created, we will set up this link in our Integration section in the Verify 2FA Dashboard.

We will start up our XAMPP Apache server.

By accessing our ngrok link, in this case https://ee503c799d3f.ngrok.io/verify.php we will see the following page:

In the index.php file we will add a redirect to the verify.php where we will pass the email address as a parameter:

The redirect will contain the full ngrok address:

            
header("Location: https://ee503c799d3f.ngrok.io/azuread/verify.php?email=".$entity->getPreferredUsername());
            
        

We will need to download the TypingDNA Verify 2FA PHP client (TypingDNAVerifyClient.php) from the TypingDNA GitHub location that is located here:

https://github.com/TypingDNA/TypingDNA-Verify-Client/tree/main/php

We will include this at the start of our verify.php file.

            
<?php
    include("TypingDNAVerifyClient.php");
?>
            
        

Next we will initialize TypingDNA Verify 2FA where we will pass as parameters the clientId, secret and applicationId.

            
$typingDNAVerifyClient = new TypingDNAVerifyClient($client_id, $application_id, $secret);
            
        

You will need to initialize the variable with the actual values from the Verify 2FA Dashboard.

Once the variable is initialized we will generate the data attributes required to start TypingDNA Verify 2FA.

            
$typingDNADataAttributes = $typingDNAVerifyClient->getDataAttributes([
    "email" => $_GET["email"],
    'language' => "en",
    'mode' => "standard"
]);
            
        

Our PHP code will look like this:

            
<?php
    include('TypingDNAVerifyClient.php');

    $typingDNAVerifyClient = new TypingDNAVerifyClient($client_id, $application_id, $secret);

    $typingDNADataAttributes = $typingDNAVerifyClient->getDataAttributes([
        "email" => $_GET["email"],
        'language' => "en",
        'mode' => "standard"
    ]);
?>

            
        

In order to generate the TypingDNA Verify 2FA window we will need to add the following javascript to our code:

            
<script src ="https://cdn.typingdna.com/verify/typingdna-verify.js"></script>
            
        

Next we will add the button that will start the TypingDNA Verify 2FA window.

            
<button
       class = "typingdna-verify"
       data-typingdna-client-id= <?php echo $typingDNADataAttributes["clientId"] ?>
       data-typingdna-application-id= <?php echo $typingDNADataAttributes["applicationId"] ?>
       data-typingdna-payload= <?php echo $typingDNADataAttributes["payload"] ?>
       data-typingdna-callback-fn= "callbackFn"> Verify with TypingDNA
</button>
            
        

The javascript callbackFn will be called once the TypingDNA Verify 2FA process is completed.

            
<script>
    function callbackFn(payload) {
        const email = '<?php echo $_GET['email'];?>';
        window.location.href = `verify_otp.php?email=${email}&otp=${payload['otp']}`;
    }
</script>
            
        

TypingDNA Verify 2FA will return a payload that will contain the one time password that is generated. For simplicity, in this demo we will navigate to another page and we will pass the one time password as a parameter in the link.

The final version of the index.php will look like this:

            
<?php
    include("TypingDNAVerifyClient.php");

    $typingDNAVerifyClient = new TypingDNAVerifyClient($client_id, $application_id, $secret);

    $typingDNADataAttributes = $typingDNAVerifyClient->getDataAttributes([
        "email" => $_GET["email"],
        "language" => "en",
        "mode" => "standard"
    ]);
?>
<html>
    <script src="https://cdn.typingdna.com/verify/typingdna-verify.js"></script>
    <script>
        function callbackFn(payload) {
            const email = '<?php echo $_GET['email'];?>';
            window.location.href = `verify_otp.php?email=${email}&otp=${payload['otp']}`;
        }
    </script>
    <head>
    </head>
    <body>
        <h1> TypingDNA Verify </h1>
        <button
            class = "typingdna-verify"
            data-typingdna-client-id= <?php echo $typingDNADataAttributes["clientId"]?>
            data-typingdna-application-id= <?php echo $typingDNADataAttributes["applicationId"] ?>
            data-typingdna-payload= <?php echo $typingDNADataAttributes["payload"]?>
            data-typingdna-callback-fn= "callbackFn"
            >Verify with TypingDNA
        </button>
    </body>
</html>
            
        

Next we will look at the verify_otp.php file. First we will initialize our variable, just like we did in verify.php.

            
<?php
    include('TypingDNAVerifyClient.php');

    $typingDNAVerifyClient = new TypingDNAVerifyClient($client_id, $application_id, $secret);
?>
            
        

In order to validate that the one time password is correct we will use the validateOTP function that will make an API call to the TypingDNA server if the one time password is correct.

            
    $response = $typingDNAVerifyClient->validateOTP([
        'phoneNumber' => $_GET['email'],
    ], $_GET['otp']);
            
        

Next we will print on the screen the response.

            
    print_r($response);
            
        

The final version of the verify_otp.php will look like this:

            
<?php
    include('TypingDNAVerifyClient.php');

    $typingDNAVerifyClient = new TypingDNAVerifyClient($client_id, $application_id, $secret);

    $response = $typingDNAVerifyClient->validateOTP([
        'email' => $_GET['email'],
    ], $_GET['otp']);

    print_r($response);
?>
            
        

Now we can go ahead and test our solution.

Testing

Access the index.php file from your localhost: localhost/azuread.

Enter your microsoft account credentials.

Next you will be redirected to the 2FA page with TypingDNA Verify.

Click on "Verify with TypingDNA".

Type the 4 words that are presented on the screen.

The success value indicates if it was a valid authentication 1 or if it was invalid 0.

For more support, contact us at support@typingdna.com.

Tell your team there’s a better way to 2FA

Share this message across Slack, email, etc. We even jotted down some of the highlights to make it easier.

Check this out! 🚀 Found a cool way to 2FA our users: TypingDNA Verify 2FA. It authenticates people based on how they type — replacing authenticator apps and OTP codes. Awesome user experience! 🙌 Quick integration too (under 10 mins). And we can try it free with 1,000 users. What do you think? https://www.typingdna.com/verify

Copy