Make sure Access-Control-Allow-Credentials is always set if configured

This commit is contained in:
Alejandro Celaya
2025-10-03 10:03:42 +02:00
parent 98b504a2de
commit b01f271f72
5 changed files with 54 additions and 19 deletions

View File

@@ -28,7 +28,7 @@ readonly class CrossDomainMiddleware implements MiddlewareInterface, RequestMeth
}
// Add Allow-Origin header
$response = $this->options->responseWithAllowOrigin($request, $response);
$response = $this->options->responseWithCorsHeaders($request, $response);
if ($request->getMethod() !== self::METHOD_OPTIONS) {
return $response;
}
@@ -38,18 +38,13 @@ readonly class CrossDomainMiddleware implements MiddlewareInterface, RequestMeth
private function addOptionsHeaders(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
{
$corsHeaders = [
// Options requests should always be empty and have a 204 status code
return EmptyResponse::withHeaders([
...$response->getHeaders(),
'Access-Control-Allow-Methods' => $this->resolveCorsAllowedMethods($response),
'Access-Control-Allow-Headers' => $request->getHeaderLine('Access-Control-Request-Headers'),
'Access-Control-Max-Age' => $this->options->maxAge,
];
if ($this->options->allowCredentials) {
$corsHeaders['Access-Control-Allow-Credentials'] = 'true';
}
// Options requests should always be empty and have a 204 status code
return EmptyResponse::withHeaders([...$response->getHeaders(), ...$corsHeaders]);
]);
}
private function resolveCorsAllowedMethods(ResponseInterface $response): string

View File

@@ -21,6 +21,7 @@ class CorsTest extends ApiTestCase
self::assertFalse($resp->hasHeader('Access-Control-Allow-Methods'));
self::assertFalse($resp->hasHeader('Access-Control-Max-Age'));
self::assertFalse($resp->hasHeader('Access-Control-Allow-Headers'));
self::assertFalse($resp->hasHeader('Access-Control-Allow-Credentials'));
}
#[Test, DataProvider('provideOrigins')]
@@ -38,6 +39,7 @@ class CorsTest extends ApiTestCase
self::assertFalse($resp->hasHeader('Access-Control-Allow-Methods'));
self::assertFalse($resp->hasHeader('Access-Control-Max-Age'));
self::assertFalse($resp->hasHeader('Access-Control-Allow-Headers'));
self::assertFalse($resp->hasHeader('Access-Control-Allow-Credentials'));
}
public static function provideOrigins(): iterable

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace ShlinkioTest\Shlink\Rest\Middleware;
use Fig\Http\Message\RequestMethodInterface;
use Laminas\Diactoros\Response;
use Laminas\Diactoros\ServerRequest;
use PHPUnit\Framework\Attributes\DataProvider;
@@ -142,13 +143,17 @@ class CrossDomainMiddlewareTest extends TestCase
}
#[Test]
#[TestWith([true])]
#[TestWith([false])]
public function credentialsAreAllowedIfConfiguredSo(bool $allowCredentials): void
#[TestWith([true, RequestMethodInterface::METHOD_OPTIONS])]
#[TestWith([false, RequestMethodInterface::METHOD_OPTIONS])]
#[TestWith([true, RequestMethodInterface::METHOD_GET])]
#[TestWith([false, RequestMethodInterface::METHOD_GET])]
#[TestWith([true, RequestMethodInterface::METHOD_POST])]
#[TestWith([false, RequestMethodInterface::METHOD_POST])]
public function credentialsAreAllowedIfConfiguredSo(bool $allowCredentials, string $method): void
{
$originalResponse = new Response();
$request = (new ServerRequest())
->withMethod('OPTIONS')
->withMethod($method)
->withHeader('Origin', 'local');
$this->handler->method('handle')->willReturn($originalResponse);