- 2013/07/03

I know common authentication in web application / website is username and password. And then the more I know about authentication mechanism in username+password, the more I want to know “Is there any other options?”. Curiosity. Since 2010-2011 I knew about TFA when my website was built on Wordpress. At that time, Google, Facebook, use TFA to verify user login process. Then I no longer use Wordpress and start to create my own website, from scratch.

WALK AROUND

  • I have a website (this site), built with ZF2, and I want to make authentication mechanism more fun with Twitter OAuth. ZF2 provides ZendService\Twitter for having fun with Twitter. Good.
  • My old authentication mechanism is username+password, I want to get rid of my password, username is same with my Twitter account, lets Twitter take care my login. Good.
  • Then I want to combine it with TFA. Thanks to Duo Security that provides their users with many SDK, they have PHP WebSDK, AND their service is free for personal. Good.
  • Duo Security had DuoMobile, mobile application for multiple platform to generate TFA key or DuoPush, and I have an Android then install DuoMobile. Good.

I want to combine all of these authentication things for my website, so lets create a ZF2 module for this.

FLOW

Here is the flow, my case:

  1. From whatever module I want to protect, if user has not logged-in, redirect them to login window.
  2. Show user a note about how to login.
  3. Take the user to Twitter Oauth Authorization page to authorize oauth application.
  4. If user login to twitter, and authorize the application, take them back to login process.
  5. Okay, user has logged-in to twitter, inject their username to TFA process.
  6. If user is not registered in my DuoSecurity users, kick out and bring back to login window.
  7. If user registered, user will see TFA login approval, they have three options (Duo Push, Phone Call, Passcode, SMS Passcode)
  8. User will need their phone to approve duo push or generate passcode.
  9. If user approved by TFA and match the username to database, redirect to protected module. If not match, log them out.

So all of username must be the same (twitter, duo security, and database). Login -> (Twitter OAuth –> DuOauth module –> Duo Security TFA -> DuOauth module) -> administrator section.

IMPLEMENTATION

DuOauth Module

I have created a module named “DuOauth” to cover Flow #1 to #9. You can download it from Github.

<?php
return array(
    'modules' => array(
        'Application',
        'DuOauth',  // DuOauth 
        'Admin'     // Protected module
    ),
    // another application options
);

Integrate To My Application

I have admin module that I want to protect from guest access, so here is authentication check in Module.php

<?php
namespace Admin;
 
use Zend\Mvc\MvcEvent;
 
class Module
{
    public function onBootstrap(MvcEvent $event)
    {
        $shm = $event->getApplication()->getEventManager()->getSharedManager();
        $shm->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', 
                      array($this, 'authenticationCheck'), 901);
    }
    
    public function authenticationCheck(MvcEvent $event)
    {
        $controller      = $event->getTarget();
        $controllerClass = get_class($controller);
        $moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
        if($moduleNamespace !== __NAMESPACE__) {
            return;
        }
        $userService = $event->getApplication()
                             ->getServiceManager()->get('duoauth-service-user');
        $authService = $userService->getAuthService();
        $request     = $controller->getRequest();
        if(! $authService->hasIdentity()) {
            $controller->redirect()->toRoute('duoauth-login-index');
        }
    }
}

For flow #9, I use a datamapper to match username against database that will return single array contain “username”. It implements \DuOauth\Mapper\UserFinderInterface::findOneByUsername($username) so whatever datamapper you create, DuOauth module will know method findOneByUsername() to authenticate with username returned by TFA success-result.

RESULT

Flow #1 and #2

1+2

Flow #3

3

Flow #7

7

Flow #8

8

Authentication mechanism is really serious part of web app, but you have your freedom if you write your own app, from the ancient username+password, to username+password+TFA, or whatever login-flow you choose. “Why so serious?”, coding is fun ;) You can check complete sample of my DuOauth module’s implementation on my github. I am not a native, I hope you understand my english :D