Monday 3 July 2017

HybridAuth : Steps to use this library for social login in CakePHP 3.x

Example of HybridAuth Library for Social Login i.e. Facebook, Google, LinkedIn and Twiter

Requirements:

  •  CakePHP 3.x, If you don't have CakePHP3.x click here to install 
  •  Composer, If you don't have composer click here to install.
  •  Should have User login/Authentication - CakePHP 3.x


Installation:
 Run: composer require --prefer-dist admad/cakephp-hybridauth

Setup:
 Load HybridAuth Plugin into your CakePHP application run below command
 Run: bin/cake plugin load ADmad/HybridAuth -b -r

Or you can add it manually, add below line into your app's config/bootstrap.php file:
 Plugin::load('ADmad/HybridAuth', ['bootstrap' => true, 'routes' => true]);

Configuration:
 Create a file: config/hybridauth.php inside of application folder, add similar to this script:

    use Cake\Core\Configure;

    return [
       'HybridAuth' => [
          'providers' => [
             'Google' => [
                'enabled' => true,
                'keys' => [
                    'id' => '<google-client-id>',
                    'secret' => '<secret-key>'
                ],
                "scope" => 'https://www.googleapis.com/auth/plus.login
                https://www.googleapis.com/auth/plus.profile.emails.read', // These two URL's required for google login
                "approval_prompt" => "force" // Force required here
           ],
           'Facebook' => [
              'enabled' => true,
              'keys' => [
                 'id' => '<facebook-application-id>',
                 'secret' => '<secret-key>'
              ],
              'scope' => 'email, user_about_me, user_hometown' //whatever you want from facebook you need to add it here
           ],
           'LinkedIn' => [
               'enabled' => true,
               'keys' => [
                  'id' => '<linkedin-key>',
                  'secret' => '<secret-key>'
               ],
               "scope"   => "r_basicprofile", // optional if you want to get email Id as well add this in scope:  r_emailaddress
            ],
            'Twitter' => [
                 'enabled' => true,
                 'keys' => [
                    'key' => '<twitter-key>',
                    'secret' => '<twitter-secret>'
                 ],
                 'includeEmail' => true // Only if your app is whitelisted by Twitter Support
             ]
        ],
        'debug_mode' => Configure::read('debug'),
        'debug_file' => LOGS . 'hybridauth.log',
     ]
 ];


Click here for more information about the HybridAuth.

Database:

 Create a table “social_profiles” run below command:
    $ bin/cake migrations migrate -p ADmad/HybridAuth 

Usage:

In AppController’s initialize() method add below script for allowing social login:

# FOR ALLOWING FACEBOOK LOGIN ALSO

parent::initialize();
$this->loadComponent('Auth', [
  'authenticate' => [
    'Form',
    'ADmad/HybridAuth.HybridAuth' => [
      // All keys shown below are defaults
      'fields' => [
        'provider' => 'provider',
        'openid_identifier' => 'openid_identifier',
        'email' => 'email'
      ],

      'profileModel' => 'ADmad/HybridAuth.SocialProfiles',
      'profileModelFkField' => 'user_id',
      'userModel' => 'Users',

      // The URL Hybridauth lib should redirect to after authentication.
      // If no value is specified you are redirect to this plugin's
      // HybridAuthController::authenticated() which handles persisting
      // user info to AuthComponent and redirection.
      'hauth_return_to' => [
        'controller'  => 'MyController',
        'action'      => 'update_status',
        'plugin' => false
      ]
    ]
  ],
  'loginRedirect' => array('controller' => '<controller Name>', 'action' => '<Method name>'),
  'logoutRedirect' => array('controller' => '<controller Name>', 'action' => '<Method name'),
]);

Your UserController’s login method should be similar to this:
    public function login() {
     if ($this->request->is('post') || $this->request->query('provider')) {
       $user = $this->Auth->identify();
       if ($user) {
         $this->Auth->setUser($user);
         return $this->redirect($this->Auth->redirectUrl());
       }
       $this->Flash->error(__('Invalid username or password, try again'));
    }
 }

On your login page you can create links to initiate authentication using required providers. Specify the provider name using variable named provider in query string.

     echo $this->Form->postLink(
         'Login with Google',
          ['controller' => 'Users', 'action' => 'login', '?' => ['provider' => 'Google']]
     );

Setup a method of your UsersTable as callback for the event:
public function initialize(array $config)
{
  parent::initialize($config);

  $this->setTable('users');
  $this->setDisplayField('id');
  $this->setPrimaryKey('id');
  $this->addBehavior('Timestamp');

  $this->hasMany('ADmad/HybridAuth.SocialProfiles');
  \Cake\Event\EventManager::instance()->on('HybridAuth.newUser', [$this, 'createUser']);
}


