Supporting CORS in REST Services
Cross-origin Resource Sharing (CORS) allows a script running in another domain to access a Caché REST service. Typically, when a browser is running a script from one domain, it allows XMLHttpRequest calls to that same domain but disallows them when they are made to another domain. This browser behavior restricts someone from creating a malicious script that can misuse confidential data. The malicious script could allow the user to access information in another domain using permissions granted to the user, but then, unknown to the user, make other use of confidential information. To avoid this security problem, browsers generally do not allow this kind of cross-domain call.
Without using CORS, a web page with a script accessing REST services typically must be in the same domain as the server providing the REST services. In some environments, it is useful to have the web pages with scripts in a different domain than the servers providing the REST services. CORS enables this arrangement.
The following provides a simplified description of how a browser can handle an XMLHttpRequest with CORS:
-
A script in a web page in domain DomOne contains an XMLHttpRequest to a Caché REST service that is in domain DomTwo. The XMLHttpRequest has a custom header for CORS.
-
A user views this web page and runs the script. The user’s browser detects the XMLHttpRequest to a domain different from the one containing the web page.
-
The user’s browser sends a special request to the Caché REST service that indicates the HTTP request method of the XMLHttpRequest and the domain of the originating web page, which is DomOne in this example.
-
If the request is allowed, the response contains the requested information. Otherwise, the response consists only of headers indicating that CORS did not allow the request.
Caché supports CORS by passing the HTTP headers and allows you to configure whether a REST service allows the CORS header. You must write code that defines when to allow a CORS request. For example, you can provide an allow list containing domains that contain only trusted scripts. Caché does provides a simple default implementation for documentation purposes but it allows any CORS request. You should not enable CORS processing for confidential data using this default implementation.
To write the code that controls CORS requests, you override the OnHandleCorsRequest() method in your %CSP.RESTOpens in a new tab subclass.
This chapter contains the following sections:
Configuring a REST Service to Use CORS
You control whether a REST service supports CORS with the %CSP.RESTOpens in a new tab HandleCorsRequest parameter and with the Cors attribute on the route element in the UrlMap.
To enable or disable CORS header processing for all REST services defined in the %CSP.RESTOpens in a new tab subclass, set the HandleCorsRequest parameter to "true" to enable CORS processing or to "false", which is the default, to disable CORS processing.
To enable or disable CORS header processing independently for each Route in the UrlMap, set the HandleCorsRequest parameter to "" (empty string) and specify Cors="true" in a Route element to enable CORS processing and Cors="false" in a Route element to disable CORS processing.
If a %CSP.RESTOpens in a new tab subclass forwards a REST request to a second %CSP.RESTOpens in a new tab subclass that contains a Route matching that REST URL, the HandleCorsRequest parameter in the %CSP.RESTOpens in a new tab subclass containing the matching Route controls the behavior of the CORS processing.
If CORS processing is disabled for an incoming REST URL with CORS headers, %CSP.RESTOpens in a new tab rejects the incoming request.
A Caché REST service supports the OPTIONS request (the CORS preflight request), which is used to determine whether a REST service supports CORS. This request is executed by the CSPSystem user. This user should have READ permission on any databases used by the REST service; if not, the service will respond with an HTTP 404 error.
Overriding the OnHandleCorsRequest Method
The default implementation of the OnHandleCorsRequest() method does not do any filtering and simply passes the CORS header to the external server and returns the response. You may want to restrict access to origins that are listed in a domain allow list or to restrict what request methods are allowed. You do this by overriding the OnHandleCorsRequest() method in your %CSP.RESTOpens in a new tab subclass.
All URL requests that match Route elements in the UrlMap , are processed with the single OnHandleCorsRequest() method that is defined in the class. If you need to have different implementations of the OnHandleCorsRequest() method for different REST URL requests, you should use Forward to send the requests to different subclasses of %CSP.RESTOpens in a new tab.
To implement the OnHandleCorsRequest() method, you must be familiar with the details of the CORS protocol. This section identifies the parts of the default OnHandleCorsRequest() method implementation and identifies the lines that handle the origin, credentials, header, and request method.
The following code from the %CSP.REST.HandleDefaultCorsRequest() method gets the origin and use it to set the response header. One possible way to handle this is to test the origin against an allow list and only use it to set the response header if the domain is allowed. If it is not allowed, you can set the response header to an empty string.
#; Get the origin
Set tOrigin=$Get(%request.CgiEnvs("HTTP_ORIGIN"))
#; Allow requested origin
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Origin",tOrigin)
The following lines specify that the authorization header should be included.
#; Set allow credentials to be true
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Credentials","true")
The following lines get the headers and the request method from the incoming request. Add code to test if these headers and request method are allowed. If they are allowed, use them to set the response headers.
#; Allow requested headers
Set tHeaders=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_HEADERS"))
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Headers",tHeaders)
#; Allow requested method
Set tMethod=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_METHOD"))
Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Method",tMethod)
The %CSP.REST.HandleDefaultCorsRequest() method provides a simple default implementation for documentation purposes only. You should not enable CORS processing for confidential data using this default implementation.