Skip to content

Commit

Permalink
Fix CORS header caching in GraphQL responses (#896)
Browse files Browse the repository at this point in the history
Ensure that that dynamic Access-Control-Allow-Origin header is not cached.

- Remove Access-Control-Allow-Origin header before saving to cache
- Add Access-Control-Allow-Origin dynamically when serving cached responses
  based on the incoming request's Origin

This resolves issues with CORS headers being cached alongside GraphQL
responses, which could cause incorrect Access-Control-Allow-Origin values
for clients with different origins.
  • Loading branch information
gajdusek authored and mcop1 committed Oct 18, 2024
1 parent 90cca98 commit b71636a
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 13 deletions.
2 changes: 2 additions & 0 deletions doc/10_GraphQL/10_Events.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ class GraphQlSubscriber implements EventSubscriberInterface
- `OutputCacheEvents::PRE_LOAD`: is triggered before trying to load an entry from cache, if cache is enabled. You can disable the cache for this request by setting `$event->setUseCache(false)`. If you disable the cache, the entry won't be loaded nor saved
- `OutputCacheEvents::PRE_SAVE`: if cache is enabled, it's triggered before saving an entry into the cache. You can use it to modify the response before it gets saved.

Uncacheable headers, such as CORS Access-Control-Allow-Origin, are removed from the response before the PRE_SAVE event and re-added after the cached response is loaded.

```php
<?php

Expand Down
10 changes: 0 additions & 10 deletions src/Controller/WebserviceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,17 +225,7 @@ public function webonyxAction(
];
}

$origin = '*';
if (!empty($_SERVER['HTTP_ORIGIN'])) {
$origin = $_SERVER['HTTP_ORIGIN'];
}

$response = new JsonResponse($output);
$response->headers->set('Access-Control-Allow-Origin', $origin);
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
$response->headers->set('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token');

$this->cacheService->save($request, $response);

return $response;
Expand Down
39 changes: 36 additions & 3 deletions src/Service/OutputCacheService.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ public function load(Request $request)

$cacheKey = $this->computeKey($request);

return $this->loadFromCache($cacheKey);
$response = $this->loadFromCache($cacheKey);
if ($response) {
$this->addCorsHeaders($response);
}

return $response;
}

/**
Expand All @@ -81,15 +86,43 @@ public function load(Request $request)
public function save(Request $request, JsonResponse $response, $extraTags = []): void
{
if ($this->useCache($request)) {
$cacheKey = $this->computeKey($request);
$clientname = $request->attributes->getString('clientname');
$extraTags = array_merge(['output', 'datahub', $clientname], $extraTags);

$this->removeCorsHeaders($response);
$cacheKey = $this->computeKey($request);

$event = new OutputCachePreSaveEvent($request, $response);
$this->eventDispatcher->dispatch($event, OutputCacheEvents::PRE_SAVE);

$this->saveToCache($cacheKey, $event->getResponse(), $extraTags);
$this->saveToCache($cacheKey, $response, $extraTags);

$this->addCorsHeaders($response);
}
}

/**
* Removes CORS headers including Access-Control-Allow-Origin that should not be cached.
*/
protected function removeCorsHeaders(JsonResponse $response): void
{
$response->headers->remove('Access-Control-Allow-Origin');
$response->headers->remove('Access-Control-Allow-Credentials');
$response->headers->remove('Access-Control-Allow-Methods');
$response->headers->remove('Access-Control-Allow-Headers');
}

protected function addCorsHeaders(JsonResponse $response): void
{
$origin = '*';
if (!empty($_SERVER['HTTP_ORIGIN'])) {
$origin = $_SERVER['HTTP_ORIGIN'];
}

$response->headers->set('Access-Control-Allow-Origin', $origin);
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
$response->headers->set('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token');
}

/**
Expand Down

0 comments on commit b71636a

Please sign in to comment.