diff --git a/docs/v4/cookbook/enable-cors.md b/docs/v4/cookbook/enable-cors.md index 52405214..aeb31043 100644 --- a/docs/v4/cookbook/enable-cors.md +++ b/docs/v4/cookbook/enable-cors.md @@ -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: ## The simple solution @@ -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 -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 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');