Corsica

Plug and DSL for handling CORS requests.

Corsica provides facilites for working with CORS in Plug-based applications. It is (well, tries to be!) compliant with the CORS specification defined by the W3C.

Corsica can be used in two ways:

Generator

The Corsica module can be used in any module in order to turn that module into a plug that handles CORS request. This is useful if there are a lot of different cases to handle (multiple origins, headers based on resources) since it gives fine control over the behaviour of the generated plug.

A quick example may make this easier to understand:

defmodule MyApp.CORS do
  use Corsica,
    origins: "*",
    max_age: 600,
    allow_headers: ~w(X-My-Header)

  resources ["/foo", "/bar"], allow_methods: ~w(PUT PATCH)

  resources ["/users"],
    allow_credentials: true,
    allow_methods: ~w(HEAD GET POST PUT PATCH DELETE)
end

In the example above, the MyApp.CORS module becomes a valid plug since the Corsica module is used. The resources/2 macro is used in order to declare resources (routes) for which the server should respond with CORS headers. Options passed to resources/2 are used to set the values of the CORS headers (for more information on options, read the “Options” section); they override the global options that can be passed to the use macro, which apply to all declared resources.

Given the example above, the MyApp.CORS plug can be used as follows:

defmodule MyApp.Router do
  use Plug.Router
  plug MyApp.CORS
  plug :match
  plug :dispatch

  match _ do
    send_resp(conn, 200, "OK")
  end
end

For more information on declaring single resources, have a look at the Corsica.DSL.resources/2 macro (which is imported when Corsica is used).

Plug

In its simplest form, Corsica can be used as a stand-alone plug too. This prevents from fine-tuning the CORS header on a per-resource basis, but it supports all other options supported by the plug generator and can be useful for simple CORS needs.

defmodule MyApp.Endpoint do
  plug Corsica,
    origins: ["http://foo.com"],
    max_age: 600,
    allow_headers: ~w(X-Header),
    allow_methods: ~w(GET POST),
    expose_headers: ~w(Content-Type)

  plug Plug.Session
  plug :router
end

Origins

Allowed origins can be specified by passing the :origins options either when Corsica is used or when the Corsica plug is plugged to a pipeline.

:origins can be a single value or a list of values. "*" can only appear as a single value. The default value is "*". Origins can be specified either as:

If :origins is a list with more than one value and the request origin matches, then a Vary: Origin header is added to the response.

Options

Besides :origins, the options that can be passed to the use macro, to Corsica.DSL.resource/2 and to the Corsica plug (along with their default values) are:

When used as a plug, Corsica supports the additional :resources options, which is a list of resources exactly like the ones passed to the Corsica.DSL.resources/2 macro. By default, this option is not present, meaning that all resources are CORS-enabled.

Responding to preflight requests

When the request is a preflight request and a valid one (valid origin, valid request method and valid request headers), Corsica directly sends a response to that request instead of just adding headers to the connection (so that a possible plug pipeline can continue). To do this, Corsica halts the connection (through Plug.Conn.halt/1) and sends a 200 OK response with a body of "".

Source

Summary

call(conn, opts)

Callback implementation of Plug.call/2

cors_request?(conn)

Checks whether a given connection holds a CORS request

init(opts)

Callback implementation of Plug.init/1

preflight_request?(conn)

Checks whether a given connection holds a CORS preflight request

Functions

call(conn, opts)

Callback implementation of Plug.call/2.

Source
cors_request?(conn)

Specs:

  • cors_request?(Plug.Conn.t) :: boolean

Checks whether a given connection holds a CORS request.

It doesn’t check if the CORS request is valid: it just checks that it’s a CORS request. A request is a CORS request if and only if it has an Origin request header.

Source
init(opts)

Callback implementation of Plug.init/1.

Source
preflight_request?(conn)

Specs:

  • preflight_request?(Plug.Conn.t) :: boolean

Checks whether a given connection holds a CORS preflight request.

Like cors_request?/1, it doesn’t check that the preflight request is valid: it just checks that it’s a preflight request. A CORS request is considered to be a preflight request if and only if it is an OPTIONS request and it has an Access-Control-Request-Method request header.

Note that this function does not check if the given request is a CORS one. If you want to check for that too, use cors_request?/1.

Source