Skip to content
Chris Kent edited this page Apr 2, 2020 · 4 revisions

Cross-Origin Resource Sharing (CORS) is a standard that allows scripts running in a web page to make requests to origins that are different from their own.

For example, a script in a page served from www.example.com would not normally be allowed to make an HTTP request to api.example.com. The browser security model and the same-origin policy forbids it.

CORS defines a set of headers that the server can return to remove this restriction. In our example, the server at api.example.com can return CORS headers to say that it allows requests from www.example.com. When it receives these headers the browser will allow the script to make the request.

This document assumes a basic understanding of CORS and the different types of CORS requests (simple and preflighted). Please see the Mozilla documentation for a full explanation of CORS.

CORS in Osiris

Osiris has built-in support for generating CORS headers and responding to preflight OPTIONS requests. It provides a way to define the CORS headers in a single place so they are added to the response for every CORS-enabled endpoint in the API.

The built-in CORS support is useful for APIs where a large number of endpoints must support CORS and especially for APIs that must support preflighted requests.

In simple cases where there are not many CORS endpoints and they do not need preflight requests it is also an option to add the CORS headers to the response manually instead of using the built-in CORS support.

The cors Flag and cors Block

CORS support in Osiris is enabled by doing two things:

  • Setting the cors flag on the API or endpoint
  • Defining a cors block in the API definition

The cors Flag

The cors flag is a boolean that indicates an endpoint should be CORS-enabled. The flag can be set on the API, in which case all endpoints are CORS-enabled, or it can be set on individual endpoints.

Setting cors = true on the API

// CORS is enabled by default for all endpoints in the API
val api = api<FooComponents>(cors = true) {
    
    // This endpoint is CORS-enabled
    get("/foo") {
    }
    
    // This endpoint is not CORS-enabled
    get("/bar", cors = false) {
    }
}

Setting cors = true on the endpoints

// CORS is not enabled by default for endpoints in the API
val api = api<FooComponents> {
    
    // This endpoint is CORS-enabled
    get("/foo", cors = true) {
    }
    
    // This endpoint is not CORS-enabled
    get("/bar") {
    }
}

Setting the cors flag for an endpoint has two effects:

  • OPTIONS requests are enabled for the path to support preflighted requests
  • The CORS headers defined in the cors block are added to the response (see below)

The cors Block

The cors block in an API definition specifies the CORS headers that should be added to the response for all CORS-enabled endpoints. There are three properties that can be set in the cors block, corresponding to the standard CORS headers. These are allowOrigin, allowMethods and allowHeaders. For example:

val api = api<FooComponents>(cors = true) {

    cors { req ->
        allowMethods = setOf(HttpMethod.POST)
        allowOrigin = setOf("*")
        allowHeaders = setOf(HttpHeaders.CONTENT_TYPE)
    }

    get("/foo") {
    }
}

These headers are added to the responses for all requests to a CORS-enabled endpoint. This includes simple CORS requests and also preflight OPTIONS requests. It is not necessary to manually define options handlers to handle preflight requests. Osiris takes care of this.

The request is passed to the cors block (the req parameter in the example above). This allows different headers to be generated for different endpoints. In most cases (including the example above) this isn't necessary, and the same headers are returned for all endpoints. In that case the req parameter can be omitted.

Manually Generating CORS Headers

In simple cases where there are a small number of CORS endpoints and the requests are all simple then it can be simpler to define the CORS headers in the handler instead of using a cors block. For example:

val api = api<FooComponents> {

    // The cors flag is not set for the endpoint so Osiris doesn't handle CORS
    get("/foo") { req ->
        req.responseBuilder()
            // Add a CORS header to allow requests from www.example.com
            .header("Access-Control-Allow-Origin", "www.example.com")
            .build("hello, world!")
    }
}

It is possible to handle preflighted requests in the same way, but in that case you must define an options handler for every path that must handle preflight CORS requests.

Clone this wiki locally