Set up MFA with Okta and TypingDNA Verify 2FA for your PHP App

Implement Okta 2FA or MFA

Intro

In this tutorial we will add TypingDNA Verify 2FA to a PHP application that has Okta already setup. The setup is based on the following video created by the Okta team:
https://www.youtube.com/watch?v=o7JW5uPDc5w

The steps presented in this tutorial are also explained in the video below.

Prerequisites

In order to setup Okta 2FA you will need 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.

Set-up your MFA with Okta and TypingDNA Verify 2FA

2. 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

Setup your biometric MFA in PHP using TypingDNA Verify 2FA and Okta

3. Setup the Okta development environment using the following tutorial:
https://www.youtube.com/watch?v=o7JW5uPDc5w

4. Setup ngrok. Ngrok creates a public url that will be linked 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/

5. It is recommended to be familiar with how TypingDNA Verify 2FA is set using the steps from the following tutorial: https://www.typingdna.com/docs/verify-integration-php.html

Configuration

The first step is to configure the Okta demo to run with ngrok.

Log in to the Okta dashboard.

Under Applications select the custom application that was created as part of the demo. In this case, the application is called TypingDNA Verify 2FA.

How to connect Ngrok with Okta for MFA setup

Edit the Sign-in redirect URIs and Sign-out redirect URIs with the ngrok link.

Sign-in redirect url's for MFA setup using Ngrok and Okta

In the index.php file edit the $redirect_uri to contain the ngrok link and client_id client_secret metadata_url with the values from Okta Dasboard.

            
$client_id = 'Your_client_id';
$client_secret = 'Your_client_secret';
$redirect_uri = 'https://b286080dca95.ngrok.io';
$metadata_url = 'https://dev-53749351.okta.com/oauth2/default/.well-known/oauth-authorization-server'

// Fetch the authorization server metadata which contains a few URLs
// that we need later, such as the authorization and token endpoints

$metadata = http($metadata_url);
            
        

After the OAuth2 exchange flow, add the following code that will set the username in session and will redirect the user to verify.php page:

            
if($token->active == 1) {
    $_SESSION['username'] = $token->username;
    header('Location: /verify.php');
    die();
}
            
        

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

            
<?php

    session_start();

    if(isset($_SESSION['username'])) {
        header('Location: /verify.php');
        die();
    }

     function http($url, $params=false) {

        $ch = curl_init($url);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        if($params) curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));

        return json_decode(curl_exec($ch));
    }

    $client_id = 'Your_client_id';
    $client_secret = 'Your_client_secret';
    $redirect_uri = 'https://b286080dca95.ngrok.io';
    $metadata_url = 'https://your_domain.okta.com/oauth2/default/.well-known/oauth-authorization-server'

    $metadata = http($metadata_url);

    if(isset($_GET['error'])) {
        die('Authorization server returned an error: '.htmlspecialchars($_GET['error']));
    }

    if(!isset($_SESSION['username']) && isset($_GET['code'])) {

        $response = http($metadata->token_endpoint, [
            'grant_type' => 'authorization_code',
            'code' => $_GET['code'],
            'redirect_uri' => $redirect_uri,
            'client_id' => $client_id,
            'client_secret' => $client_secret,
        ]);

        if(!isset($response->access_token)) {
            die('Error fetching access token');
        }

        $token = http($metadata->introspection_endpoint, [
            'token' => $response->access_token,
            'client_id' => $client_id,
            'client_secret' => $client_secret,
        ]);

        if($token->active == 1) {
            $_SESSION['username'] = $token->username;
            header('Location: /verify.php');
            die();
        }
    }

    $authorize_url = $metadata->authorization_endpoint.'?'.http_build_query([
        'response_type' => 'code',
        'client_id' => $client_id,
        'redirect_uri' => $redirect_uri,
        'state' => time(),
        'scope' => 'openid',
    ]);
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Okta - Verify</title>
</head>
<body>
    <p>Not logged in</p>
    <p><a href="<?=$authorize_url?>">Log In</a></p>
</body>
</html>
            
        

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

Next we will add the verify.php file. First we include TypingDNAVerifyClient library and then we initialize our variable just like we did in index.php. We also create an TypingDNAVerifyClient instance using the credentials from Verify 2FA Dashboard and store it in $typingDNAVerifyClient variable.

            
include('TypingDNAVerifyClient.php');

