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.
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;
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.