im trying to learn slim and setting up cors, csrf, and using it as a api for my react app. I've ran into issues with cors and csrf... hoping you can help. Heres what I learnt about cors and csrf and after this post ill share my index.php for my app? ok?

Cross-Origin Resource Sharing (CORS) is a security feature implemented in web browsers that allows or restricts web pages from making requests to a domain different from the one that served the web page.

It is a mechanism that enables controlled access to resources located outside of a given domain.

CORS is essential for enabling secure communication between different web applications while preventing malicious cross-origin requests.

By specifying certain headers, servers can indicate which origins are permitted to access their resources, thus maintaining a balance between usability and security.

A good flowchart for implementing CORS support: CORS Server Flowchart

You can read the specification here: https://fetch.spec.whatwg.org/#cors-protocol
The simple solution

For simple CORS requests, the server only needs to add the following header to its response:

Access-Control-Allow-Origin: <domain>, ... 

The following code should enable lazy CORS.

$app->options('/{routes:.+}', function ($request, $response, $args) {
    return $response;
});

$app->add(function ($request, $handler) {
    $response = $handler->handle($request);
    return $response
            ->withHeader('Access-Control-Allow-Origin', 'http://mysite')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});

Optional: Add the following route as the last route:

use Slim\Exception\HttpNotFoundException;

/**
 * Catch-all route to serve a 404 Not Found page if none of the routes match
 * NOTE: make sure this route is defined last
 */
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function ($request, $response) {
    throw new HttpNotFoundException($request);
});

CORS example application

Here is a complete CORS example application that uses a CORS middleware:

<?php

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Factory\AppFactory;

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

$app = AppFactory::create();

$app->addBodyParsingMiddleware();

// Add the RoutingMiddleware before the CORS middleware
// to ensure routing is performed later
$app->addRoutingMiddleware();

// Add the ErrorMiddleware before the CORS middleware
// to ensure error responses contain all CORS headers.
$app->addErrorMiddleware(true, true, true);

// This CORS middleware will append the response header
// Access-Control-Allow-Methods with all allowed methods
$app->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler) use ($app): ResponseInterface {
    if ($request->getMethod() === 'OPTIONS') {
        $response = $app->getResponseFactory()->createResponse();
    } else {
        $response = $handler->handle($request);
    }

    $response = $response
        ->withHeader('Access-Control-Allow-Credentials', 'true')
        ->withHeader('Access-Control-Allow-Origin', '*')
        ->withHeader('Access-Control-Allow-Headers', '*')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
        ->withHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
        ->withHeader('Pragma', 'no-cache');

    if (ob_get_contents()) {
        ob_clean();
    }

    return $response;
});

// Define app routes
$app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {
    $response->getBody()->write('Hello, World!');
    
    return $response;
});

// ...

$app->run();

Access-Control-Allow-Credentials

If the request contains credentials (cookies, authorization headers or TLS client certificates), you might need to add an Access-Control-Allow-Credentials header to the response object.

$response = $response->withHeader('Access-Control-Allow-Credentials', 'true');


In most cases you want to register Slim\Csrf for all routes, however, as it is middleware, you can also register it for a subset of routes.
Register for all routes

use DI\Container;
use Slim\Csrf\Guard;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

// Start PHP session
session_start();

// Create Container
$container = new Container();
AppFactory::setContainer($container);

// Create App
$app = AppFactory::create();
$responseFactory = $app->getResponseFactory();

// Register Middleware On Container
$container->set('csrf', function () use ($responseFactory) {
    return new Guard($responseFactory);
});

// Register Middleware To Be Executed On All Routes
$app->add('csrf');

$app->get('/foo', function ($request, $response, $args) {
    // CSRF token name and value
    $csrf = $this->get('csrf');
    $nameKey = $csrf->getTokenNameKey();
    $valueKey = $csrf->getTokenValueKey();
    $name = $request->getAttribute($nameKey);
    $value = $request->getAttribute($valueKey);

    /*
       Render HTML form which POSTs to /bar with two hidden input fields for the
       name and value:
       <input type="hidden" name="<?= $nameKey ?>" value="<?= $name ?>">
       <input type="hidden" name="<?= $valueKey ?>" value="<?= $value ?>">
     */
});

