If you are using CakePHP 3.4+ checkout my ADmad/cakephp-social-auth instead.
A CakePHP plugin which allows using the HybridAuth social sign on library.
- CakePHP 3.1+.
Run:
composer require --prefer-dist admad/cakephp-hybridauth
Load the plugin by running following command in terminal:
bin/cake plugin load ADmad/HybridAuth -b -r
or by manually adding following line to your app's config/bootstrap.php
:
Plugin::load('ADmad/HybridAuth', ['bootstrap' => true, 'routes' => true]);
Make a config file config/hybridauth.php
:
use Cake\Core\Configure;
return [
'HybridAuth' => [
'providers' => [
'Google' => [
'enabled' => true,
'keys' => [
'id' => '<google-client-id>',
'secret' => '<secret-key>'
]
],
'Facebook' => [
'enabled' => true,
'keys' => [
'id' => '<facebook-application-id>',
'secret' => '<secret-key>'
],
'scope' => 'email, user_about_me, user_birthday, user_hometown'
],
'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',
]
];
For more information about the hybridauth configuration array check http://hybridauth.github.io/hybridauth/userguide/Configuration.html
The plugin expects that you have a users table with at least email
field
and a social_profiles
table. You can run
bin/cake migrations migrate -p ADmad/HybridAuth
to generate the social_profiles
tabel using a migration file provided with
the plugin.
Check the CakePHP manual on how to configure and use the AuthComponent
with
required authentication handler. You would have something like this in your
AppController
's initialize()
method.
$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' => null
]
]
]);
Note: When specifying loginRedirect
and loginAction
URLs for AuthComponent be sure to add
'plugin' => false
(or appropiate plugin name) to the URL array.
Your controller's login action should be similar to this:
public function login() {
if ($this->request->is('post')) {
$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'));
}
}
Note: When your action calls $this->Auth->identify()
the method may not return.
The authenticator may need to redirect to the provider's site to complete the
identification procedure. It's important not to implement any important business
logic that depends upon the identify()
method returning.
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']]
);
We use a POST link here instead of a normal link to prevent search bots and other crawlers from following the link. (Adding "nofollow" attribute to link doesn't suffice as it's often ignored by bots/crawlers.)
Once a user is authenticated through the provider the authenticator gets the user
profile from the identity provider and using that tries to find the corresponding
user record in your app's users table. If no user is found emits a HybridAuth.newUser
event. You must setup a listener for this event which save new user record to
your users table and return an entity for the new user. Here's how you can setup
a method of your UsersTable
as callback for the event.
If you also want to monitor all logins - and execute e.g. a login counter - you can listen for the HybridAuth.login
event.
public function initialize(array $config)
{
$this->hasMany('ADmad/HybridAuth.SocialProfiles');
\Cake\Event\EventManager::instance()->on('HybridAuth.newUser', [$this, 'createUser']);
\Cake\Event\EventManager::instance()->on('HybridAuth.login', [$this, 'updateUser']);
}
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;
}
public function updateUser(\Cake\Event\Event $event, array $user)
{
$this->updateAll(['logins = logins + 1', 'last_login' => new FrozenTime()], ['id' => $user['id']]);
}
Additionally, you can also get a flash message for login back using the HybridAuth.login
event:
// In your AppController
public function initialize()
{
EventManager::instance()->on('HybridAuth.login', [$this->MyComponent, 'updateUser']);
}
// In your MyComponent
public $components = [
'Flash'
];
public function updateUser(Event $event, array $user)
{
$this->Flash->success(__('You are now logged in'));
}
If you are trying to achieve a 'Sign in using Twitter' functionality, and you require the users email address, you need to specifically get your application white-listed by Twitter Support using this form and selecting 'I need access to special permissions'. Then you can use the 'includeEmail' => true
configuration option.
Copyright 2016 ADmad