public function createUser(\Cake\Event\Event $event) {
    // Entity representing record in social_profiles table
    $profile = $event->data()['profile'];

    // Make sure here that all the required fields are actually present

    $user = $this->newEntity(['email' => $profile->email]);
    $user = $this->save($user);

    if (!$user) {
        throw new \RuntimeException('Unable to save new user');
    }

    return $user;
}

Create a Model src/Model/SocialProfilesTable.php and add similar to this:
    namespace ADmad\HybridAuth\Model\Table;
    use Cake\ORM\Table;

   /**
   * HybridAuth Authenticate
   *
   * Licensed under The MIT License
   * For full copyright and license information, please see the LICENSE.txt
   */
   class SocialProfilesTable extends Table
   {
      /**
       * Initialize table.
       *
       * @param array $config Configuration
       * @return void
       */
       public function initialize(array $config)
      {
          parent::initialize($config);
          $this->addBehavior('Timestamp');
          $this->belongsTo('Users');
      }
   }

Any one have questions or doubt please ✋ your hands.

20 comments:

  1. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Hi Hunk,

      Please ask me anything. I am available here for help.

      Thanks,

      Rajendra

      Delete
    2. this blog is very useful .thanks alot rajan kumar

      Delete
  2. how to use logout function for both side.

    $this->HybridAuth->logoutAllProviders();

    ReplyDelete
  3. Hi Chakradhar,
    You can instantiate a Hybrid_Auth class in your logout function and then use the logoutAllProviders method

    // UsersController where have logout method
    public function __construct(Hybrid_Auth $hybridAuth) {
    $this->hybridAuth = $hybridAuth;
    }

    public function logout() {
    $this->HybridAuth->logoutAllProviders();
    }

    // In your routs file (for instance)
    App::bind('Hybrid_Auth', function() {
    return new Hybrid_Auth(array(
    "base_url" => "http:///login/auth",
    "providers" => array (
    "OpenID" => array (
    "enabled" => true
    ),
    "Steam" => array (
    "enabled" => true
    )
    )
    ));
    });

    ReplyDelete
  4. Great tutorial! it will help for us....

    ReplyDelete
  5. sir, if in a users table i have not a 'email'field i am using username how to configure app controller because i have done a hole project i am not change username to email please provide a solution
    this type of error- Column not found: 1054 Unknown column 'Users.email' in 'where clause'

    ReplyDelete
    Replies
    1. Hi Karmveer Singh,
      Can you check below solution for you prob.

      Delete
  6. You need to add this code into your usercontroller: $this->Auth->constructAuthenticate();
    Check below scripts

    AppController.php

    use Cake\Controller\Controller;
    use Cake\Event\Event;
    use Cake\Core\Configure;

    class AppController extends Controller
    {
    public function initialize()
    {
    parent::initialize();
    $this->loadComponent('Flash');
    $this->loadComponent('Auth', [
    'loginRedirect' => [
    'controller' => 'Users',
    'action' => 'index'
    ],
    'logoutRedirect' => [
    'controller' => 'Users',
    'action' => 'login'
    ]
    ]);
    }
    }
    UsersController.php

    use App\Controller\AppController;
    use Cake\Validation\Validation;

    class UsersController extends AppController
    {
    public function login()
    {
    if ($this->request->is('post')) {

    if (Validation::email($this->request->data['username'])) {
    $this->Auth->config('authenticate', [
    'Form' => [
    'fields' => ['username' => 'email']
    ]
    ]);
    $this->Auth->constructAuthenticate();
    $this->request->data['email'] = $this->request->data['username'];
    unset($this->request->data['username']);
    }

    $user = $this->Auth->identify();

    if ($user) {
    $this->Auth->setUser($user);
    return $this->redirect($this->Auth->redirectUrl());
    }

    $this->Flash->error(__('Invalid username or password, try again'));
    }
    }
    }

    ReplyDelete
    Replies
    1. Perfect solution for using email as username

      Delete
  7. CreateUser and updateUser where to place in model or controller

    ReplyDelete
    Replies
    1. Hi Ashish Jain,
      You can check this link for CakePHP 3.x :
      https://book.cakephp.org/3.0/en/tutorials-and-examples/blog-auth-example/auth.html

      Delete
  8. yes it is working on localhost as well

    ReplyDelete
  9. I got an issue. I use Adminlte plugin in cakephp 3.7 for backend login and for front-end user I use HybridAuth. But at Adminlte font icon and images are not showing up. Is there anyone can help me with that please.

    ReplyDelete