All CORS explained from A to Z
Tags: webdev, cors, http, backend
Published: 2023-06-24 08:54:05

## CORS definition

Before defining CORS, some fundamental concepts to understand CORS will be defined.

### origin

Given a URL, the origin is the first part that is made up of the scheme: the host and the port (if present).

> source = scheme + host + port

For example, in `https://localhost:8080/surveys` the source is `https://localhost:8080` and `/survey` is the path

### same-origin

It refers to the case where the client makes a request to a server and the server has the same origin as the client.

```
https://aurora.net/home -> https://aurora.net/api/pos?id=290
```

### cross-origin (cross-origin)

The client makes a request to a server whose origin is different from that of the client.

```
https://localhost:8080/home -> https://aurora.net/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:8080/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:3000/api/pos?id=290
```

### Same-origin policy and CORS

By default and by security policy, browsers only allow HTTP requests to the same origin, that is, to the same domain as the page that makes the request.

CORS (Cross-Origin Resource Sharing) is the mechanism by which browsers allow a request to be made from one origin to a different one, cross-origin.

In order to make a CORS request, the client does not have to do anything, it simply launches the request. The browser makes the request and it is the server that must respond if it accepts requests from that origin. It does this by sending the `Access-Control-Allow-Origin` header response. This header can have an asterisk (`*`) or a specific origin. The `*` means that the server accepts requests from any origin.
The browser reviews the browser's response. If you have the `Access-Control-Allow-Origin` header and it has the value `*` or the origin matches the client origin, then the browser accepts the response. Otherwise, it discards it and generally displays a CORS-descriptive error on the console.

```
 1. browser with origin X makes a request to origin Y
 2. server responds
 3. If the response from the server includes the header `Access-Control-Allow-Origin` Then
 4. If (Access-Control-Allow-Origin == '*') or (Access-Control-Allow-Origin == X) Then
 5. browser accepts the response
 6. If not
 7. discard response and show error
 8. End Yes
 9. If not
10. discard response and show error
11. End Yes
```

> `Access-Control-Allow-Origin` can also be set to `null` for unknown origins (such as accessing from a file instead of a website). This is useful while you are in a development or testing phase, this way you don't need to put '\*' which is less restrictive. `Access-Control-Allow-Origin: null`

## Preflight

Above we have seen in a generic way how a cross-origin request should be treated using CORS. Actually, before making a cross-origin request, browsers that implement CORS will "meta" the actual request.

A preflight is a request that the browser makes before making a CORS request if the request meets one of these criteria:

- Use an HTTP method other than `GET`, `POST`, or `HEAD`
- Set the `Content-Type` header to a value other than:
   -application/x-www-form-urlencoded
   - multipart/form-data
   -text/plain
- Set additional headers other than:
   -`Accept`
   - `Accept-Language`
   - `Content-Language`
- The XMLHttpRequest object contains upload events

> The HTTP methods `GET`, `POST` and `HEAD` are called simple methods. Simple methods do not need to be listed in the `Control-Allow-Methods` header.

The preflight is a request with the `OPTIONS` HTTP method. This "meta" request that the browser makes is for the server to decide how to respond to CORS. In the preflight the browser sends the headers:

- `Origin`
- `Access-Control-Request-Method` (supports only one method)
- `Access-Control-Request-Headers` (only if the headers are not simple. You can have a comma-separated list of headers)

   The server checks if the HTTP origin and method are in its list of allowed origins and methods (it will also check the headers indicated in `Access-Control-Request-Headers`, if any). If this check is favorable, it responds with a status code in the range `200`. The answer should not wear a bodysuit. The headers sent by the server are:

- `Access-Control-Allow-Origin`
- `Access-Control-Allow-Methods` (in this case it can be a comma separated list of commands)
- `Access-Control-Allow-Headers`

Example of preflight request:

```
OPTIONS /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Security-Level, App-Name
```

Example preflight response:

```
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http//localhost:3000
Access-Control-Allow-Methods: GET, DELETE
Access-Control-Allow-Headers: Security-Level, App-Name
```

Request example:

```
GET /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Security-Level: High
App-Name: Survey
```

The previous algorithm, now modified with the preflights, would look like this:

```
  1. the Javascript client whose origin is X, makes a request P to the server with origin Y.
        P contains the HTTP method M and a set of headers C
  2. If M is a simple HTTP method and all C headers are simple Then
  3. send the request P to the server
  4. If not
  5. prepare a preflight request F with HTTP OPTIONS method and a set of headers C' with:
         Origin = X, Access-Control-Request-Method = M
  6. If in C there are headers that are not simple Then
  7. add to C' the Access-Control-Request-Headers header with the list of non-simple C headers
  8. End Yes
  9. send preflight F to server
10 If server responds to preflight with status in the range of 200 Then
11. send the request P to the server
12. If not
13. Check the Access-Control-Allow-Origin response headers,
           Access-Control-Allow-Methods and Access-Control-Allow-Headers to determine which
           are the validations that have failed and report the error
14. End Yes
15. End Yes
```

