Simple webservice authentication using cakephp. A closer look.

After bringing up a very simple way to get authentification done in an older post, it is time to give it a closer look and bring up some code that will help to get things done.

I use one single controller to handle all external requests the webservice could receive, i do not know if that is “best practice”, but it seems reasonable
to me at this point. All responses are JSON, so i simply named the controller “json_controller“… 😉

Of course the user must log in first. For that he makes a request on “/json/login“.
The process is pretty much standard, the user logs in with his credentials as in the normal web application.
For this (my first) approach it was important to make it work before i start with (at this point) unnecessary security stuff.

public function login () {
	if($this->request->is('post')) {
		$post['username'] = $this->request->data['username'];
		$post['password'] = $this->request->data['password'];
		$post['android_registration_id'] = $this->request->data['reg_id'];
		$data['hash'] = $this->Auth->password($post['password']);
		$check = $this->User->find('first',
			array(
				'conditions' => array(
					'username' => $post['username'],
					'password' => $data['hash']
				)
			)
		);
		$save = array();
		$return = array();
		if($check) {
			$save['id'] = $check['User']['id'];
			$save['token'] = $this->Auth->password($post['username'].date('dmY'));
			$save['android_registration_id'] = $this->request->data['reg_id'];
			$save['last_mobile_login'] = date('Y-m-d H:i:s');
			if($this->User->save($save)) {
				$return['return']['token'] = $save['token'];
				$return['return']['hash'] = $data['hash'];
				$return['return']['id'] = $check['User']['id'];
				$return['return']['username'] = $check['User']['username'];
			} else {
				$return = false;
			}
		} else {
			$return = false;
		}
	}
	return new CakeResponse(array('body' => json_encode($return, JSON_NUMERIC_CHECK)));
}

The credentials will be checked against the request and the necessary data will be given back to the request source. As you can see i am saving the additional information (android_registration_id) already, so extending the code will be done quickly.
The returned JSON object will contain the token and the hash that will be needed for any further requests on the webservice and needs to be saved in the app on the device.

To make any request on the webservice we need to check its credentials first. I do that through a method that is called out of the app_controller on every request.

/* --- app_controller --- */
$authed = false;
// you might put some standard authentication for browser use here
if($authed == false) {
	if($this->request->is('post') && $this->request->data('token') && $this->request->data('hash')) {
		App::uses('User', 'Model');
		$this->User = new User;
		$this->mobile = $this->User->authenticateMobile($this->request->data('hash'),$this->request->data('token'));
	}
}

In the example above the call is made on every request as long the user is not authenticated [$authed == true], this can also be restricted to specific controllers to reduce the requests. Just ask if the controller that is requested needs this process.
As you can see, we do not accept GET requests and do not make a call on the authentication when no token and no hash are delivered.

To do this i created a method in the model User that will just pick up the user data, based on the hash and the token that is created during the first login process.
No requests will be accepted without this data.

public function authenticateMobile($hash,$token) {
	return $this->find('first',
		array(
			'conditions' => array(
				'password' => $hash,
				'token' => $token
			)
		)
	);
}

Now let’s check the json_controller method that responded to a valid request (“/json/messages/“) with the messages a user received in the web application.

public function messages($fromorto = 'to') {
	if($this->mobile != false) {
		$user_id = $this->mobile['User']['id'];
		$data['Messages'] = $this->Message->getMessagesByUserId($user_id, $fromorto);
	} else {
		$data = false;
	}
	return new CakeResponse(array('body' => json_encode($data, JSON_NUMERIC_CHECK)));
}

In the first line in the method, i ask if $this->mobile – which was set in the app_controller – is not false.
That means the request is a POST request with hash and token and brings useful user data (eg: $this->mobile[‘User’][‘id’]), which might be needed for further request.

There are improvements to make on this simple approach, that includes:

  • implementing more specific usage of device related information, e.g. Android and iOS
    making log in procedure safer
  • making post-login authentication more secure
  • converting parts into a cake plugin
  • point-to-point data encryption (long term)

But all of this is here to give you an idea, how get things done in pretty quick.

6 thoughts on “Simple webservice authentication using cakephp. A closer look.

  1. Mohammed

    Hello,
    Thanks for the article. I still have problems, I don’t get the point.
    1- Where is the part that will actually authenticate user and let the cake know he is authenticated and then have access to the site data. in case we have acl implemented, this will cause a problem !.

    2- What is $this->mobile?

    3- In this example, Will the user make the request url in the mobile device to json/message ?

    4- Where this code is actually written ?

    if($this->request->is(‘post’) && $this->request->data(‘token’) && $this->request->data(‘hash’)) {
    App::uses(‘User’, ‘Model’);
    $this->User = new User;
    $this->mobile = $this->User->authenticateMobile($this->request->data(‘hash’),$this->request->data(‘token’));

    }
    inside Beforefilter ?

    5- where is the part that will cancel cake form authentication and pass the user to those actions?

    Sorry for the many questions 🙂

    Reply
    1. alexdd55 Post author

      Hi Mohammed,

      1. The user will be authenticated in the first method that is posted. i did not use ACL in this example, but you could go a different way with not using one controller which responds to requests, but use all already given controllers and use ACL on the necessary methods you created.
        With ACL i would try a different approach to that subject.
      2. in the app_controller it os initialised with

        $this->mobile = false;

        and filled in the second code example.

      3. the user will make the request to the url, and will receive a respond
      4. This code is written in the app_controller, so every request can be authenticated. there could also be a different approach on that, but this seems to me as the easiest way to get a simple webservice done.
      5. there is no form authentication used.
        when we do this:

        $this->mobile = $this->User->authenticateMobile($this->request->data('hash'),$this->request->data('token'));

        we bypass the form authentication and go through a different process

      i hope i could clear up some things on this subject? feel free to ask if you need more answers 😉

      Reply
  2. Steven P

    cakePHP sucks. Why? Their own blog tutorial – ambiguously written – results invariably in error messages:

    Error: PostsController could not be found.
    Error: Create the class PostsController below in file: app\controllers\PostsController.php

    If those idiots cannot write out a simple “hello world” tutorial … one wonders what else they punted on.

    PHP is an old, old friend. codeigniter is not, but at least it works out of the box and is documented by humans rather than imbeciles.

    Reply
  3. Dennis

    Steven it’s not cakePHP that sucks but your power to read and follow a simple tutorial is beyond stupid, CakePHP actually tells you there what you did wrong and you can’t figure out what you did wrong? Lol you must even be more imbecillic than the ones you ought to be imbecils.

    Reply
  4. Vishal Parkash

    I still feels it is not the correct way as the user can manipulate the data using the postman/RestClient at the login time. Just by passing some random values

    Reply
    1. alex Post author

      Thats absolutely right. Also the article is a bit outdated.

      I made it up, because its a quick and simple way, but not the best or safest way to do it.
      It just gives you an idea how you can approach this task.

      Reply

Leave a Reply