Search code examples
phpauthenticationcontrollerzend-framework2actiondispatch

Zend Framework 2 - Check for authentication in abstract controller -> onDispatch?


I am re-writing the authentication process for my application running under ZF2.

I need to have more options to authenticate the user depending on the service called.

I.E. for web application accessed from browser I will authenticate via Ldap and for API services I will check the user credentials in the header.

I created an abstract controller checking if the user is authenticated; if not it will be redirected to login page.

All the controllers in the modules needing the same authentication process will extend this class.

I need to save the original request to redirect it back to it after successful login.

My questions are:

1. Is the abstract controller -> onDispatch() method the right place to place it?

Every solution I found around was always doing it in the Module.php. To distinguish the auth method they need to check if the requested controller match, since Module.php is called always. Isn't it 'cleaner' to set it in the controller?

2. Should I use redirect or forward to pass from original controller to login controller and then back?

I don't mind the url changing in the browser bar, just looking for the best and fastest solution keeping also original request.

3. Is it correct to store the uri in the session class ( from the auth module)? Is there any way to conserve the whole request (including maybe the POST data in case needed)?

Here is the abstract controller:

abstract class AbstractAuthActionController extends AbstractActionController {

    public function onDispatch(MvcEvent $e) {
        $serviceManager = $e->getApplication ()->getServiceManager ();
        $auth = $serviceManager->get ( 'LdapAuth\Client\Ldap' );
            if (! $auth->hasIdentity ()) {
                $uri = $e->getRequest()->getRequestUri();
                $callBackFunction = $this->getLdap ()->getCallBackFunction (); // = new SessionData();
                $callBackFunction::setOriginalUri($uri); // function to store temporarly the uri
                return $this->redirect ()->toRoute ( 'ldap-login-route' );
        } else {
            return parent::onDispatch ( $e );
        }
    }
}

Solution

    1. A lot of people do that because they want to take care of checking authentication before the controller dispatch event. Authentication can be checked much earlier in the process, for example on route event, or at least before the controller has been instantiated (dispatch with higher priority then controller).

      In case the user is unauthenticated you want to respond with a 401 (unauthorized) or 403 (forbidden) or a 302 (moved temporarily) response (read some more background on this status code here on Wiki) as early as possible to prevent all the overhead which only keeps your server (unnecessarily) occupied and thus slows down your application and delays the unauthenticated response.

      module.php is NOT the best place to add all the authentication related code. Better would be to create an authentication listener (and inject a authentication service in the listener) and only connect the listener in your module.php.

    2. Read on the difference between redirect and forward here in this answer. If want to redirect the client that it is not properly authenticated in a response with a 302 status code you will need to send a redirect response including this status code. I also see people using forward in such cases, but in my opinion it is not correct, because the client won't get notified about any redirection. You could also check authentication modules like ZfcUser to see how they handle this.

    3. You don't need to store this url on the server, you can send the url you want to go to after logging in (the original url) inside the redirect response. For example you redirect to login.php from a request targeted at profile.php, then your redirect url could look like this:

      http://www.example.com/login.php?redirect=profile.php

      You can now set the redirect inside the login process/controller so that after a successful login you return the client to to profile.php.