$client_id='your_verify_client_id';
$secret='your_verify_client_secret';
$application_id='your_verify_application_id';

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

Once we have a TypingDNAVerifyClient instance, we will generate the data attributes required to start TypingDNA Verify 2FA.

For this demo we will use the email as the root of trust and we will populate this information from the session variable.

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

For simplicity, in order to verify the OTP code that will be generated, we will redirect the user to the same page, where we will verify the code and print the response.

            
if( isset($_GET['otp']) ) {
    $response = $typingDNAVerifyClient->validateOTP([
        'email' => $_SESSION['username'],
    ], $_GET['otp']);

    print_r($response);
}
            
        

Next we will add the javascript library that will allow us to draw the TypingDNA Verify pop-up at the start of the file.

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

Next we will create the callbackFn that will redirect the user to the same page where we will process the result obtained from the Verify pop-up.

            
<script>
    function callbackFn(payload)
        {
            window.location.href = `verify.php?otp=${payload["otp"]}`;
        }
</script>
            
        

Finally we will add the TypingDNA Verify button that will start the pop-up.

            
<button
    class="typingDNA-verify"
    data-typingdna-client-id="<?= $typingDNADataAttributes['clientId']; ?>"
    data-typingdna-application-id="<?= $typingDNADataAttributes['applicationId']; ?>"
    data-typingdna-payload="<?= $typingDNADataAttributes['payload']; ?>"
    data-typingdna-callback-fn="callbackFn"
>
    Verify with Typingdna
</button>
            
        

Once we log in with the Okta credentials, the page will look similar to the following print screen.

MFA using Okta and Verify with Typingdna

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

            
<?php

    session_start();

    if(!isset($_SESSION['username'])) {
        header('Location: /');
        die();
    }

    $client_id='your_verify_client_id';
    $secret='your_verify_client_secret';
    $application_id='your_verify_application_id';

    include('TypingDNAVerifyClient.php');

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

    if( isset($_GET['otp']) ) {

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

        print_r($response);

        echo '<p><a href="/logout.php">Log Out</a></p>';
        die();
    }

    $typingDNADataAttributes = $typingDNAVerifyClient->getDataAttributes([
        'email' =>  $_SESSION['username'],
        'language' => "en",
        'mode' => "standard"
    ]);
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Okta - Verify</title>
    <script src ="https://cdn.typingdna.com/verify/typingdna-verify.js"></script>
    <script>
        function callbackFn(payload)
        {
            window.location.href = `verify.php?otp=${payload["otp"]}`;
        }
    </script>
</head>
<body>
    <p>Second factor of authentication</p>
    <button
            class="typingDNA-verify"
            data-typingdna-client-id="<?= $typingDNADataAttributes['clientId']; ?>"
            data-typingdna-application-id="<?= $typingDNADataAttributes['applicationId']; ?>"
            data-typingdna-payload="<?= $typingDNADataAttributes['payload']; ?>"
            data-typingdna-callback-fn="callbackFn"
    >
        Verify with Typingdna
    </button>
    <p><a href="/logout.php">Log Out</a></p>
</body>
</html>
            
        

Testing your new Okta 2FA functionality?

Let's go ahead and test the solution.

Our first screen will be identical from the Okta demo.

Test your biometric MFA setup for PHP apps with Verify 2FA and Okta

Then we will enter the username and password that we have set in Okta

Test MFA set up with Verify 2FA and Okta for PHP apps

Next we are presented with the TypingDNA Verify 2FA authentication page

Second-factor authentication for PHP with Verify 2FA and Okta

We click the "Verify with TypingDNA" button.

Typing biometrics MFA with Verify 2FA and Okta for PHP

After we complete the instruction from the TypingDNA Verify 2FA button we will be redirected to the verify_otp.php page where the result will be displayed.

Success message MFA set up with Verify 2FA and Okta for PHP

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


You can download the source code for this demo from our GitHub repository.


For the production environment, in order to send OTPs via emails you will need to integrate your Verify 2FA account with SendGrid from the dashboard.

Multi-factor authentication in PHP with Okta, Verify 2FA, Sendgrid

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