How to authenticate for the whole module level application?

Hi, I am working on how to anthenticate and authorize for my member module in ZF3 project

I worked with zf2 for a while, and remember that in ZF2, we used to do with the following:

in the module.php for the module. we wrote an onBootstrap function like:

    public function onBootstrap(MvcEvent $e)
    {
        $eventManager = $e->getApplication()->getEventManager();
        $eventManager->attach('dispatch', array($this, 'setLayout'),2);
        $eventManager->attach('route', array($this, 'doAuthorization'),3);
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);
    }

    public function doAuthorization(MvcEvent $e)
    {
        $application = $e->getApplication();
        $sm = $application->getServiceManager();
        $sharedManager = $application->getEventManager()->getSharedManager();
        $router = $sm->get('router');
        $request = $sm->get('request');
        $matchedRoute = $router->match($request);
        if (!preg_match('%^/'.__NAMESPACE__.'/.*%i', $request->getUri()->getPath())) return true;
        if (null !== $matchedRoute) {
            $sharedManager->attach('Zend\Mvc\Controller\AbstractActionController','dispatch',
                function($e) use ($sm) {
                    $sm->get('ControllerPluginManager')->get('Appauth')->doAuthorization($e);
                },4
            );
        }
    }

then we wrote the main authorize logic in the controller plugin like:

<?php
namespace Members\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class Appauth extends AbstractPlugin
{	
	public function doAuthorization($e)  //just consider the module members
	{
		$matches = $e->getRouteMatch();
		$controller = $matches->getParam('controller');
		$action = $matches->getParam('action');
		
		$membersTable = $this->getController()->getServiceLocator()->get('Members\Model\MembersTable');
		$userInfo = $membersTable->userInfo();
		$rawUri = strtolower($e->getRequest()->getUri()->getPath()); // /members/index/registration /members/index /members
		$uri = substr($rawUri, 0, strpos($rawUri, $action)).$action;
		$response = $e->getResponse();
		$router = $e->getRouter();
		if ($controller == 'Members\Controller\Index' && $action == 'login') //if it is login page
		{
			if ($userInfo)
			{
				$url = $router->assemble(array('controller'=>'index','action'=>'index'),array('name'=>'members'));
				$response->getHeaders()->addHeaderLine('Location', $url);
				$response->setStatusCode(302);
				$e->stopPropagation(true);
				return $response;
			}
			else 
			{
				return true;
			}
		}
		if ($controller == 'Members\Controller\Index' && $action == 'logout')
		{
			return true;
		}
		if ($controller == 'Members\Controller\Index' && $action == 'register')
		{
			if ($userInfo)
			{
				$url = $router->assemble(array('controller'=>'index','action'=>'index'),array('name'=>'members'));
				$response->getHeaders()->addHeaderLine('Location', $url);
				$response->setStatusCode(302);
				$e->stopPropagation(true);
				return $response;
			}
			else 
			{
				return true;
			}
		}
		if (!$userInfo) //others not logined will redirect to login page
		{
			$url = $router->assemble(array('controller'=>'index','action'=>'login'),array('name'=>'members'));
			$response->getHeaders()->addHeaderLine('Location', $url);
			$response->setStatusCode(302);
			$e->stopPropagation(true);
			return $response;
		}
		else 
		{
			if ($controller == 'Members\Controller\Index' && $action == 'index') return true;
			if ($userInfo['rid'] == 1) return true;
			$resourcesArr = $this->getController()->getServiceLocator()->get('Members\Model\ResourcesTable')->formatResources('iu');
			$sid = array_search($uri,$resourcesArr);
			if (!$sid) //which is not included in resourcelist
			{
				$url = $router->assemble(array('controller'=>'index','action'=>'index'),array('name'=>'members'));
				$response->getHeaders()->addHeaderLine('Location', $url);
				$response->setStatusCode(302);
				$e->stopPropagation(true);
				return $response;
			}
			else 
			{
				if ($this->getController()->getServiceLocator()->get('Members\Model\AclTable')->checkAcl($userInfo['rid'],$sid))
				{
					return true;
				}
				else 
				{
					$url = $router->assemble(array('controller'=>'index','action'=>'index'),array('name'=>'members'));
					$response->getHeaders()->addHeaderLine('Location', $url);
					$response->setStatusCode(302);
					$e->stopPropagation(true);
					return $response;
				}
			}
		}
	}
}

