Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Route Middleware in Slim 4 doesn't stop invoking the callable in the route

I'm strugling with authorization middleware in Slim4. Here's my code:

$app = AppFactory::create();
$app->add(new Authentication());

$app->group('/providers', function(RouteCollectorProxy $group){
    $group->get('/', 'Project\Controller\ProviderController:get');
})->add(new SuperuserAuthorization());

Authentication middleware checks the user and works fine.

The method get in ProviderController is

public function get(Request $request, Response $response): Response{
    $payload = [];
    foreach(Provider::all() as $provider){
        $payload[] = [
            'id' => $provider->id,
            'name' => $provider->name,
        ];
    }
    $response->getBody()->write(json_encode($payload));
    return $response;
}

The SuperuserAuthorization looks like this

class SuperuserAuthorization{
    public function __invoke(Request $request, RequestHandler $handler): Response{
        $response = $handler->handle($request);
        $authorization = explode(" ", $request->getHeader('Authorization')[0]);
        $user = User::getUserByApiKey($authorization[1]);
        if(! Role::isSuperuser($user)){
            return $response->withStatus(403);//Forbidden
        }
        return $response;
    }
}

The thing is that even though the user is not a superuser, the application continues executing. As a result I get json with all the providers and http code 403 :/

Shouldn't route middleware stop the request from getting into the app and just return 403 right away?

I know that I can create new empty response with status 403, so the data won't come out, but the point is that the request should never get beyond this middleware, am I right or did I just misunderstand something here…

Any help will be appreciated :)

------------- SOLUTION ----------------

Thanks to @Nima I solved it. The updated version of middleware is:

class SuperuserAuthorization{
    public function __invoke(Request $request, RequestHandler $handler): Response{
        $authorization = explode(" ", $request->getHeader('Authorization')[0]);
        $user = User::getUserByApiKey($authorization[1]);
        if(! Role::isSuperuser($user)){
            $response = new Response();
            return $response->withStatus(403);//Forbidden
        }
        return $handler->handle($request);
    }
}
like image 423
mk_coder Avatar asked Jan 14 '20 10:01

mk_coder


1 Answers

Shouldn't route middleware stop the request from getting into the app and just return 403 right away?

Slim 4 uses PSR-15 compatible middlewares. There is good example of how to implement an authorization middleware in PSR-15 meta document. You need to avoid calling $handler->handle($request) if you don't want the request to be processed any further.

As you can see in the example, if the request is not authorized, a response different from the return value of $handler->handle($request) is returned. This means your point saying:

I know that I can create new empty response with status 403, so the data won't come out, but the point is that the request should never get beyond this middleware

is somehow correct, but you should prevent the request from going further by returning appropriate response before invoking the handler, or throwing an exception and let the error handler handle it.

Here is a simple middleware that randomly authorizes some of requests and throws an exception for others:

$app->group('/protected', function($group){
    $group->get('/', function($request, $response){
        $response->getBody()->write('Some protected response...');
        return $response;
    });
})->add(function($request, $handler){
    // randomly authorize/reject requests
    if(rand() % 2) {
        // Instead of throwing an exception, you can return an appropriate response
        throw new \Slim\Exception\HttpForbiddenException($request);
    }
    $response = $handler->handle($request);
    $response->getBody()->write('(this request was authorized by the middleware)');
    return $response;
});

To see different responses, please visit /protected/ path a few times (remember the middleware acts randomly)

like image 87
Nima Avatar answered Oct 22 '22 06:10

Nima