diff --git a/README.md b/README.md index a9aef1c44..4f3bc1d65 100644 --- a/README.md +++ b/README.md @@ -2131,6 +2131,26 @@ Once you have enabled CORS you can then control four new headers in the HTTP Res Max-Age: 600 ``` +5. **Access-Control-Allow-Credentials.** + + When a request's credentials mode (Request.credentials) is "include", browsers + will only expose the response to frontend JavaScript code if the + Access-Control-Allow-Credentials value is true. + + The Access-Control-Allow-Credentials header works in conjunction with the + XMLHttpRequest.withCredentials property or with the credentials option in the + Request() constructor of the Fetch API. For a CORS request with credentials, + in order for browsers to expose the response to frontend JavaScript code, both + the server (using the Access-Control-Allow-Credentials header) and the client + (by setting the credentials mode for the XHR, Fetch, or Ajax request) must + indicate that they’re opting in to including credentials. + + This is set to empty by default but can be changed in YAML as in this example: + + ```yaml + Allow-Credentials: 'true' + ``` + ### Sample Custom CORS Config ```yaml @@ -2141,6 +2161,7 @@ SilverStripe\GraphQL\Controller: Allow-Origin: 'silverstripe.org' Allow-Headers: 'Authorization, Content-Type' Allow-Methods: 'GET, POST, OPTIONS' + Allow-Credentials: 'true' Max-Age: 600 # 600 seconds = 10 minutes. ``` ## Persisting queries diff --git a/src/Controller.php b/src/Controller.php index 15604edaf..5df6f7bc2 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -40,6 +40,7 @@ class Controller extends BaseController implements Flushable 'Allow-Origin' => [], // List of all allowed origins; Deny by default 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400, // 86,400 seconds = 1 day. ]; @@ -224,6 +225,10 @@ public function addCorsHeaders(HTTPRequest $request, HTTPResponse $response) $response->addHeader('Access-Control-Allow-Methods', $corsConfig['Allow-Methods']); $response->addHeader('Access-Control-Max-Age', $corsConfig['Max-Age']); + if (isset($corsConfig['Allow-Credentials'])) { + $response->addHeader('Access-Control-Allow-Credentials', $corsConfig['Allow-Credentials']); + } + return $response; } diff --git a/tests/ControllerTest.php b/tests/ControllerTest.php index 995551151..2a590d728 100644 --- a/tests/ControllerTest.php +++ b/tests/ControllerTest.php @@ -180,6 +180,7 @@ public function testAddCorsHeadersOriginDisallowed() 'Allow-Origin' => null, 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400 ]); @@ -200,6 +201,7 @@ public function testAddCorsHeadersOriginAllowed() 'Allow-Origin' => 'http://localhost', 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400 ]); @@ -226,6 +228,7 @@ public function testAddCorsHeadersRefererAllowed() 'Allow-Origin' => 'http://localhost', 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400 ]); @@ -252,6 +255,7 @@ public function testAddCorsHeadersRefererPortAllowed() 'Allow-Origin' => 'http://localhost:8181', 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400 ]); @@ -283,6 +287,7 @@ public function testAddCorsHeadersRefererPortDisallowed() 'Allow-Origin' => 'http://localhost:9090', 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400 ]); @@ -300,6 +305,7 @@ public function testAddCorsHeadersOriginAllowedWildcard() 'Allow-Origin' => '*', 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, PUT, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 600 ]); @@ -323,6 +329,7 @@ public function testAddCorsHeadersOriginMissing() 'Allow-Origin' => 'localhost', 'Allow-Headers' => 'Authorization, Content-Type', 'Allow-Methods' => 'GET, POST, OPTIONS', + 'Allow-Credentials' => '', 'Max-Age' => 86400 ]);