enoratp enoratp - 1 month ago 8
PHP Question

Unable to make Google OAuth2 work on CakePHP 3 app

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 :

AppController.php :



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'
]
]);
}


UsersController.php



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

paths.php



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!)

Answer

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']);