Skip to main content
Previous section   Next section

Supporting CORS in REST Services

This chapter describes how to enable Cross-Origin Resource Sharing (CORS) in InterSystems IRIS REST services. This chapter discusses the following topics:

This chapter assumes that you have previously generated REST service classes as described in the chapters “Using the /api/mgmnt Service to Create REST Services,” “Using the ^%REST Routine to Create REST Services,” or “Using the %REST.API Class to Create REST Services.”

Overview

This section provides overview of CORS and an overview of how to enable CORS in InterSystems IRIS REST services.

Introduction to CORS

Cross-Origin Resource Sharing (CORS) allows a script running in another domain to access a 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 Cross-Origin Resource Sharing (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:

  1. A script in a web page in domain DomOne contains an XMLHttpRequest to an InterSystems IRIS REST service that is in domain DomTwo. The XMLHttpRequest has a custom header for CORS.

  2. 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.

  3. The user’s browser sends a special request to the InterSystems IRIS 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.

  4. 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.

Overview of Enabling a REST Service to Support CORS

By default, InterSystems REST services do not allow the CORS header. You can, however, enable CORS support. There are two parts to enabling support for CORS in a REST service:

  • Enabling the REST service to accept the CORS header for some or all HTTP requests. See “Accepting the CORS Header.”

  • Writing code that causes the REST service to examine the CORS requests and decide whether to proceed. For example, you can provide a white-list containing domains that contain only trusted scripts. InterSystems IRIS provides a simple default implementation for documentation purposes; this default implementation allows any CORS request.

Important:

The default CORS header processing is not suitable for REST services handling confidential data.

Accepting the CORS Header

To specify that a REST service accepts the CORS header:

  1. Modify the specification class to include the HandleCorsRequest parameter.

    To enable CORS header processing for all calls, specify the HandleCorsRequest parameter as 1:

    Parameter HandleCorsRequest = 1;
    Copy code to clipboard

    Or, to enable CORS header processing for some but not calls, specify the HandleCorsRequest parameter as "" (empty string):

    Parameter HandleCorsRequest = "";
    Copy code to clipboard
  2. If you specified HandleCorsRequest parameter as "", edit the OpenAPI XData block in the specification class in order to indicate which calls support CORS. Specifically, for the operation objects, add the following property name and value:

    "x-ISC_CORS":true
    Copy code to clipboard

    For example, the OpenAPI XData block might contain this:

          "post":{
            "description":"Creates a new pet in the store.  Duplicates are allowed",
            "operationId":"addPet",
            "produces":[
              "application/json"
            ],
            ...
    
    Copy code to clipboard

    Add the x-ISC_CORS property as follows:

          "post":{
            "description":"Creates a new pet in the store.  Duplicates are allowed",
            "operationId":"addPet",
            "x-ISC_CORS":true, 
            "produces":[
              "application/json"
            ],
            ...
    
    Copy code to clipboard
  3. Compile the specification class. This action regenerates the dispatch class, causing the actual change in behavior. It is not necessary to understand the dispatch class in detail, but notice the following changes:

    • It now contains your value for the HandleCorsRequest parameter.

    • The URLMap XData block now includes Cors="true" for the <Route> element that corresponds to the operation you modified.

If the HandleCorsRequest parameter is 0 (the default), then CORS header processing is disabled for all calls. In this case, if the REST service receives a request with the CORS header, the service rejects the request.

Important:

An IRIS REST service supports the OPTIONS request (the CORS preflight request), which is used to determine whether a REST service supports CORS. This request is always sent unauthenticated and 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.

Defining How to Process the CORS Header

When you enable a REST service to accept the CORS header, by default, the service accepts any CORS request. Your REST service should examine the CORS requests and decide whether to proceed. For example, you can provide a white-list containing domains that contain only trusted scripts. To do this, you need to:

The net result is that the dispatch class inherits from your custom class instead of from %CSP.REST and thus uses your definition of OnHandleCorsRequest(), which overrides the default CORS header processing.

Defining OnHandleCorsRequest()

In your subclass of %CSP.REST, define the OnHandleCorsRequest() method, which needs to examine the CORS requests and set the response header appropriately.

To define this method, you must be familiar with the details of the CORS protocol (not discussed here).

You also need to know how to examine the requests and set the response headers. For this, it is useful to examine the method that is used by default, the HandleDefaultCorsRequest() method of %CSP.REST. This section explains how this method handles the origin, credentials, header, and request method and suggests variations. You can use this information to write your OnHandleCorsRequest() method.

The following code gets the origin and uses it to set the response header. One possible variation is to test the origin against a white list. Then the domain is allowed, set the response header. If not, 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) 
Copy code to clipboard

The following lines specify that the authorization header should be included.

    #; Set allow credentials to be true
    Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Credentials","true")
Copy code to clipboard

The following lines get the headers and the request method from the incoming request. Your code should test if the headers and request method are allowed. If they are allowed, use them to set the response headers. If not, set the response header to an empty string.

    #; 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)
Copy code to clipboard
Important:

The default CORS header processing is not suitable for REST services handling confidential data.

Modifying the Specification Class

After defining your custom subclass of %CSP.REST including an implementation of the OnHandleCorsRequest(), do the following:

  1. Edit the OpenAPI XData block in the specification class so that the info object contains a new property named x-ISC_DispatchParent. The value of this property must be the fully qualified name of your custom class.

    For example, suppose that the OpenAPI XData block looks like this:

      "swagger":"2.0",
      "info":{
        "version":"1.0.0",
        "title":"Swagger Petstore",
        "description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
        "termsOfService":"http://swagger.io/terms/",
        "contact":{
          "name":"Swagger API Team"
        },
    ...
    Copy code to clipboard

    Suppose that the custom subclass of %CSP.REST is named test.MyDispatchClass. In this case, you would modify the XData block as follows:

      "swagger":"2.0",
      "info":{
        "version":"1.0.0",
        "title":"Swagger Petstore",
        "description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
        "termsOfService":"http://swagger.io/terms/",
        "x-ISC_DispatchParent":"test.MyDispatchClass",
        "contact":{
          "name":"Swagger API Team"
        },
    ...
    Copy code to clipboard
  2. Compile the specification class. This action regenerates the dispatch class. You will notice that the class now extends your custom dispatch superclass. Thus it will use your OnHandleCorsRequest() method.