3
votes

I am having trouble implementing a REST API authentication for mobile in CakePHP 3.

Reading the Authentication official CakePHP 3 documentation:

http://book.cakephp.org/3.0/en/controllers/components/authentication.html#id1

It said that I can use Stateless Basic Authentication instead of Form Authentication. However, I also read in one of the forums that you cannot use both Basic and Form Authentication at the same time.

I also researched on using prefix route for REST API.

http://www.bravo-kernel.com/2015/04/how-to-prefix-route-a-cakephp-3-rest-api/

By doing so, I can now perform the Basic Authentication to the prefix route and the Form Authentication to the non-prefix route. However, this requires controllers to be defined in its prefix namespace which violates CakePHP's concept of keeping your code DRY; as my controllers already consist logic catering for web and mobile devices.

If I were to just use Form Authentication, I can just create a POST request from my mobile to the web system but the response would be in HTML format. It would not be a good approach to parse the entire HTML format.

I am stuck and do not know what to do. I kept on researching the entire afternoon and still failed in finding a solution.

All I want is to be able to authenticate through my mobile app while still using the same controllers.

EDIT: Workaround

class AppController extends Controller
{
    public function initialize()
    {
        parent::initialize();
        $this->loadComponent('Auth', [
            'authorize' => 'Controller',
            'authenticate' => [
                'Form' => [
                    'fields' => [
                        'username' => 'username',
                        'password' => 'password'
                    ]
                ]
            ],
            'loginAction' => ['controller' => 'Users', 'action' => 'login'],
            'unauthorizedRedirect' => $this->referer()
        ]);
    }

    public function beforeFilter(Event $event)
    {
        if($this->request->params['_ext'] === 'json')
        {
            $this->Auth->config('authenticate', ['Basic' => ['userModel' => 'Users']]);
            $this->Auth->config('storage', 'Memory');
            $this->Auth->config('unauthorizedRedirect', false);
        }
        return parent::beforeFilter($event);
    }
}

Managed to make it work by conditionally adding Basic Authentication to the authenticate configuration.

However, this is not the perfect solution as it certainly has pitfalls.

This will only work under the assumptions that:

  1. Referring to this post: CakePHP will do a priority based on the order of authentication schemes. Since the Form Auth came before the Basic Auth, the Basic Auth will not execute if the Form Auth is successful.

    CakePHP 2.1 - As a web application and REST service with Authentication

  2. Normally, web users will not visit URL with the file extension of json, so the user will be redirected to the user login page using Form Authentication.

  3. Once the user is authenticated through Form Authentication, the Basic Authentication won't pop up even if the user will indirectly visit URL ending with .json using AJAX.

Is there any better way?

1
did you find a better solution? I think here is the answer book.cakephp.org/3.0/en/controllers/components/… - rrd

1 Answers

0
votes

you can do this by using basic authentication.for web you have to use form authentication and for REST API you have to use basic authentication.and you can use both at a time.i have implemented it already below is the working example.

   public function initialize() {
        parent::initialize();
        $this->loadComponent('RequestHandler');
        $this->loadComponent('Flash');
        $url = $this->request->url;
        $ext = $this->request->params['_ext'];
        if ((isset($ext) && $ext == 'json') && (!($this->request->is('ajax')) && ($url != 'users/loginapi.json')) ) {    
            $this->loadComponent('Auth', [
                'authenticate' => [
                    'Basic' => [
                        'fields' => ['username' => 'email', 'password' => 'api_key'],
                        'userModel' => 'Users',
                        'scope' => ['status' => 'A']
                    ],
                ],
                'storage' => 'Memory',
                'unauthorizedRedirect' => false
            ]);
        } else {
            $this->loadComponent('Auth', [
                'authenticate' => [
                    'Form' => [
                        'fields' => ['username' => 'email_or_mobile', 'password' => 'password'],
                        'userModel' => 'Users',
                        'scope' => ['status' => 'A'],
                        'finder' => 'auth'
                    ]
                ],
                'storage' => 'Session',
                //'unauthorizedRedirect' => false,
                'loginRedirect' => [
                    'controller' => 'Users',
                    'action' => 'index'
                ],
                'logoutRedirect' => [
                    'controller' => 'Users',
                    'action' => 'login'
                ],
                'authError' => false
            ]);
        }
    }