$app->post('/bar', function ($request, $response, $args) {
    // CSRF protection successful if you reached
    // this far.
});

$app->run();

Register per route

use DI\Container;
use Slim\Csrf\Guard;
use Slim\Factory\AppFactory;

require __DIR__ . '/vendor/autoload.php';

// Start PHP session
session_start();

// Create Container
$container = new Container();
AppFactory::setContainer($container);

// Create App
$app = AppFactory::create();
$responseFactory = $app->getResponseFactory();

// Register Middleware On Container
$container->set('csrf', function () use ($responseFactory) {
    return new Guard($responseFactory);
});

$app->get('/api/route',function ($request, $response, $args) {
    $csrf = $this->get('csrf');
    $nameKey = $csrf->getTokenNameKey();
    $valueKey = $csrf->getTokenValueKey();
    $name = $request->getAttribute($nameKey);
    $value = $request->getAttribute($valueKey);

    $tokenArray = [
        $nameKey => $name,
        $valueKey => $value
    ];
    
    return $response->write(json_encode($tokenArray));
})->add('csrf');

$app->post('/api/myEndPoint',function ($request, $response, $args) {
    //Do my Things Securely!
})->add('csrf');

$app->run();

Token persistence

By default, Slim\Csrf\Guard will generate a fresh name/value pair after each request. This is an important security measure for certain situations. However, in many cases this is unnecessary, and a single token throughout the user's session will suffice. By using per-session requests it becomes easier, for example, to process AJAX requests without having to retrieve a new CSRF token (by reloading the page or making a separate request) after each request. See issue #49.

To use persistent tokens, set the sixth parameter of the constructor to true. No matter what, the token will be regenerated after a failed CSRF check. In this case, you will probably want to detect this condition and instruct your users to reload the page in their legitimate browser tab (or automatically reload on the next failed request).
Accessing the token pair in templates (Twig, etc)

In many situations, you will want to access the token pair without needing to go through the request object. In these cases, you can use getTokenName() and getTokenValue() directly on the Guard middleware instance. This can be useful, for example in a Twig extension:

use Slim\Csrf\Guard;

class CsrfExtension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
{
    /**
     * @var Guard
     */
    protected $csrf;
    
    public function __construct(Guard $csrf)
    {
        $this->csrf = $csrf;
    }

    public function getGlobals()
    {
        // CSRF token name and value
        $csrfNameKey = $this->csrf->getTokenNameKey();
        $csrfValueKey = $this->csrf->getTokenValueKey();
        $csrfName = $this->csrf->getTokenName();
        $csrfValue = $this->csrf->getTokenValue();
        
        return [
            'csrf'   => [
                'keys' => [
                    'name'  => $csrfNameKey,
                    'value' => $csrfValueKey
                ],
                'name'  => $csrfName,
                'value' => $csrfValue
            ]
        ];
    }
}

Once you have registered your extension, you may access the token pair in any template:

<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">


Handling validation failure

By default, Slim\Csrf\Guard will return a Response with a 400 status code and a simple plain text error message.

To override this, provide a callable as the third parameter to the constructor or via setFailureHandler(). This callable has the same signature as middleware: function($request, $handler) and must return a Response.

For example:

use Slim\Csrf\Guard;
use Slim\Psr7\Factory\ResponseFactory;

$responseFactory = new ResponseFactory();
$guard = new Guard($responseFactory);
$guard->setFailureHandler(function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
    $request = $request->withAttribute("csrf_status", false);
    return $handler->handle($request);
});

In this example, an attribute is set on the request object that can then be checked in subsequent middleware or the route callable using:

if (false === $request->getAttribute('csrf_status')) {
    // display suitable error here
} else {
    // successfully passed CSRF check
}