If you develop web applications (especially apps, which interact with remote APIs), you saw errors in the browser console about CORS. That is because your web browser by default does not permit you to load resources, coming outside the same origin. You need to tell it, that it is safe – and this is implemented using CORS (Cross-Origin-Resource-Sharing) protocol.
Technically, the server application has to write certain response headers, telling to the browser, that the origin is trusted and is allowed to get resources from the server. CORS is a powerful security mechanism, which can include or exclude certain methods, headers or even MIME types from the access by the certain origin and you can use it as a big part of your security policy. Eclipse Vertx includes, as a part of the web layer, the special type of handler –
CorsHandler that allows to specify a CORS configuration and to use it with all or certain HTTP routes.
What does CORS mean?
Imagine, that you are working on an web application, that consists of two separate components: REST API and front end app. This client application communicates with the remote server by sending HTTP requests (for example, using fetch). From a technical point of view, these parts reside on different domains (e.g. domain A for client, domain B for server), and the web browser does not permit you to access resources from the different origin. So, in other words, you need to tell the browser, that the server’s origin is safe.
This task is implemented using CORS (stands for Cross Origin Resource Sharing) mechanism. In a nutshell it relies on specific headers, which are attached by the server, and specify who can access that resources. Moreover, CORS protocol allows to define not only the recevier itself (origin), but also can oermit or reject certain HTTP methods, MIME types or headers. Developers also can use CORS requests to inform clients, what types of credentials are required (e.g. Basic authentication or cookies).
Types of CORS requests
The standard defines two types of CORS requests: simple requests and so-called preflighted requests. Basically, we can say that all requests, that do not meet criteria for simple requests are preflighted requests. That got their name due to the fact, that they require a client to send preflight
OPTIONS request before executing requests in order to check the server’s security policy.
In this post we will briefly overview both types of CORS requests. As we are interested more in how it is done in Vertx, I do a brief explanation of concepts. I really encourage you to follow links and check an actual standard to get a solid understanding of the CORS protocol.
From a technical point of view, not all requests ask frontend apps to send a checking request. If the HTTP request meets certain requirements, it is considered as simple and can be used directly. Although, simple requests also put some limitations, that we will also observe later.
So, what kind of criteria will make a simple request? They can be summarized into three categories: method, content-type and headers:
- Methods: only
- For the content type you can utilize only certain values:
- Apart of headers, that are set automatically (for example,
Content-Lengthor other headers, that are known in a standard as a forbidden header name), you can use only those headers, which are listed as safelisted headers. This includes
Content-Languageand some others.
Basically all cross-origin mechanism for simple requests requires you to just put an
Origin header (which is placed automatically). The server validates the origin and added to the response an another header –
Access-Control-Allow-Origin. Its value can be either same as
* (that is not allowed if the request requires cookies). Otherwise, the response is considered as an error. Take a look on the graph below, which represents a process of simple requests:
Besides these listed requirements, simple requests place a number of limitations. First of all, this does not allow you to work with REST API, as you can not send/receive JSON data or to use JWT tokens as an authentication measure. Moreover, if your application uses custom headers, they are also prohibited.
The second issue is that the server also limits types of headers it can write to the response. For example, if you send a file at the response you need to remember, that the
Content-Length header is not placed by the server. So, if you want to measure a download progress on the client side, you need to explicitly allow this header. It can be done by writing
Access-Control-Expose-Headers header’s value by the server.
In other words, if you want to access other functionality (and for REST API you basically do not have a choice) you need to consider preflighted CORS requests. We will observe them in the next subsection.
As we already discussed, this kind of requests received its name, because it requires a client to execute a preflight request in order to check the security policy. This can be done using the
OPTIONS method in order to assert that the actual request is safe to execute. The server in its turn will validate this “pre-request” and either allows of disallows the actual one.
Take a look on this diagram below, which shows a process of the preflighted requests:
From a technical point of view, the preliminary
OPTIONS request “asks” the server for allowed for the actual request methods (in the
Accept-Control-Request-Method header) and headers (
Access-Control-Request-Headers). In its turn, the server writes the list of allowed methods (inside the
Access-Control-Allow-Methods header), allowed headers (in the
Access-Control-Allow-Header). Finally, the server has to specify an allowed origin (
Access-Control-Allow-Origin) – as in the case of simple requests it can be either
* (any origin – you should not use it unless you write a public API) or a fully qualified name of the client.
That is a helicopter view of the CORS protocol. Now, as we are equipped with a theory, we can proceed to practice and to observe how to set up CORS configuration in Vertx.
Configure CORS in Vertx
In order to work with CORS configuration we need to work with a router. This component allows not only set up routes and their handlers, but also to define configurations, like authentication, logging or CORS. This is done via special handlers.
The Vertx Web library includes the
CorsHandler – a component, that is used for an implementation of a server-side CORS mechanism. It is very simple to use – basically, we need to complete three steps:
- Create a new CORS handler instance for a route (or pattern)
- Set up configuration parameters, like allowed methods, origins, headers
- Attach a handler to the router
We could obtain a new CORS handler using a static factory method
create(), which accepts an allowed origin pattern as an argument (with regard to all routes use
CorsHandler handler = CorsHandler.create("*");
Then, we use the handler instance to define configuration. Note, that the class API allows to specify a single value for a parameter (like a method –
allowedMethod()) as well to specify a set of values (like a method
allowedMethods()). In the second approach, methods accept a set of objects.
// specify a set of values Set<HttpMethod> allowedMethods = Set.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.GET, HttpMethod.DELETE); handler.allowedMethods(allowedMethods); // specify by a single value handler.allowedHeader("X-ACCOUNT-ID"); handler.allowedHeader("X-USER-ID");
Finally, we need to attach this handler to an application router.
Router router = Router.router(vertx); //... set up routes router.route().handler(handler);
CORS mechanism is a header-based protocol, that is a powerful part of your application’s security policy. In order to implement it, your server have to expose a certain CORS configuration for clients. If you are using Vertx framework to develop your REST API it is simple to do that. All you need to provide a special config handler for the application’s router and to provide a set of allowed methods/headers. In this post we observed how to do it.