# Auth & Preflight Middleware

**URL:** <https://discourse.slimframework.com/t/auth-preflight-middleware/5370>\
**Category:** Questions\
**Created:** [September 11, 2022, 3:15pm UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370 "2022-09-11T15:15:48Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![FOUK](https://avatars.discourse-cdn.com/v4/letter/f/a5b964/32.png) [@FOUK](https://discourse.slimframework.com/u/FOUK)\
**Post date:** [September 11, 2022, 3:15pm UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370/1 "2022-09-11T15:15:48Z")

</div>

Hi All

I hope someone can help, I am struggling to add some middleware (I think)  
I have created the below api via the docs and various tutorials that talks to a mySQL & SQL server database.  
**Problems:** I am currently repeating a lot of code and wondering if anyone knows how I can prevent it.

**Problem 1 (Auth)**: See **CompanyController.php** & **Validator.php**  
For every function in a controller I am writing the same auth logic and hope someone could help prevent that.  
I have seen and attempted to add middleware to a group but cannot get anything to work.  
I would like to have on a group or single route an authorization check: See **Validator.php** below.

**Problem 2 (Preflight)**: See **Routes.php** & **PreFlightController.php**  
For every route I am having to to add an options route so it works through a browser, Is there a way of adding this PreFlightController to all routes in the $app?

I hope I have made sense, any help would be very much appreciated.  
(I have added my questions in the code comments)

Sorry its a lot.

Thanks

> \*\*MAIN API CODE BELOW \*\* (Questions prefixed with //???..)

**bootstrap.php**

```auto
<?php
date_default_timezone_set('Europe/London');
use DI\Container;
use DI\Bridge\Slim\Bridge as SlimAppFactory;

require_once __DIR__.'/../vendor/autoload.php';
require_once __DIR__.'/../vendor/paragonie/sodium_compat/autoload.php';

$container = new Container();

$settings = require_once __DIR__.'/settings.php';
$settings($container);

$app = SlimAppFactory::create($container);
$app->setBasePath('/api');

$middleware = require_once __DIR__. '/middleware.php';
$middleware($app);

$routes = require_once __DIR__.'/routes.php';
$routes($app);

$app->run();

```

**settings.php**

```auto
<?php
use Psr\Container\ContainerInterface;

return function (ContainerInterface $container)
{
  $container->set('settings',function()
  {
    $db = require __DIR__. '/database.php';

    return [
        "db"=>$db
    ];
  });
};

```

**middleware.php**

```auto
<?php
use Slim\App;
use App\Response\CustomResponse;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Routing\RouteContext;

return function (App $app) {
    $app->getContainer()->get('settings');
    $app->addBodyParsingMiddleware();

    // This middleware will append the response header Access-Control-Allow-Methods with all allowed methods
    $app->add(function (Request $request, RequestHandlerInterface $handler): Response {
        $routeContext = RouteContext::fromRequest($request);
        $routingResults = $routeContext->getRoutingResults();
        $methods = $routingResults->getAllowedMethods();
        $requestHeaders = $request->getHeaderLine('Access-Control-Request-Headers');

        $response = $handler->handle($request);
        $response = $response->withHeader('Access-Control-Allow-Origin', '*');
        $response = $response->withHeader('Access-Control-Allow-Methods', implode(',', $methods));
        $response = $response->withHeader('Access-Control-Allow-Headers', $requestHeaders);

        return $response;
    });

    $app->addRoutingMiddleware();

    // Define Custom Error Handler
    $customErrorHandler = function (
        Request $request,
        Throwable $exception,
        bool $displayErrorDetails,
        bool $logErrors,
        bool $logErrorDetails
        // ?LoggerInterface $logger = null
    ) use ($app) {
        // $logger->error($exception->getMessage());
        $payload = ['error' => $exception->getMessage()];
        $response = $app->getResponseFactory()->createResponse();
        $customResponse = new CustomResponse();
        return $customResponse->is400Response($response, $payload);
    };
    $errorMiddleware = $app->addErrorMiddleware(true, true, true);
    $errorMiddleware->setDefaultErrorHandler($customErrorHandler);
};

```

**database.php**

```auto
<?php
//MY SQL
$database_config_mysql = [
 'driver'=>'mysql',
    'host'=>'localhost',
    'database'=>'mysql_dbname',
    'username'=>'username',
    'password'=>'password',
    'charset'=>'utf8',
    'collation'=>'utf8_unicode_ci',
    'prefix'=>''
];

//SQL SERVER
$database_config_sqlsrv = [
    'driver' => 'sqlsrv',
    'host' => 'sqlServerHost',
    'database' => 'sqlsrvl_dbname',
    'username' => 'username',
    'password' => 'password',
    'charset' => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix' => ''
];

$capsule = new Illuminate\Database\Capsule\Manager;
$capsule->addConnection($database_config_mysql);

//???How do I get access to this connection from one of my controllers???
$capsule->addConnection($database_config_sqlsrv , "SQLSRV");
$capsule->setAsGlobal();
$capsule->bootEloquent();

return $capsule;

```

**routes.php**

```auto
<?php
use Slim\App;
use Slim\Exception\HttpNotFoundException;

use App\Controllers\PreFlightController;
use App\Controllers\SQLSRV\CompanyController;

return function (App $app) {

    //???How do I have all of these routes go through my validation check before accessing the route.
    $app->group("/company", function ($app) {
            $app->get('/company', [CompanyController::class, 'getAllRows']);
            $app->get('/compay/{id}', [CompanyController::class, 'getSingleCompany']);
            $app->get('/company/search/{id}', [CompanyController::class, 'getAllRows']);
            
            //???Can I prevent writing an option route for each above route.
            $app->options('/company', [PreFlightController::class, 'preflight']);
            $app->options('/compay/{id}'', [PreFlightController::class, 'preflight']);
            $app->options('/company/search/{id}'', [PreFlightController::class, 'preflight']);
    });

    $app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function ($request, $response) {
        throw new HttpNotFoundException($request);
    });
};

```

**CompanyController.php** (sqlsrv)

```auto
<?php
namespace App\Controllers\SQLSRV;

use App\Response\CustomResponse;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use App\Models\SQLSRV\Company;
use App\Validation\Validator;

class CompanyController
{
    protected $company;
    protected $customResponse;
    protected $validator;
    public function __construct()
    {
        $this->company= new Company();
        $this->customResponse = new CustomResponse();
        $this->validator = new Validator();
    }

    public function getCompanies(Request $request, Response $response)
    {

        //???Can I prevent writing this validation logic in every function, Would like to add it to all routes in a group.
        $user= $this->validator->validateFirebaseJWT($request);
        if ($user== null) {
            return $this->customResponse->is401Response($response, "Unauthorised Request");
         }

        $users = $this->user->get();
        return $this->customResponse->is200Response($response, $users);
    }

//???How can I run a stored procedure on a SQL server?
 public function runStoredProcedure(Request $request, Response $response)
    {
        //???I need a connection to the SQLSRV database, something like below...
        $result = $db->select($db->raw("Stored Procedure Code'") 
        return $this->customResponse->is200Response($response, $result );
    }

}

```

**Company.php** (sqlsrv)

```auto
<?php
namespace App\Models\Eclipse;

use Illuminate\Database\Eloquent\Model;

class Company extends Model
{
    protected $table = 'SQLSRV.Commercial.Company';
    protected $fillable = [
        "CompanyID",
        "CompanyName",
    ];
}

```

**validator.php**

```auto
<?php

namespace App\Controllers\Base;

use Firebase\JWT\JWT;
use Psr\Http\Message\RequestInterface as Request;
use App\Models\User;
use App\Models\GooglePublicKey;

//Gets the GooglePublicKey stored in the database
//Firebase\JWT\JWT Decodes the JWT
//Use Decoded data to check user is in the data base
//If user then authorize

//NOTE:this has some functions removed for space
class FirebaseAuth
{
    protected $user;
    protected $googlePublicKey;

    public function __construct()
    {
        $this->user = new User();
        $this->googlePublicKey = new GooglePublicKey();
    }

 public function validateFirebaseJWT(Request $request)
    {
        //Get JWT from auth header
        $jwt = $this->getJWT($request);
        if (!$jwt) {
            return null;
        }
        //Check if use is found in database
        $userData = $this->getUserData($jwt);
        if (!$userData) {
            return null;
        } else {
            return $userData;
        }
    }
}

```

**PreFlightController.php**

```auto
<?php
namespace App\Controllers;

use Psr\Http\Message\ResponseInterface as Response;

class PreFlightController
{
    public function preflight(Response $response)
    {
        return $response;
    }
}

```

**CustomResponse.php**

```auto
<?php

namespace App\Response;

class CustomResponse
{

    public function is200Response($response, $responseMessage)
    {
        $responseMessage = json_encode(["success" => true, "response" => $responseMessage, "status" => 200]);
        $response->getBody()->write($responseMessage);
        return $response->withHeader("Content-Type", "application/json")
            ->withStatus(200);
    }

    public function is400Response($response, $responseMessage)
    {
        $responseMessage = json_encode(["success" => false, "response" => $responseMessage, "status" => 400]);
        $response->getBody()->write($responseMessage);
        return $response->withHeader("Content-Type", "application/json")
            ->withStatus(400);
    }

    public function is401Response($response, $responseMessage)
    {
        $responseMessage = json_encode(["success" => false, "response" => $responseMessage, "status" => 401]);
        $response->getBody()->write($responseMessage);
        return $response->withHeader("Content-Type", "application/json")
            ->withStatus(401);
    }
}

```

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [September 12, 2022, 4:50pm UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370/2 "2022-09-12T16:50:31Z")

</div>

I try to give you some general tips.

To prevent code duplication, you may move the shared logic into a specific class. Depending on the use case, this class could be a Middleware class or a Service class.

According to CORS, I would choose a middleware based approach, because defining OPTION route for each route is a lot of extra work.

Example of a generic CorsPreflightMiddleware:

```php
if ($request->getMethod() === 'OPTIONS') {
    return $this->responseFactory->createResponse();
}

return $handler->handle($request);

```

The CorsPreflightMiddleware should be added after the RoutingMiddleware.

```php
$app->addRoutingMiddleware();
$app->add(\App\Middleware\CorsPreflightMiddleware::class);

```

To send the correct CORS headers you also need to replace Slim’s default emitter with a custom `CorsResponseEmitter`. [Example](https://github.com/slimphp/Slim-Skeleton/blob/67640c90e5908abda7ac05103ca8ba6f0347a932/src/Application/ResponseEmitter/ResponseEmitter.php).

[Usage example in index.php](https://github.com/slimphp/Slim-Skeleton/blob/67640c90e5908abda7ac05103ca8ba6f0347a932/public/index.php#L80-L82)

---

<div class="post-metadata">

**Author:** ![FOUK](https://avatars.discourse-cdn.com/v4/letter/f/a5b964/32.png) [@FOUK](https://discourse.slimframework.com/u/FOUK)\
**Post date:** [September 13, 2022, 6:54pm UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370/3 "2022-09-13T18:54:34Z")

</div>

Thanks for the reply,

Seeing the ‘OPTIONS’ gave me an idea for the **Preflight** problem, I added the following to the routes.php which seems to work.

```auto
    $app->map(['OPTIONS'], '/{routes:.+}', function ($request, $response) {
        return $response;
    });

```

I still cant seem to add middleware to a route or group, I am taking examples from the docs but getting the following errors:

```auto
"Too few arguments to function Closure::{closure}(), 2 passed in C:\\xampp\\htdocs\\foukAPI\\vendor\\slim\\slim\\Slim\\MiddlewareDispatcher.php on line 313 and exactly 3 expected"
$app->group("/users", function ($app) {
        $app->get('/users', [UserController::class, 'getAllRows']);
    })->add(function ($request, $response, $next) {
        $user = ['name' => 'Joe'];
        if ($user) {
            $response = $next($request, $response);
        } else {
            $response->getBody()->write('Unauthorized');
        }

        return $response;
    });

```

```auto
"Call to undefined method Slim\\MiddlewareDispatcher::getBody()"
    $app->group("/users", function ($app) {
        $app->get('/users', [UserController::class, 'getAllRows']);
    })->add(function ($request, $response) {
        $response->getBody()->write('Hello');
        return $response;
    });

```

Any Ideas?

Thanks

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [September 14, 2022, 8:28pm UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370/4 "2022-09-14T20:28:53Z")

</div>

Do you use Slim 3 or Slim 4?

---

<div class="post-metadata">

**Author:** ![FOUK](https://avatars.discourse-cdn.com/v4/letter/f/a5b964/32.png) [@FOUK](https://discourse.slimframework.com/u/FOUK)\
**Post date:** [September 18, 2022, 1:06am UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370/5 "2022-09-18T01:06:40Z")

</div>

Hi  
I am using Slim4

Thanks

---

<div class="post-metadata">

**Author:** ![odan](https://avatars.discourse-cdn.com/v4/letter/o/9de053/32.png) [@odan](https://discourse.slimframework.com/u/odan)\
**Post date:** [September 18, 2022, 8:43am UTC](https://discourse.slimframework.com/t/auth-preflight-middleware/5370/6 "2022-09-18T08:43:33Z")

</div>

> [@FOUK](#):
>
> `})->add(function ($request, $response, $next) {`

In this case you may add a middleware as documented in the Slim 4 docs, see [here](https://www.slimframework.com/docs/v4/concepts/middleware.html#how-do-i-add-middleware).
