shlink icon indicating copy to clipboard operation
shlink copied to clipboard

Allow some customizations on CORS configuration

Open acelaya opened this issue 9 months ago • 4 comments

Summary

Allow customizing allowed origins, allow credentials, and max age:

  • CORS_ALLOW_ORIGIN: * (allow any origin), <origin> (which will make it resolve the value from the request's Origin header) or a comma-sepparated list of origins, which will make Shlink return the header only when those origins are the ones making the request. Defaults to *.
  • CORS_ALLOW_CREDENTIALS: true or false. Defaults to false.
  • CORS_MAX_AGE: Any number representing a value in seconds. Defaults to 3600.

More details:

  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Credentials
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age

Use case

This will allow to better support cases like the one described in https://github.com/shlinkio/shlink-web-client/issues/1510

acelaya avatar Apr 17 '25 07:04 acelaya

Please note that you will also need to allow the Access-Control-Allow-Headers header to be configured.

Per MDN:

The value * only counts as a special wildcard value for requests without credentials (requests without HTTP cookies or HTTP authentication information). In requests with credentials, it is treated as the literal header name * without special semantics.

stouset avatar Apr 21 '25 17:04 stouset

Please note that you will also need to allow the Access-Control-Allow-Headers header to be configured.

I will verify it, but I would expect this to cover for anything https://github.com/shlinkio/shlink/blob/develop/module/Rest/src/Middleware/CrossDomainMiddleware.php#L42

That basically means Shlink will set the value of Access-Control-Allow-Headers based on what the browser set in Access-Control-Request-Headers.

It's always better if it can be made so that there's less room for human error.

acelaya avatar Apr 21 '25 17:04 acelaya

~~Unfortunately I think that's not enough. The problem is that the preflight request happens before those headers are sent, because the client doesn't know that it's okay to send them yet. So they're not included in the preflight request, the preflight response doesn't know to say "yes, those headers are okay", and then the browser fails to send the actual request because the headers it was asked to provide weren't whitelisted by the preflight.~~

~~I think in your mental model, the browser sends all the headers it hopes to send as part of the initial CORS preflight, the preflight response says "yes, those are okay", and then the browser makes the actual request with those headers. But instead, the client makes a request without any of those headers, the server explicitly tells the client which ones are okay to send, and then the client decides whether or not it can make the request based on the headers it needs to include.~~

One sec, I think something else is going on for us. You're right that the browser should include all of the headers in the original request, in the Access-Control-Request-Headers header. Sorry, CORS is quite complex in practice.

stouset avatar Apr 21 '25 17:04 stouset

Sorry, CORS is quite complex in practice.

It's a pain in the ass 😅

acelaya avatar Apr 21 '25 18:04 acelaya

This feature is now implemented.

Three new options are now available:

  • CORS_MAX_AGE: determines the value of the Access-Control-Max-Age header.
  • CORS_ALLOW_CREDENTIALS: sets Access-Control-Allow-Credentials: true when it is true. Access-Control-Allow-Credentials is not set at all otherwise.
  • CORS_ALLOW_ORIGIN:
    • When it is * it sets Access-Control-Allow-Origin: *.
    • When it has the special value <origin>, it sets the value on the request's Origin header as the response's Access-Control-Allow-Origin.
    • When a comma-separated list of values is set, it sets the value on the request's Origin header as the response's Access-Control-Allow-Origin, only of the value is is part of that list. Otherwise, Access-Control-Allow-Origin is not set at all.

acelaya avatar Jul 16 '25 06:07 acelaya