First of all, thanks to anyone who will look into this with me. Let me just say that I am a CakePHP beginner. Although my website is functional, it is relatively simple thus I did not gain much knowledge of the framework from developing it. Let's say, I'm a basic user with a less basic problem...!
So, I'm currently developing a website with AngularJS and CakePHP 3. The CakePHP part is a REST API, and of course, the Angular, the client side of the website.
Some of the pages should only be accessed by registered/logged in users, or at least users which email matches @mydomain.com (who then should be registered).
At fisrt, the API/site were designed to deal with this through HTTP Basic authentication but two days ago I got asked to deal with it through a Google OAuth2 Authentication.
So I've tried looking around if anyone had done that on CakePHP3 yet, without a plugin (someone mentioned to me the CakeDC/users plugin, but the documentation is so poor that I didn't go there...). I found these :
http://caketuts.key-conseil.fr/index.php/2015/05/22/integrer-lapi-oauth2-de-google-avec-cakephp-v3/ (in French, sorry, but the code is pretty clear)
http://blog.jainsiddharth21.com/2013/04/29/login-with-google-in-cakephp/ (which is understandable but not really what I chose to do, but still usefull and close to my code)
Although my code is pretty much 90% like this first link, I can't seem to get the Authentication working the way it is supposed to.
Here is my code :
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => 'Users',
'action' => 'googlelogin',
],
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'authError' => __("You don't have rights for this page"),
'authorize' => ['Controller'],
'unauthorizedRedirect' => [
'controller' => 'Users',
'action' => 'forbidden'
],
'loginRedirect' => [
'controller' => 'myHomePage',
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'googlelogin'
]
]);
}
public function googlelogin() {
$client = new Google_Client();
$client->setClientId(GOOGLE_OAUTH_CLIENT_ID);
$client->setClientSecret(GOOGLE_OAUTH_CLIENT_SECRET);
$client->setRedirectUri(GOOGLE_OAUTH_REDIRECT_URI);
$client->setScopes(array(
"https://www.googleapis.com/auth/userinfo.profile",
'https://www.googleapis.com/auth/userinfo.email'
));
$url = $client->createAuthUrl();
$this->redirect($url);
}
public function confirmLogin() {
$client = new Google_Client();
$client->setClientId(GOOGLE_OAUTH_CLIENT_ID);
$client->setClientSecret(GOOGLE_OAUTH_CLIENT_SECRET);
$client->setRedirectUri(GOOGLE_OAUTH_REDIRECT_URI);
$client->setScopes(array(
"https://www.googleapis.com/auth/userinfo.profile",
'https://www.googleapis.com/auth/userinfo.email'
));
$client->setApprovalPrompt('auto');
if (isset($this->request->query['code'])) {
$client->authenticate($this->request->query['code']);
$this->request->Session()->write('access_token', $client->getAccessToken());
}
if ($this->request->Session()->check('access_token') && ($this->request->Session()->read('access_token'))) {
$client->setAccessToken($this->request->Session()->read('access_token'));
}
if ($client->getAccessToken()) {
$this->request->Session()->write('access_token', $client->getAccessToken());
$oauth2 = new Google_Service_Oauth2($client);
$user = $oauth2->userinfo->get();
try {
if (!empty($user)) {
if (preg_match("/(@mydomain\.com)$/", $user['email'])) {
$result = $this->Users->find('all')
->where(['email' => $user['email']])
->first();
if ($result) {
$this->Auth->setUser($result->toArray());
$this->redirect($this->Auth->redirectUrl());
} else {
$data = array();
$data['email'] = $user['email'];
$data['first_name'] = $user['givenName'];
$data['last_name'] = $user['familyName'];
$data['socialId'] = $user['id'];
//$data matches my Users table
$entity = $this->Users->newEntity($data);
if ($this->Users->save($entity)) {
$data['id'] = $entity->id;
$this->Auth->setUser($data);
$this->redirect($this->Auth->redirectUrl());
} else {
$this->Flash->set('Logging error');
$this->redirect(['action' => 'login']);
}
}
} else {
$this->Flash->set('Forbidden');
$this->redirect(['action' => 'login']);
}
} else {
$this->Flash->set('Google infos not found');
$this->redirect(['action' => 'login']);
}
} catch (\Exception $e) {
$this->Flash->set('Google error');
return $this->redirect(['action' => 'login']);
}
}
}
I also added the following lines to the file
define('GOOGLE_OAUTH_CLIENT_ID', 'My_client_id');
define('GOOGLE_OAUTH_CLIENT_SECRET', 'My_client_secret');
define('GOOGLE_OAUTH_REDIRECT_URI', 'mylinkto/confirmLogin');
In Chrome's debugging tool, it seems that confimLogin is called (twice by the way) with a valid code as query parameter, and then googlelogin is called. So I end up on the log page every time...
I feel like there must be something I'm missing here. Does anyone have any idea? (Thanks!)
You should allow your login method in Auth component so that action to these method not get redirected again to googlelogin method
$this->Auth->allow(['googlelogin','confirmLogin']);
reacting to your comment about our documentation, we've added a detailed step by step tutorial about how to configure CakeDC/Users to use Google login in CakePHP:
To be able to configure the callbacks in Google dashboard, you'll need to create a virtual host for you application. You don't need a working domain name, you could use something like "mydomain.dev" but Google requires a domain name (no localhost).
composer require cakedc/users:@stable
composer require league/oauth2-google:@stable
Load it from your bootstrap.php file
Plugin::load('CakeDC/Users', ['routes' => true, 'bootstrap' => true]);
Run migrations to add 2 new tables: 'users' and 'social_accounts'
bin/cake migrations migrate -p CakeDC/Users
Load the Component in your src/Controller/AppController.php
public function initialize() { parent::initialize(); // // ... // $this->loadComponent('CakeDC/Users.UsersAuth'); }
Create a new Google application
Once you get your client id and client secret strings, add the following configuration file under /config/users.php, and paste your client id and secret. Trim any blank space in the codes before paste.
// /config/users.php file contents $config = [ 'Users.Social.login' => true, 'OAuth.providers.google.options.clientId' => 'CLIENT_ID_HERE', 'OAuth.providers.google.options.clientSecret' => 'SECRET_HERE', ];
return $config;
Modify your bootstrap.php file to ensure the config file is loaded this way
Configure::write('Users.config', ['users']); //add this line before Plugin::load('CakeDC/Users... Plugin::load('CakeDC/Users', ['routes' => true, 'bootstrap' => true]);
This file will override any configuration key present in the Plugin, you can check the configuration options here Configuration.
Done!
Now you are ready to go to your login page and click "Sign up with Google".