Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Custom OAuth 2 Authentication in Apigility

DZone's Guide to

Custom OAuth 2 Authentication in Apigility

Getting Apigility's OAuth 2 integration to talk to a specific table name is quite easy. Simply add this config:

· Integration Zone
Free Resource

Build APIs from SQL and NoSQL or Salesforce data sources in seconds. Read the Creating REST APIs white paper, brought to you in partnership with CA Technologies.

I have a client that's writing an Apigility API that needs to talk to a database that's already in place. This also includes the users table that is to be used with Apigility's OAuth2 authentication.

Getting Apigility's OAuth 2 integration to talk to a specific table name is quite easy. Simply add this config:

'storage_settings' => array(
    'user_table' => 'user',
),

To the relevant adapter within zf-mvc-auth => authentication config.

However, if you want to use different column names, that's a bit trickier as they are hardcoded in theOAuth2\Storage\Pdo class. To get Apigility's OAuth2 components to look at the correct columns, you create your own OAuth2 Adapter. I chose to extend ZF\OAuth2\Adapter\PdoAdapter which extends OAuth2\Storage\Pdo and go from there.

ZF\OAuth2\Adapter\PdoAdapter extends the base class to add bcrypt hashing. This is good, so it's a good place to start from. I created a new module, MyAuth to hold my adapter and its factory. The adapter looks like this:

<?php
namespace MyAuth;

use ZF\OAuth2\Adapter\PdoAdapter;

/**
 * Custom extension of PdoAdapter to validate against the WEB_User table.
 */
class OAuth2Adapter extends PdoAdapter
{
    public function __construct($connection, $config = array())
    {
        $config = [
            'user_table' => 'legacy_user'
        ];

        return parent::__construct($connection, $config);
    }

    public function getUser($username)
    {
        $sql = sprintf(
            'SELECT * from %s where email_address=:username',
            $this->config['user_table']
        );
        $stmt = $this->db->prepare($sql);
        $stmt->execute(array('username' => $username));

        if (!$userInfo = $stmt->fetch(\PDO::FETCH_ASSOC)) {
            return false;
        }

        // the default behavior is to use "username" as the user_id
        return array_merge(array(
            'user_id' => $username
        ), $userInfo);
    }

    public function setUser($username, $password, 
        $firstName = null, $lastName = null)
    {
        // do not store in plaintext, use bcrypt
        $this->createBcryptHash($password);

        // if it exists, update it.
        if ($this->getUser($username)) {
            $sql = sprintf(
                'UPDATE %s SET pwd=:password, firstname=:firstName,
                    surname=:lastName WHERE username=:username',
                $this->config['user_table']
            );
            $stmt = $this->db->prepare($sql);
        } else {
            $sql = sprintf(
                'INSERT INTO %s (email_address, pwd, firstname, surname)
                    VALUES (:username, :password, :firstName, :lastName)',
                $this->config['user_table']
            );
            $stmt = $this->db->prepare($sql);
        }

        return $stmt->execute(compact('username', 'password', 'firstName',
            'lastName'));
    }

    protected function checkPassword($user, $password)
    {
        return $this->verifyHash($password, $user['pwd']);
    }
}

This code for getUser and setUser() is lifted directly from OAuth2\Storage\Pdo and all I've done is changed the column names. In this case I have email_address for my username, and pwd for the password column. Similar, I wrote my own checkPassword based on ZF\OAuth2\Adapter\PdoAdapter, again changing the array key to check to'pwd'.

Now that we have the actual work done, we need to wire it into Apigility.

Firstly we need a factory so that the DIC can instantiate our adapter:

<?php
namespace MyAuth;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\Driver\Pdo\Pdo as PdoDriver;

class OAuth2AdapterFactory implements FactoryInterface
{
    /**
     * Create service
     *
     * @param ServiceLocatorInterface $serviceLocator
     * @return OAuth2Adapter
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        $connection = $serviceLocator->get('DB\Master');
        if (!$connection->getDriver() instanceof PdoDriver) {
            throw new \RuntimeException("Need a PDO connection!");
        }

        $pdo = $connection->getDriver()->getConnection()->getResource();
        return new OAuth2Adapter($pdo);
    }  
}

This is fairly standard code. Note that the DB\Master is the name of the database connection that is set up in the Apigility admin. I've been a bit lazy and assume that it's a PDO based adapter. If it isn't, it'll blow up, so if you're not using PDO, then it won't work as is!

To register your new authentication adapter with Apigility, create a config file in config/autoload and call itmyauth.global.php or something:

<?php
return [
    'zf-mvc-auth' => [
        'authentication' => [
            'adapters' => [
                'MyAuth' => [
                    'adapter' => 'ZF\\MvcAuth\\Authentication\\OAuth2Adapter',
                    'storage' => [
                        'storage' => 'MyAuth\OAuth2Adapter',
                        'route' => '/oauth',
                    ],
                ],
            ],
        ],
    ],
];

The adapter is called MyAuth and is now available to select in the API configuration pages of the admin:

Myauth apigility

To sum up

All in all, it's really easy to write custom OAuth 2 authentication for Apigility as it's a very flexible platform. I've simply changed the column names here, but it would be easy enough to write an adapter against a different storage system altogether, though you would have to override more methods and possibly start from a more appropriate base class.

The Integration Zone is brought to you in partnership with CA Technologies.  Use CA Live API Creator to quickly create complete application backends, with secure APIs and robust application logic, in an easy to use interface.

Topics:
apigility ,api ,oauth2 ,oauth ,oauth integration

Published at DZone with permission of Rob Allen, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}