and config the plugin in the moudule.config.php like:

    'controller_plugins' => array(
        'invokables' => array(
            'Appauth' => 'Members\Controller\Plugin\Appauth',
        ),
    ),

I wonder how it looks like in ZF3, when I wrote this new topic, it says my topic is similar to https://discourse.zendframework.com/t/the-right-mvc-event-to-tie-into-for-authorization/614, I am not well fit this way. I think it looks like yii framework, besides, It is a little complicate to me. I am seeking a way which is similar to the code I pasted above. Is there any ZF3-patterned one like the code above?

I prefer to write the main logic in one file with acl.

Thank you for your time.

This article on zf-mvc-auth may be helpful: https://blog.tomhanderson.com/2017/10/using-zf-mvc-auth-for-custom.html

Hi TomHAnderson, Thank you for taking your time.

I am not familar to Doctrine, in fact I have never used Doctrine. if I just use the tablegateway like the zf3 office demo, what will the code look like? I can figure out you are very good at configuration. I am at very low level in configuration the zf3.

Hi jobsfan

the zf3 logic is quite the same

on Module.php::onBootstrap, I attach an event listener (note: on Dispatch event. the dispatch event occurs after bootstrap and after route events so the route will be available)

    $eventManager = $mvcEvent->getApplication()->getEventManager();
    $sharedEvents = $eventManager->getSharedManager();

    // we only check login status for the current namespace (note the high priority) :
    $sharedEvents->attach('Zend\Mvc\Controller\AbstractActionController',
        MvcEvent::EVENT_DISPATCH, array($this, 'checkLoggedStatus'), 1000);

the checkLoggedStatus will then make use of the authentication service the :

    $authService = $sm->get('Zend\Authentication\AuthenticationService');
    if (!$authService->hasIdentity()) {...  

Hope this helps

Have a good look at https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/User_Management__Authentication_and_Access_Filtering.html There’s a lot of good information there which hopefully you will find helpful.

Hi jcaillot,
Thanks for your reply!

I do not just want to check the user logined or not, also I need use the acl component to authorized via url(by module, controller,action), you know that. I do not want to write the acl logic in the module.php, I thought there might by a suit place the logic, I do not know whether controller plugin is the best one, but I just found this demo from the internet. If there are any other place to set the acl logic, pls let me known. I can not really clear figure out the differences between model, service, or controller plugin, etc. I guess it might be a habit when structure the code, or there are some difference on the progress sequence, some execute earlier and some later, if you know, pls tell me.

Thank you again.

hello davidmintz,
Thanks for your reply.

I mentioned the olegkrivtsov.github.io in my topic. unfortunately, I am not familiar to doctrine stuff, in fact, I have never used doctrine. of course I will learn later when I have time, but for now, I need to push the project to go on. Yesterday I cloned the sample code on the github of the website you recommend.
QQ%E6%88%AA%E5%9B%BE20181211093658
Sorry that, I do not well understand it, and as well, I did not make it run on my computer. I think my php level is not enough for me to use this manner.

Anyway, thanks again for your reply.

ugh, my apologies for not noticing what you said about Doctrine.

@ jobsfan

the best place for an ACL logic would e IMHO in a service.
A service is basically a class that the service container (aka service manager) instantiates (an injects as method param) when needed. In ZF3 pretty everything is a service. When one uses the word it more precisely means a custom class (and is factory) declared under the service manager key in config :

‘service_manager’ => [
‘factories’ => [

         'Application\My\ACL\Provider'    => 'Application\My\ACL\ ProviderFactory',

@jcaillot thanks for your advice, I also think place the acl logic in service is much better than plugin. I will try. Thank you again.

you can find an example with tableGateway here, GitHub - ozzpy/zf-expressive-db-tutorial-stackoverflow