Skip to content

Adding New OAuth To Shield OAuth

Shield OAuth supports Google OAuth and GitHub OAuth out-of-the-box and also provides an easy way to connect any server that offers OAuth to it. This guide explains how to achieve this.

Setup Instruction

Command Setup

  1. Run the following command. This command handles steps 1 - 3 of Manual Setup.
php spark make:oauth Example

Note The name of the new OAuth you want to create doesn't need to contain the OAuth suffix. The command will automatically add it to the class name for you.

This command will automatically generate new files ExampleOAuth.php, ShieldOAuthLang.php in the app/Libraries/ShieldOAuth, app/Language/en paths respectively and also update the ShieldOAuthConfig.php file in the app/Config path.

Note The ShieldOAuthConfig.php file must be present in your app/Config path for this command to run successfully. So ensure that you have run the make:oauthconfig command first, as stated here.

  1. Configure the files.
// updated file - app/Config/ShieldOAuthConfig.php
<?php 
public array $oauthConfigs = [
    // ..
    'example' => [
        'client_id'     => 'Get this from the OAuth server',
        'client_secret' => 'Get this from the OAuth server',

        'allow_login' => true
    ],
];
// new file - app/Libraries/ShieldOAuth/ExampleOAuth.php
<?php 

declare(strict_types=1);

namespace App\Libraries\ShieldOAuth;

use Datamweb\ShieldOAuth\Libraries\Basic\AbstractOAuth;

class ExampleOAuth extends AbstractOAuth 
{
    private static $API_CODE_URL = '';
    private static $API_TOKEN_URL = '';
    private static $API_USER_INFO_URL = '';
    private static $APPLICATION_NAME = 'ShieldOAuth';

    protected string $token;
    protected string $client_id;
    protected string $client_secret;
    protected string $callback_url;

    public function __construct(string $token = '')
    {
        // your code here
    }

    public function makeGoLink(string $state): string
    {
        // your code here
        return '';
    }

    public function fetchAccessTokenWithAuthCode(array $allGet): void
    {
        // your code here
    }

    public function fetchUserInfoWithToken(): object
    {
        // your code here
        return json_decode('');
    }

    public function setColumnsName(string $nameOfProcess, object $userInfo): array
    {
        // your code here
        return [];
    }
}

See YahooOAuth example for full code.

Manual Setup

  1. Create a file ExampleOAuth in the app/Libraries/ShieldOAuth path with the following contents. The OAuth suffix is mandatory in creating each new class. For example, if you want to add Yahoo, you should create a file named YahooOAuth.
<?php 

declare(strict_types=1);

namespace App\Libraries\ShieldOAuth;

use Datamweb\ShieldOAuth\Libraries\Basic\AbstractOAuth;

class ExampleOAuth extends AbstractOAuth 
{
    private static $API_CODE_URL = '';
    private static $API_TOKEN_URL = '';
    private static $API_USER_INFO_URL = '';
    private static $APPLICATION_NAME = 'ShieldOAuth';

    protected string $token;
    protected string $client_id;
    protected string $client_secret;
    protected string $callback_url;

    public function __construct(string $token = '')
    {
        // your code here
    }

    public function makeGoLink(string $state): string
    {
        // your code here
        return '';
    }

    public function fetchAccessTokenWithAuthCode(array $allGet): void
    {
        // your code here
    }

    public function fetchUserInfoWithToken(): object
    {
        // your code here
        return json_decode('');
    }

    public function setColumnsName(string $nameOfProcess, object $userInfo): array
    {
        // your code here
        return [];
    }
}

See YahooOAuth example for full code.

  1. Config Setup Add the new OAuth config keys to the app/Config/ShieldOAuthConfig.php file.
<?php 
public array $oauthConfigs = [
    //...
    'example' => [
        'client_id'     => 'Get this from the OAuth server',
        'client_secret' => 'Get this from the OAuth server',

        'allow_login' => true
    ],
    //...
];
  1. Language setup Add the following values to the app/Language/en/ShieldOAuthLang.php file. Create the file if it doesn't exist.
return [
    // ...
    'Example' => [
        'not_allow' => 'Now you can\'t login or register with Example!',
        'example'   => 'Example',
    ],
    // ...
];
  1. Translations Depending on the requirements of your application, you can translate the language file using the same keys, in as many languages as possible. See CodeIgniter docs for more information. Also note that the file name for each language must be ShieldOAuthLang.php.

Available Methods

Your new OAuth file/class has just one requirement. It must extend Datamweb\ShieldOAuth\Libraries\Basic\AbstractOAuth. The abstract class AbstractOAuth implement methods makeGoLink($state), fetchAccessTokenWithAuthCode($allGet), fetchUserInfoWithToken() and setColumnsName(string $nameOfProcess, $userInfo), which should be built according to the documentation of each server.

