How to respond in Middleware Slim PHP Framework

4.3k views Asked by At

I am creating middleware for auth into REST API. My API is created using Slim PHP Framework, which in case provides great features to build APIs. One of these features is Middleware.
I need to check credentials in Middleware and respond with an error (HTTP code with JSON descriptions) to the user.
But unfortunately, Slim Framework gives me an exception whenever I try to halt and respond with the HTTP code.

<?php
require_once __DIR__.'/../Slim/Middleware.php';
class TokenAuth extends \Slim\Middleware {
    private $auth;
    const SECURED_URI_REGEX = "/^\/v\d\/store\/(orders|users|payment).*/";
    const TOKEN_PARAMETER = "token";
    const USER_EMAIL_PARAMETER = "user_email";
    public static $credentialsArray = array(TokenAuth::TOKEN_PARAMETER,TokenAuth::USER_EMAIL_PARAMETER);
    public function __construct() {  
    }
    public function deny_access() {
       print Response::respondWithHttpStatus($app,401,true);
    }
    public function call() {
         $app = $this->app;  
        $uri = $app->request->getResourceUri();
        if (preg_match(TokenAuth::SECURED_URI_REGEX, $uri)) {
             $tokenAuth = $app->request->headers->get('Authorization'); 
             if(isset($tokenAuth)) {
             $parsedCredentials = TokenAuth::parseAndValidateCredentials($tokenAuth); 
             if (!$parsedCredentials) {
                 Response::respondWithHttpStatus($app,401,true);
             }
             else {
                $auth = new Authenticator($parsedCredentials[TokenAuth::USER_EMAIL_PARAMETER],$app);
                print $auth->userHasToken();
             }
         }
         else {
             Response::respondWithHttpStatus($app,400,true);
         }
        }
        else {
             $this->next->call();
        }

    }

respondWithHttpStatus method uses slim framework method $app->halt($code, $response);

In this case, when I try to execute this method I get an Exception from

Slim Framework 
The application could not run because of the following error:

Details

Type: Slim\Exception\Stop
File: /var/www/api/Slim/Slim.php
Line: 1022

How to deal with this problem.
My goal is to control user credentials in middleware and if it is something wrong respond with the appropriate HTTP code and JSON message describing the reason of the error.
Maybe it is better to follow another way.
Please suggest.

ONE POSSIBLE WORKAROUND

   $app->response->setStatus(400);
   $app->response->headers->set('Content-Type', 'application/json');
   print Response::respondWithHttpStatus($app,400,false);

And respond function

public static function basicRespond($app,$code,$message,$halt) {

    if(!isset($message) || empty($message)) {
        $message = Response::$RESPONSE_MAP[$code];
    }
    $response = json_encode($message);
    if($halt===true) {
        $app->halt($code, $response);
    }
    else {
        return $response;
    }
}

For my needs suits well, also throwing an exception can be another solution but in my case, I don't need to continue, just setting the header, code and don't call next - works for me.

1

There are 1 answers

0
alexw On BEST ANSWER

You cannot use halt in middleware:

https://stackoverflow.com/a/10201595/2970321

Halt should only be invoked within the context of a route callback.

Instead, you could manually generate a 400 response using PHP's header along with exit:

header("HTTP/1.1 400 Access denied");
exit;

Alternatively,

you could define a new type of Exception:

class AuthException extends Exception {
    public function __construct() {
        $message = 'You must authenticate to access this resource.';
        $code = 400;
    }
}

Catch this in your error route:

$app->error(function (\Exception $e) use ($app) {
    // Example of handling Auth Exceptions
    if ($e instanceof AuthException) {
        $app->response->setStatus($e->getCode());
        $app->response->setBody($e->getMessage());
    }
});

And throw an AuthException when authorization is to be denied:

throw new AuthException();

This is essentially how it is done in Slim-Auth.