> Cache. The browser can cache the preflights and thus avoid making an extra request for each client request that would require a preflight. The cache saves on its origin + path entries.

## Canvas and images

For images whose `src` attribute is a different origin than the client, the browser loads the resource and displays it but does not allow manipulations with it such as `toBlob`, `toDataURL`, and `getImageData`.
To enable CORS in the case of images, there is the `crossOrigin` attribute that can take the values `anonymous` or `user-credentials`. `user-credentials` is like `withCredentials` in XMLHttpRequest, ie user credentials (eg cookies) are sent with the request. If it is not necessary to send the credentials, it is preferable to send `anonymous`.

## Cookies and user credentials

By default the browser does not send user credentials as cookies in cross-origin requests. The browser also does not expose all the response headers that the server sends.
That's why CORS has mechanisms to remedy those restrictions: the `Access-Control-Allow-Credentials` and `Access-Control-Expose-Headers` response headers.

So for the use of cookies it is necessary for the client to indicate:

```javascript
xhr.withCredentials = true;
document.cookie = someUniqueId;
```

The server must respond to the preflight and the request with the headers
`Access-Control-Allow-Credentials` and `Access-Control-Allow-Origin`.

When the server sends the `Access-Control-Allow-Credentials` header with the value `true`, it must send the `Access-Control-Allow-Origin` header with a specific origin; `Access-Control-Allow-Origin: *` is invalid because accepting cookies from any origin can cause security problems.

Valid:

```
Access-Control-Allow-Origin: http://localhost:1111
Access-Control-Allow-Credentials: true
```

Invalid:

```
Access-Control-Allow-Origin: \*
Access-Control-Allow-Credentials: true
```

In the case where the client has `xhr.withCredentials = true` and the server responds with `Access-Control-Allow-Origin: true`, the server can set cookies on the client. These cookies will be sent by the browser in successive requests, however, the client will not be able to read these cookies with Javascript.

## Response headers

The XMLHttpRequest object exposes two methods for reading response headers: `getResponseHeader` and `getAllResponseHeaders`. In a same-origin request these methods return all the headers sent by the server. When dealing with a cross-origin request, only the plain headers can be read.

Using the `Access-Control-Expose-Headers` header the server can specify which headers the client can read.

```
Access-Control-Expose-Headers: X-Powered-By
```

> Simple headers:
>
> - `Cache-Control`
> - `Content-Language`
> - `Content-Type`
> - `Expires`
> - `Last-Modified`
> - `Pragma`

## Summary of headers used in CORS

### Request headers (sent by client browser)

- `Origin`: Header added by the browser. Contains the origin of the client in the request. Counterpart: `Access-Control-Allow-Origin`.
- `Access-Control-Request-Method`: Header added by the browser in the preflight. Contains the HTTP method that the client will do on the actual request. Counterpart: `Access-Control-Allow-Methods`.
- `Access-Control-Request-Headers`: Header added by the browser in the preflight. Contains the HTTP headers that the client will send in the actual request. Counterpart: `Access-Control-Allow-Headers`.

### Response headers (sent by the server)

- `Access-Control-Allow-Origin`: The value `*` indicates that all origins are allowed
   A specific value indicates that the server only accepts requests from that source
   The value `null` indicates that it only accepts requests from sources that are not a web site, for example a file (only recommended for development and testing). Counterpart: `Origin`

- `Access-Control-Allow-Credentials`: The value `true` indicates that the server supports the use of user credentials such as cookies.

- `Access-Control-Allow-Methods`: Has a list of HTTP methods accepted by the server.
   It is not necessary to include the simple methods, although it is good practice to do so.
   This header should only be sent in response to preflight requests. Counterpart: `Access-Control-Request-Methods`

- `Access-Control-Allow-Headers`: Has a list of HTTP headers accepted by the server.
   This header should only be sent in response to preflight requests. Counterpart: `Access-Control-Request-Headers`

- `Access-Control-Max-Age`: Its value specifies the maximum time in seconds that it is recommended to keep a preflight cache, although the browser can have its own maximum value.
   This header should only be sent in response to preflight requests.

- `Access-Control-Expose-Headers`: Contains the headers that can be read by the browser. You can have a comma separated list of headers.
   It is optional and is not required for the CORS request to be successful.