# Cakephp 4 RequestAuthorizationMiddleware

**URL:** <https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365>\
**Category:** Need Help\
**Created:** [May 6, 2022, 3:15am UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365 "2022-05-06T03:15:46Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![EssEss](https://avatars.discourse-cdn.com/v4/letter/e/d9b06d/32.png) [@EssEss](https://discourse.cakephp.org/u/EssEss)\
**Post date:** [May 6, 2022, 3:15am UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/1 "2022-05-06T03:15:46Z")

</div>

I have implemented Authentication plugin and is working fine by itself. I am currently working on the Authorisation plugin. I am using the RequestAuthorizationMiddleware as per [Request Authorization Middleware - 2.x](https://book.cakephp.org/authorization/2/en/request-authorization-middleware.html)

My middleware look like this:

```auto
public function middleware($middlewareQueue): \Cake\Http\MiddlewareQueue
{
	$middlewareQueue->add(new ErrorHandlerMiddleware(Configure::read('Error')))
	        		->add(new AssetMiddleware(['cacheTime' => Configure::read('Asset.cacheTime')]))
					->add(new RoutingMiddleware($this))
	            	->add(new BodyParserMiddleware())
	            	->add(new AuthorizationMiddleware($this, [
		                    'unauthorizedHandler' => [
		                        'className' => 'Authorization.Redirect',
		                        'url' => '/pages/permission',
		                        'exceptions' => [
		                            'MissingIdentityException' => 'Authorization\Exception\MissingIdentityException',
		                            'ForbiddenException' => 'Authorization\Exception\ForbiddenException',
		                        ]
		                    ]
	                	]))
	            	->add(new RequestAuthorizationMiddleware());

	return $middlewareQueue;
}

public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface {
    $mapResolver = new MapResolver();
    $mapResolver->map(ServerRequest::class, RequestPolicy::class);
    return new AuthorizationService($mapResolver);
}

```

RequestPolicy.php

```auto
namespace App\Policy;
use Authorization\Policy\RequestPolicyInterface;
use Cake\Http\ServerRequest;

class RequestPolicy implements RequestPolicyInterface
{
    /**
     * Method to check if the request can be accessed
     *
     * @param \Authorization\IdentityInterface|null $identity Identity
     * @param \Cake\Http\ServerRequest $request Server Request
     * @return bool
     */
    public function canAccess($identity, ServerRequest $request)
    {   
        if ($request->getParam('plugin') === 'DebugKit') {
            return true;
        }

        if(!empty($identity)){
            $userRole = $identity->role;
            $userStatus = $identity->status;
            $action = $request->getParam('action');
            $controller = $request->getParam('controller');

            switch ($controller) {
                case 'Users':
                    if($userRole === 'ADMIN') { 
                        return true; 
                    }
                    elseif($userRole == 'X' || $userRole == 'Y') {
                        if (in_array($action, ['logout','login', ....])) { //need to specify login and logout in this list even though it is declared in the skipAuthorization array in AppController; otherwise logged in users cannot logout.
                            return true;
                        }
                    }
                    break;
                    
                .....
                
            }
            return false;
        }else{
            
            return true; //works as expected only if it is return true
            // return false; //redirects to unauthorized redirect url (/pages/permission) causing redirect loop
        }
    }

```

Currently, authorization is working fine for the logged in users with the following issues:

1. If identity is empty, when canAccess return false, redirects to unauthorized redirect url (/pages/permission) causing redirect loop. However, if I return true, authorization is working as expected.

I read RequestAuthorizationMiddleware would check authorization policies before Authentication kicks in. In that case, is it safe to return true from canAccess when there is no identity? or, is there any other way to solve this?

1. In the AppController before filter, I have declared $this-\>Authorization-\>skipAuthorization(); for all the actions that do not need authentication. This working properly for the nonauthenticated users. However, If an identity exists and is valid, the actions in the skipAuthorization list is still chekcing authorization for the logged in users. So it redirects to unauthorized redirect url (/pages/permission) unless I specify that again in the canAccess method. Why do I need to declare that again even though it is declared in the skipAuthorization array in AppController;

Could anyone please point out what I am doing wrong? Any help is much appreciated.

---

<div class="post-metadata">

**Author:** ![dirk](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/dirk/32/2174_2.png) [@dirk](https://discourse.cakephp.org/u/dirk)\
**Post date:** [May 7, 2022, 3:14pm UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/2 "2022-05-07T15:14:03Z")

</div>

> [@EssEss](#):
>
> If identity is empty, when canAccess return false, redirects to unauthorized redirect url (/pages/permission) causing redirect loop. However, if I return true, authorization is working as expected.

Do you have in your pages controller this:

```auto
    public function beforeFilter(\Cake\Event\EventInterface $event)
    {
        parent::beforeFilter($event);
        // Configure the login action to not require authentication, preventing
        // the infinite redirect loop issue
        $this->Authentication->addUnauthenticatedActions(['display']);
    }

```

---

<div class="post-metadata">

**Author:** ![Zuluru](https://yyz1.discourse-cdn.com/flex029/user_avatar/discourse.cakephp.org/zuluru/32/1230_2.png) [@Zuluru](https://discourse.cakephp.org/u/Zuluru)\
**Post date:** [May 8, 2022, 2:06am UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/3 "2022-05-08T02:06:51Z")

</div>

Why does “/pages/permission” cause a redirect loop? Is it not excluded from authentication?

---

<div class="post-metadata">

**Author:** ![EssEss](https://avatars.discourse-cdn.com/v4/letter/e/d9b06d/32.png) [@EssEss](https://discourse.cakephp.org/u/EssEss)\
**Post date:** [May 12, 2022, 4:18am UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/4 "2022-05-12T04:18:17Z")

</div>

@Zuluru @dirk Thanks for the pointers, redirect loop mentioned in my original post is resolved after excluding ‘display’ from authentication.

However, for nonauthenticated users when tying to access the functions that require authentication, if RequestPolicy/canAccess return false, are still redirected to unauthorized redirect url (pages/permission) instead of unauthenticatedRedirect url (users/login). If RequestPolicy/canAccess return true, everything is working as expected.

I tried to disable the Authorization and RequestAuthorization middlewares from the Application, then Authentication itself is working as expected for nonauthenticated users. i.e, Non authenticated users trying to access a function that requires authentication will be redirected to my unauthenticatedRedirect (users/login).

When I debug (eg. if I try to access Users/dashboard without logging in), the stack trace is as follows,  
Application/middleware → Application/getAuthenticationService → Application/getAuthorizationService → RequestPolicy/canAccess → AppController/initialize → UsersController/beforeFilter → AppController/beforeFilter (IdentityCheck seems to be done after beforeFilter)

Is it safe to return true from RequestPolicy/canAccess function for nonauthenticated users, assuming the identitycheck will kick them back to the unauthenticatedRedirect url? Or is it because I’m doing something wrong? Could anybody please clarify? Thanks in advance.

Edit: I have missed to include the Authentication middleware in my original post. My middleware queue is with Authentication, Authorization and RequestAuthorization middlewares.

```auto
$middlewareQueue->...(other middlewares)
->add(new AuthenticationMiddleware($this))
->add(new AuthorizationMiddleware($this, [
                            'unauthorizedHandler' => [
                                'className' => 'Authorization.Redirect',
                                'url' => '/pages/permission',
                                'exceptions' => [
                                    'MissingIdentityException' => 'Authorization\Exception\MissingIdentityException',
                                    'ForbiddenException' => 'Authorization\Exception\ForbiddenException',
                                ]
                            ]
                        ]))
->add(new RequestAuthorizationMiddleware());

```

I have my $this-\>Authentication-\>allowUnauthenticated([‘login’, ‘logout’, ‘display’…]) in my AppController/initialize.

---

<div class="post-metadata">

**Author:** ![pavia20](https://avatars.discourse-cdn.com/v4/letter/p/a8b319/32.png) [@pavia20](https://discourse.cakephp.org/u/pavia20)\
**Post date:** [October 6, 2023, 2:28pm UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/5 "2023-10-06T14:28:36Z")

</div>

> [@dirk](#):
>
> ` $this->Authentication->addUnauthenticatedActions(['display']);`

Im having the same problem. If identity is empty and I return false I get an authorization error, if i return true it will redirect to login page.

---

<div class="post-metadata">

**Author:** ![EssEss](https://avatars.discourse-cdn.com/v4/letter/e/d9b06d/32.png) [@EssEss](https://discourse.cakephp.org/u/EssEss)\
**Post date:** [October 8, 2023, 11:35pm UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/6 "2023-10-08T23:35:33Z")

</div>

@pavia20 I managed to resolve the issue by returning false when identity is empty, and then use a CustomRedirectHandler middleware to redirect the users according to various conditions.

---

<div class="post-metadata">

**Author:** ![pavia20](https://avatars.discourse-cdn.com/v4/letter/p/a8b319/32.png) [@pavia20](https://discourse.cakephp.org/u/pavia20)\
**Post date:** [October 9, 2023, 8:34am UTC](https://discourse.cakephp.org/t/cakephp-4-requestauthorizationmiddleware/10365/7 "2023-10-09T08:34:20Z")

</div>

Thanks - that worked! Although, I’m now wondering why the exception thrown is a `ForbiddenException` rather than a `MissingIdentityException`. Nevertheless the custom redirect seems to work, but if you have any idea _why_ it works, that would be great 🙂
