Tutorial: Authentication

Introduction

phpBB 3.1 introduced built-in authentication plugins which you can extend or create your own.

This tutorial explains:

Authentication Providers

phpBB 3.1 supports external authentication plugins that may be used in place of the built-in authentication providers. Only one provider may currently be active at a time and the active one is chosen from the ACP.

Creating an Authentication Provider

An authentication provider that comes with phpBB requires a minimum of two files: a class and an entry in a config/auth.yml file. Authentication providers that are part of an extension must provide their own YAML file defining the service in addition to all normal requirements of an extension.

The class file

The provider class must implement the \phpbb\auth\provider\provider_interface in order to ensure proper functionality. However, it is recommended to extend \phpbb\auth\provider\base so as to not implement unneeded methods and to ensure that the provider will not break due to an update to the interface. An example authentication provider class is show below:

<?php

namespace acme\demo\auth\provider;

/**
 * Database authentication provider for phpBB3
 *
 * This is for authentication via the integrated user table
 */
class db2 extends \phpbb\auth\provider\base
{
    /** @var \phpbb\db\driver\driver_interface $db */
    protected $db;

    /**
     * Database Authentication Constructor
     *
     * @param \phpbb\db\driver\driver_interface $db
     */
    public function __construct(\phpbb\db\driver\driver_interface $db)
    {
        $this->db = $db;
    }

    /**
     * {@inheritdoc}
     */
    public function login($username, $password)
    {
        // Auth plugins get the password untrimmed.
        // For compatibility we trim() here.
        $password = trim($password);

        // do not allow empty password
        if (!$password)
        {
            return array(
                'status'    => LOGIN_ERROR_PASSWORD,
                'error_msg' => 'NO_PASSWORD_SUPPLIED',
                'user_row'  => array('user_id' => ANONYMOUS),
            );
        }

        if (!$username)
        {
            return array(
                'status'    => LOGIN_ERROR_USERNAME,
                'error_msg' => 'LOGIN_ERROR_USERNAME',
                'user_row'  => array('user_id' => ANONYMOUS),
            );
        }

        $username_clean = utf8_clean_string($username);

        $sql = 'SELECT user_id, username, user_password, user_passchg, user_pass_convert, user_email, user_type, user_login_attempts
            FROM ' . USERS_TABLE . "
            WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'";
        $result = $this->db->sql_query($sql);
        $row = $this->db->sql_fetchrow($result);
        $this->db->sql_freeresult($result);

        // Successful login... set user_login_attempts to zero...
        return array(
            'status'    => LOGIN_SUCCESS,
            'error_msg' => false,
            'user_row'  => $row,
        );
    }
}

The service file

For proper dependency injection the provider must be added to services.yml. The name of the service must be in the form of auth.provider.<service name> in order for phpBB to register it. The arguments are those of the provider’s constructor and may be empty if no arguments are necessary. The provider must be tagged with { name: auth.provider } in order for the class to be made available in phpBB.

services:
    auth.provider.db2:
        class: acme\demo\auth\provider\db2
        arguments:
            - '@dbal.conn'
        tags:
            - { name: auth.provider }

The template file

Following the above steps renders the authentication provider visible in the ACP. However, to allow an admin to configure your plugin the available fields need to be created in order to reach the configuration from the php-auth-provider plugin. This interface is configured in HTML format in adm/style/auth_provider_<providername>.html.

For example, the sample below is based on existing LDAP terms used to configure an HTTPS server:

<fieldset id="auth_test_settings">
    <legend>{TEST}</legend>
    <dl>
        <dt><label for="https_server">{TEST_SERVER}{L_COLON}</label><br /><span>{TEST_SERVER_EXPLAIN}</span></dt>
        <dd><input type="text" id="https_server" size="40" name="config[https_server]" value="{AUTH_HTTPS_SERVER}" /></dd>
    </dl>
</fieldset>

This value can then be retrieved from the <provider>.php file like this:

$domain = $this->config['https_server'];

OAuth Providers

phpBB 3.1 ships with a new authentication provider: OAuth. This provider is based on the Lusitanian/PHPoAuthLib library.

Enabling an OAuth Provider

To enable a new OAuth service in phpBB you need only create two files in your extension. The class file which defines functionality necessary for phpBB to get the data it needs from the service, and the service file which allows phpBB to find the class. To find out how you should most likely make calls to the OAuh service, it is recommended that you refer to the included OAuth services and to the examples provided by Lusitanian/PHPoAuthLib.

The example files below show the minimum needed to enable an OAuth service in phpBB. They are copies of the bitly service implementation from phpBB3’s develop branch.

The Class file

<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/

namespace phpbb\auth\provider\oauth\service;

/**
* Bitly OAuth service
*/
class bitly extends \phpbb\auth\provider\oauth\service\base
{
    /**
    * phpBB config
    *
    * @var \phpbb\config\config
    */
    protected $config;

    /**
    * phpBB request
    *
    * @var \phpbb\request\request_interface
    */
    protected $request;

    /**
    * Constructor
    *
    * @param    \phpbb\config\config               $config
    * @param    \phpbb\request\request_interface   $request
    */
    public function __construct(\phpbb\config\config $config, \phpbb\request\request_interface $request)
    {
        $this->config = $config;
        $this->request = $request;
    }

    /**
    * {@inheritdoc}
    */
    public function get_service_credentials()
    {
        return array(
            'key'     => $this->config['auth_oauth_bitly_key'],
            'secret'  => $this->config['auth_oauth_bitly_secret'],
        );
    }

    /**
    * {@inheritdoc}
    */
    public function perform_auth_login()
    {
        if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Bitly))
        {
            throw new \phpbb\auth\provider\oauth\service\exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
        }

        // This was a callback request from bitly, get the token
        $this->service_provider->requestAccessToken($this->request->variable('code', ''));

        // Send a request with it
        $result = json_decode($this->service_provider->request('user/info'), true);

        // Return the unique identifier returned from bitly
        return $result['data']['login'];
    }

    /**
    * {@inheritdoc}
    */
    public function perform_token_auth()
    {
        if (!($this->service_provider instanceof \OAuth\OAuth2\Service\Bitly))
        {
            throw new \phpbb\auth\provider\oauth\service\exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
        }

        // Send a request with it
        $result = json_decode($this->service_provider->request('user/info'), true);

        // Return the unique identifier returned from bitly
        return $result['data']['login'];
    }
}

The Service File

In the service file, the name of the service must be in the form of auth.provider.oauth.service.<service name> in order for phpBB to properly recognise it.

services:
    auth.provider.oauth.service.bitly:
        class: phpbb\auth\provider\oauth\service\bitly
        arguments:
            - '@config'
            - '@request'
        tags:
            - { name: auth.provider.oauth.service }