Skip to content

Commit

Permalink
Update Enable CORS #313
Browse files Browse the repository at this point in the history
  • Loading branch information
odan committed Jul 10, 2024
1 parent 83ee3aa commit f71e746
Showing 1 changed file with 51 additions and 95 deletions.
146 changes: 51 additions & 95 deletions docs/v4/cookbook/enable-cors.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@
title: Setting up CORS
---

CORS - Cross origin resource sharing
Cross-Origin Resource Sharing ([CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/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.

A good flowchart for implementing CORS support Reference:
It is a mechanism that enables controlled access to resources located
outside of a given domain.

[CORS server flowchart](http://www.html5rocks.com/static/images/cors_server_flowchart.png)
CORS is essential for enabling secure communication between different
web applications while preventing malicious cross-origin requests.

You can test your CORS Support here: http://www.test-cors.org/
By specifying certain headers, servers can indicate which origins are
permitted to access their resources, thus maintaining a balance between
usability and security.

You can read the specification here: https://www.w3.org/TR/cors/
A good flowchart for implementing CORS support:
[CORS Server Flowchart](https://www.html5rocks.com/static/images/cors_server_flowchart.png)

You can read the specification here: <https://fetch.spec.whatwg.org/#cors-protocol>

## The simple solution

Expand All @@ -37,10 +45,9 @@ $app->add(function ($request, $handler) {
});
```

Add the following route as the last route:
**Optional:** Add the following route as the last route:

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

/**
Expand All @@ -52,123 +59,72 @@ $app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function ($
});
```

## CORS example application

## Access-Control-Allow-Methods

The following middleware can be used to query Slim's router and get a list of methods a particular pattern implements.

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

```php
<?php

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

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

$app = AppFactory::create();

$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);

// Optional: Allow Ajax CORS requests with Authorization header
// $response = $response->withHeader('Access-Control-Allow-Credentials', 'true');

return $response;
});

// The RoutingMiddleware should be added after our CORS middleware so routing is performed first
// Add the RoutingMiddleware before the CORS middleware
// to ensure routing is performed later
$app->addRoutingMiddleware();

// The routes
$app->get('/api/v0/users', function (Request $request, Response $response): Response {
$response->getBody()->write('List all users');

return $response;
});

$app->get('/api/v0/users/{id}', function (Request $request, Response $response, array $arguments): Response {
$userId = (int)$arguments['id'];
$response->getBody()->write(sprintf('Get user: %s', $userId));

return $response;
});

$app->post('/api/v0/users', function (Request $request, Response $response): Response {
// Retrieve the JSON data
$parameters = (array)$request->getParsedBody();

$response->getBody()->write('Create user');

return $response;
});

$app->delete('/api/v0/users/{id}', function (Request $request, Response $response, array $arguments): Response {
$userId = (int)$arguments['id'];
$response->getBody()->write(sprintf('Delete user: %s', $userId));
// 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;
});

// Allow preflight requests
// Due to the behaviour of browsers when sending a request,
// you must add the OPTIONS method. Read about preflight.
$app->options('/api/v0/users', function (Request $request, Response $response): Response {
// Do nothing here. Just return the response.
// Define app routes
$app->get('/', function (ServerRequestInterface $request, ResponseInterface $response) {
$response->getBody()->write('Hello, World!');

return $response;
});

// Allow additional preflight requests
$app->options('/api/v0/users/{id}', function (Request $request, Response $response): Response {
return $response;
});

// Using groups
$app->group('/api/v0/users/{id:[0-9]+}', function (RouteCollectorProxy $group) {
$group->put('', function (Request $request, Response $response, array $arguments): Response {
// Your code here...
$userId = (int)$arguments['id'];
$response->getBody()->write(sprintf('Put user: %s', $userId));

return $response;
});

$group->patch('', function (Request $request, Response $response, array $arguments): Response {
$userId = (int)$arguments['id'];
$response->getBody()->write(sprintf('Patch user: %s', $userId));

return $response;
});

// Allow preflight requests
$group->options('', function (Request $request, Response $response): Response {
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.
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.

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

0 comments on commit f71e746

Please sign in to comment.