The AbstractOAuth defines four methods for your usage:

  1. makeGoLink($state) In this method, you need to create a link to transfer the user to the new provider. The output of this method is a string in the form of URL. For example, regarding Yahoo, you can follow the instructions available here to create this link.
  2. fetchAccessTokenWithAuthCode($allGet) In this method, you should try to get the value of access_token according to the code received from the previous method. The output of this method is of void. For Yahoo, you can see the description here. Everything is ready, just replace.
  3. fetchUserInfoWithToken() In this method, you try to receive user information (including first name, last name, email, etc) according to the token code set in the previous step.The output of this method is a object of user info(email, name, ...). See here for more details about Yahoo.
  4. setColumnsName(string $nameOfProcess, $userInfo) In this method, you set the fields received from each service OAuth to be recorded in each column of the table.

YahooOAuth Example Class

<?php

declare(strict_types=1);

namespace App\Libraries\ShieldOAuth;

use CodeIgniter\HTTP\CURLRequest;
use Config\Services;
use Datamweb\ShieldOAuth\Config\ShieldOAuthConfig;
use Datamweb\ShieldOAuth\Libraries\Basic\AbstractOAuth;
use Exception;

class YahooOAuth extends AbstractOAuth
{
    // https://developer.yahoo.com/oauth2/guide/flows_authcode/#refresh-token-label
    private static string $API_CODE_URL      = 'https://api.login.yahoo.com/oauth2/request_auth';
    private static string $API_TOKEN_URL     = 'https://api.login.yahoo.com/oauth2/get_token';
    private static string $API_USER_INFO_URL = 'https://api.login.yahoo.com/openid/v1/userinfo';
    private static string $APPLICATION_NAME  = 'ShieldOAuth';

    protected string $token;
    protected CURLRequest $client;
    protected ShieldOAuthConfig $config;
    protected string $client_id;
    protected string $client_secret;
    protected string $callback_url;

    public function __construct(string $token = '')
    {
        $this->token  = $token;
        $this->client = Services::curlrequest();

        $this->config        = config('ShieldOAuthConfig');
        $this->callback_url  = base_url('oauth/' . $this->config->call_back_route);
        $this->client_id     = env('ShieldOAuthConfig.yahoo.client_id', $this->config->oauthConfigs['yahoo']['client_id']);
        $this->client_secret = env('ShieldOAuthConfig.yahoo.client_secret', $this->config->oauthConfigs['yahoo']['client_secret']);
    }

    public function makeGoLink(string $state): string
    {
        $yahooURL= self::$API_CODE_URL."?response_type=code&client_id={$this->client_id}&redirect_uri={$this->callback_url}&state={$state}";

        return $yahooURL;
    }

    public function fetchAccessTokenWithAuthCode(array $allGet): void
    {

        $client = \Config\Services::curlrequest();
        try {
            //send request to API URL
            $response = $client->request('POST', self::$API_TOKEN_URL, [
                'form_params' => [
                        'client_id'     => $this->client_id ,
                        'client_secret' => $this->client_secret ,
                        'redirect_uri'  => $this->callback_url,
                        'code'          => $allGet['code'],
                        'grant_type'    => 'authorization_code'
                ],
                'http_errors' => false,
            ]);

        } catch (Exception $e) {
            die($e->getMessage());
        }

         $token = json_decode($response->getBody())->access_token;
         $this->setToken($token);
    }

    protected function fetchUserInfoWithToken(): object
    {
        // send request to API URL
        try {
            $response = $this->client->request('GET', self::$API_USER_INFO_URL, [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->getToken(),
                ],
                'http_errors' => false,
            ]);
        } catch (Exception $e) {
            die($e->getMessage());
        }

        return json_decode($response->getBody());
    }

    protected function setColumnsName(string $nameOfProcess, object $userInfo): array
    {
        if($nameOfProcess === 'syncingUserInfo'){
            $usersColumnsName = [
                $this->config->usersColumnsName['first_name'] => $userInfo->given_name,
                $this->config->usersColumnsName['last_name']  => $userInfo->family_name,
                $this->config->usersColumnsName['avatar']     => $userInfo->picture,
            ];
        }

        if($nameOfProcess === 'newUser'){
            $usersColumnsName = [
                'username'                                    => $userInfo->nickname,
                'email'                                       => $userInfo->email,
                'password'                                    => random_string('crypto', 32),
                'active'                                      => $userInfo->email_verified,
                $this->config->usersColumnsName['first_name'] => $userInfo->given_name,
                $this->config->usersColumnsName['last_name']  => $userInfo->family_name,
                $this->config->usersColumnsName['avatar']     => $userInfo->picture,
            ];
        }

        return $usersColumnsName;
    }
}