IMPORTANT: The below description is the target solution, after we remove all deprecated types and fix (uncomment) code and tests for it. Currently, we cannot have the "fallback" CORS configuration that prohibits everything, as we would never reach the currently allowed custom CORS handling done in routing. The steps are exactly as they happen in the code, only the "Do not allow any origins for any other requests" part of default configuration is currently not there
CORS: Cross-Origin Resource Sharing
CORS is a protocol designed for browsers, to create a trusted client. The browser ensures that if there is an invocation of an endpoint from a different domain than the script invoking it came from, the request is allowed by the endpoint for that domain.
In Helidon, CORS is configured either through config, or by setting up a CorsFeature (a ServerFeature),
or by creating a ServiceRegistry service(s) that provides CorsPathConfig instance.
Configuration is treated as a sequence of protected paths. The first path configuration that matches the provided path pattern and allowed method will be used to process the CORS requests, and all other path configurations will be ignored.
Each CorsPathConfig can configure (see io.helidon.webserver.cors.CorsPathConfigBlueprint):
path-pattern: web server path pattern to handle with this configenabled: whether this path should be usedallow-origins: a set of origins, or origin regular expressions (if it contains\,*, or{we consider this to be a regular expression)allow-headers: a set of header names (case-insensitive) - which headers will be sent from the script with a different originallow-methods: a set of method names (case-sensitive) - which methods are allowed for this path (only if both path-pattern and method match will this be used)expose-headers: a set of header names (case-insensitive) - which headers may be exposed from the response to the script with a different originallow-credentials: boolean - whether to add credentials (such as cookies) to requests from a script from a different originmax-age: duration - how long is this response valid
By default, Helidon will add the following configuration (on path pattern /* - i.e. matches all requests):
- Allow any
GET,HEAD, andPOSTrequest with any origin, any headers, with no exposed headers, and allow credentials set tofalse - Do not allow any origins for any other requests (IMPORTANT: this is future behavior, cannot be enabled until we remove deprecated types and behavior)
The first default (allow GET, HEAD, and POST) can be disabled.
Consider the following config:
cors:
add-defaults: false
paths:
path-pattern: "/metrics/*" # all paths under /metric including /metric
allow-origins: ["https://my.server"]This will create a single allowed path (anything under /metric) for the origin https://my.server, and
any other CORS request will be denied (as we disable defaults).
Cors can also be disabled:
cors:
enabled: falseCors can be either a "Pre-Flight" check (OPTIONS method invoked by the browser when it needs to check a CORS request, defined by the fetch spec), or a "Flight" check (the target HTTP method with Origin header),
or it can be a non-cors request (no Origin header, or the request is same origin).
Decision whether to invoke "Pre-Flight" or "Flight" check:
- Check if this is
OPTIONSmethod, if not -> may be a "Flight" check, if options, may be both - Check if this is a
CORSrequest (must containOriginheader, and origin must differ from host), if not -> non CORS - For
OPTIONSmethods: check if this is preflight (must containAccess-Control-Request-Methodheader), if not -> "Flight" check - "Pre-Flight" check
If the request is not a CORS request, we ignore it and do not send any headers back.
Pre-flight check is done in CorsHttpFilter, and finalized in the CorsFeature.CorsOptionsHttpFeature where we ensure that a 200 is returned for any options method, even if such a routing does not exist in the application.
Pre flight validation sequence:
- Find first validator that matches the path, if not found, allow (we will not configure any headers, so if no route catches this, browser will not allow the request)
- If method is not in
Allow-Methods, return403 - If origin is not in
Allow-Origins, return403 - If requested header (
Access-Control-Request-Headers) is not inAllow-Headers, return403
The pre-flight response setup sequence:
- If we allow credentials, send
Access-Control-Allow-Credentials: trueand (fetch spec, section 3.2.5):- send
Access-Control-Allow-Originwith the requested origin - send
Access-Control-Allow-Methodswith the requested method - send
Vary: Origin
- send
- If we do not allow credentials:
- if we allow all origins, send
Access-Control-Allow-Origin: *, otherwise send the requested origin - if we do not allow all origins, send
Vary: Origin - if we allow all methods, send
Access-Control-Allow-Methods: *, otherwise send the allowed methods
- if we allow all origins, send
- If there are
Access-Control-Request-Headersdefined, sendAccess-Control-Allow-Headerswith the same set that was requested (not theAllow-Headerconfigured set) - If configured max age is higher than 0 seconds, send
Access-Control-Max-Agewith the configured value
Flight check is done in CorsHttpFilter only.
The flight validation sequence:
- Find first validator that matches the path
- If method is not in
Allow-Methods, return403 - If origin is not in
Allow-Origins, return403
The flight response setup sequence:
- If we allow credentials
- send
Access-Control-Allow-Credentials: trueheader - send
Access-Control-Allow-Originheader configured to the origin from request - send
Vary: Originheader - if
Expose-Headersis configured and not to*, sendAccess-Control-Expose-Headerswith the configured values
- send
- If we do not allow credentials:
- if we allow all origins, send
Access-Control-Allow-Origin: *, otherwise send the requested origin - if we do nota allow all origins, send
Vary: Origin - if we expose all headers, send
Access-Control-Expose-Headers: *, otherwise send the set of exposed headers (this is treated as literal*when allow credentials is configured)
- if we allow all origins, send