CakePHP 3 login with AngularJs — Part 1

CakePHP REST

Jan Ranostaj
Aug 4, 2015 · 4 min read

Description

I’m going to create basic login system using CakePHP with AngularJs. It is just a simple application only to login and logout the user.

This first part will cover creating CakePHP REST.

The most common problem you might face when creating SPA with CakePHP and AuthComponent is autoRedirect configuration which causes problems every time user access restricted controller action.

Explanation

When user hits restricted action or controller the AuthComponent redirect user to loginAction (/login — by default) which does not return 401/403 status code by default.

Application just tries to render the login form instead. But what we want here is the application to return JSON with status code 401/403

So if your Javascript application is waiting to catch 401/403 in your callback functions, you might not get it.

Let’s get started…

Application setup

First thing first. Let’s create some basic application structure we are going to use in this example. You might have your own best practices structure. This one is just for demo purposes.

Folders structure

These are files used in this demo

www/
/js
/controllers/
/login-controller.js
/factories
/login-factory.js
/auth-interceptor-factory.js
/main.js
/router.js
/css
/api <- folder where our CakePHP application resides
/config/
/routes.php
/src/
/Controller
/LoginController.php
/UsersController.php
/AppController.php
/PagesController
/Model
/Table
/UsersTable.php
/Entity
/User.php
/index.html

API Routes

We should start first by API routes design. So we know how to call our backed application

USER Routes

For accessing user info within restriced area

LOGIN Routes

For login and logout purposes

PAGES Routes

These are used just for redirecting user when accessing forbiden action or is not authenticated

Routes table

These are routes our app will handle

AppController

We should define our main App Controller first.

AuthComponent setup is quite easy, what I notice here is the loginAction, pointing to unauthorized page action instead of login page, why?

Every time you try to access action within restricted area CakePHP forces you to redirect to login action configured in AuthComponent.

I have tried configure Auth to disable this default action like these, but did not worked:

$this->Auth->autoRedirect = false; // did not work
$this->Auth->unauthorizedRedirect = false; // dit not work

So I decided for this simple solution to send all unauthenticated users to unauthorized action which returns 401 so can be easily handled by Angular application:

$this->loadComponent('Auth', [
'loginAction'=>[
'controller'=>'Pages',
'action'=>'unauthorized',
'_ext'=>'json'],
'authorize'=>['Controller'],
'authError'=>"Error"
]);

Before filter

Just basic logic, to assign logged user_id to our application and check user token, if token is invalid we log out user and respond with 403 error

public function beforeFilter(Event $event)
{
$this->user_id = $this->Auth->user('id'); // validate user token
if($this->user_id)
{
if(!$this->checkUserToken())
{
$this->Auth->logout(); // logout user
throw new ForbiddenException("Invalid Token!");
} }
}

Check user token

Just for security reasons we should check if the request token equals user session token

public function checkUserToken()
{
$request_token = $this->getRequestToken();
if (!$request_token) {
return false;
}
if ($request_token != $this->userToken()) {
return false;
}
return true;
}

Request token

This is where we grab our token send along every request to the application

public function getRequestToken()
{
$headers = $this->getHeaders();
if (!isset($headers['Authorization'])) return false;
$token = explode(" ", $headers['Authorization']);
return $token[1];
}

Complete AppController

LoginController

Now lets take a look at login process in login controllers actions.

Since our API login routes are api/login for login user we are going to send request using POST method which points to INDEX action

Index Action

Index action requires some logic I walk you through.

First check for username and password request variables

if(!isset($this->request->data['username'])){
throw new UnauthorizedException("Please enter your username");
}
if(!isset($this->request->data['password'])){
throw new UnauthorizedException("Please enter your password");
}
$username = $this->request->data['username'];
$password = $this->request->data['password'];

If everything is OK let’s check for user credentials

$user = $this->User->find('login',['username'=>$username, 'password'=>$password]);if(!$user) {
throw new UnauthorizedException("Invalid login");
}

Set User Token

// Generate user Auth token
$token = Security::hash($user->id.$user->username, 'sha1', true);
// Add user token into Auth session
$this->request->session()->write('Auth.User.token', $token);
// return Auth token
$this->response->header('Authorization', 'Bearer ' . $token);

Response

$this->set('user', $this->Auth->user());$this->set('_serialize', ['user']);

Complete LoginController

Pages Controller

In Pages Controller we define methods which returns 401/403 errors.

This methods are used to configure AuthComponent loginAction

UsersTable

As last thing create User Model and Entity. In UsersTable we only create public FindLogin method.

User Entity

Next

Check Part 2 on how implementing AngularJS
Thank’s for reading :)

Jan Ranostaj

Written by

Senior AngularJS engineer at Accenture

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade