Política de Cross-Origin
Published: 2020-04-19 03:04:00

É muito comum uma pessoas que desenvolvem Front-End se depararem com um erro frequente que é parecido com:

> Origin http://localhost is not allowed by Access-Control-Allow-Origin

E então vamos procurar na internet como resolver esse problema e nos deparamos com diversas soluções e cada uma funcionando de forma diferente e as vezes só copiamos e colamos e não entendemos muito bem.

![Resposta do StackOverflow falando para adicionar um header no servidor](https://dev-to-uploads.s3.amazonaws.com/i/pq26lhu9yzw6qwk1o6ym.png)
<figcaption>Resposta do StackOverflow falando para adicionar um header no servidor</figcaption>

Pensando neste exemplo de resposta do StackOverflow, a recomendação é adicionar um header no servidor e tudo será resolvido. Se alguém que passou por essa questão e fez apenas o que foi sugerido na imagem, acredito que não resolveu 100% do problema (sim, já aconteceu comigo). Além disso, nem sempre temos acesso a este servidor.

![Resposta do StackOverflow para abrir o chrome em modo sem segurança](https://dev-to-uploads.s3.amazonaws.com/i/ebphv65de3z1uj3tm5ig.png)
<figcaption>Resposta do StackOverflow para abrir o chrome em modo sem segurança</figcaption>

Neste outro caso, é recomendado que a pessoa abra o navegador com a segurança desativada que os requests vão funcionar. Já testei esse modo também e é incrível! Tudo funciona perfeitamente! Mas será que todos os usuários do meu site vão fazer isso também? É, acho que não.

Neste artigo vou mostrar um pouco do que é essa política cross-origin, mais conhecida como CORS.

* * *

## O que é CORS?

**C**ross-**O**rigin **R**esource **S**haring é a sigla atribuída ao mecanismo dos navegadores que gerencia o compartilhamento de recursos entre diferentes origens.

Por padrão, quando fazemos uma requisição através do [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) ou utilizando a [API Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) do javascript, os navegadores utilizam a política _same-origin_ que só autoriza a troca de recursos entre as mesmas origens (domínios). Se for necessário essa troca entre diferentes origens, é necessário configurar as chamadas para que contenham os cabeçalhos CORS corretos.

A utilização do CORS é importante porque além de poder restringir QUEM pode acessar os recursos de um servidor também pode especificar COMO esses recursos devem ser acessados. Mais para frente retornaremos nesta parte com exemplos.

## O que é uma origem?

Uma origem, ou domínio é composto por três partes: protocolo, host e porta.

![Definição de Origem ou Domínio: conjunto de protocolo, host e porta](https://dev-to-uploads.s3.amazonaws.com/i/yrz87dfb1xr4satxoj6x.png)
<figcaption>Definição de Origem ou Domínio: conjunto de protocolo, host e porta</figcaption>

Pegando como exemplo o domínio _http://exemplo.com_ acima temos como protocolo HTTP, o host exemplo.com e a porta (oculta) 80. Se for necessário obter um recurso que mude qualquer um destes 3 itens, já não está dentro da mesma origem e, portanto a política same-origin não é mais válida e por isso precisamos configurar as chamadas para utilizarmos a política _cross-origin_.

![Políticas de compartilhamento de recursos](https://dev-to-uploads.s3.amazonaws.com/i/dn7eqlsio49fvj6xunef.png)
<figcaption>Políticas de compartilhamento de recursos</figcaption>

* * *

## Como funciona o CORS?

O mecanismo funciona adicionando cabeçalhos HTTP nas respostas dos servidores para que estabeleçam um compartilhamento de recursos seguro entre diferentes origens. Com ele é possível configurar as origens permitidas para acessar o recurso e quais métodos (GET, POST, PUT, DELETE, etc.) elas podem utilizar.

Além disso, quando o site faz uma requisição que pode causar **efeitos colaterais** no servidor, o navegador realiza uma “pré-requisição” ou _preflight request_ para que o servidor retorne que é possível realizar a requisição que é pedida originalmente e só então ela é enviada ao servidor.

![Preflight — Simulação de uma conversa entre o Servidor A e o Servidor B](https://dev-to-uploads.s3.amazonaws.com/i/3118pmzuol3f1hx5wlpu.png)
<figcaption>Preflight — Simulação de uma conversa entre o Servidor A e o Servidor B</figcaption>

A figura acima mostra como seria essa requisição se ela fosse uma conversa entre humanos. O Servidor A está precisando realizar uma ação no Servidor B que causa um efeito colateral nos dados do Servidor B e por isso, é necessário fazer a pré-chamada que é o mesmo que pedir autorização para fazer a ação 𝞪 no recurso 𝞫 sendo a origem 𝟃.

Mas então, pra toda requisição que meu site faz, na verdade são feitas duas requisições? A preflight e a original? Bom, na verdade existem algumas condições que necessitam de fazer as pré-chamadas. Portanto existem dois tipos de requisições:

- as requisições simples
- as requisições que exigem preflight.

### Requisições Simples

Uma requisição simples é basicamente uma requisição que, em teoria não causa efeitos colaterais no servidor. Além disso tem que atender a todas as condições abaixo:

- Métodos permitidos: GET, HEAD, POST (com ressalvas relacionadas ao cabeçalho, como mostra os próximos tópicos)
- Além dos cabeçalhos pré-configurados pela conexão, apenas alguns outros são permitidos, como: Accept, Accept-Language, Content-Language, Content-Type (com ressalvas), DPR, Downlink, Save-Data, Viewport-Width, Width
- Valores permitidos para Content-Type: application/x-www-form-urlencoded, multipart/form-data, text/plain

Nos códigos abaixo podemos verificar exemplos de chamada de uma requisição simples e como ela deve ser tratada no servidor.

```javascript
fetch("http://localhost:8080", {
  method: "GET"
});
```

_Requisição simples — Cliente_

```javascript
app.get("/", (req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
  res.send("Hello Cors Test");
});
```

_Requisição simples — Servidor_

Nesta chamada podemos verificar os cabeçalhos da requisição e não é necessário fazer a pré-requisição pois está dentro das condições anteriores. Contudo, mesmo nas chamadas simples, precisamos configurar o **CORS** como podemos ver no cabeçalho _Access-Control-Allow-Origin_.

![Cabeçalhos — Requisição simples](https://dev-to-uploads.s3.amazonaws.com/i/jw4a7tqc9wliydquqqgy.png)
<figcaption>Cabeçalhos — Requisição simples</figcaption>

### Requisições que exigem pré-requisições

Quando a requisição não atende às condições citadas acima, ela precisa enviar uma **pré-requisição** que nada mais é que um **OPTIONS** com algumas informações sobre a requisição original.

```javascript
fetch("http://localhost:8080", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: { key: "value" }
});
```

_Requisição preflight — Cliente_

No caso dessas requisições é necessário configurar os cabeçalhos para a chamada **OPTIONS** porque ela que realiza a autorização da requisição original.

```javascript
app.options("/", (req, res) => {
  res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");
  res.send(true);
});

app.post("/", (req, res) => {
  res.send("POSTED on root");
});
```

_Requisição preflight — Servidor_

As imagens abaixo mostram os cabeçalhos da pré-chamada e da chamada efetiva respectivamente. É possível reparar que, na parte do _Request Headers_ o cliente envia ao servidor que deseja adicionar um cabeçalho não autorizado por padrão — _content-type_ — e também uma ação que pode causar um efeito colateral — _POST_ — e por isso é preciso enviar esses cabeçalhos requisitando esses recursos. Na parte de _Response Headers_ é possível observar que o servidor **autoriza** a origem http://localhost:3000 a acessar seu recurso e também autoriza o cliente a adicionar o cabeçalho content-type.

| Preflight Headers | Post Headers |
| --- | --- |
| ![](https://dev-to-uploads.s3.amazonaws.com/i/wvwygd502tdeq56lhkdr.png) | ![](https://dev-to-uploads.s3.amazonaws.com/i/jf9ffm78c1l5bz5w30ne.png) |
<figcaption>Cabeçalhos — Preflight e Efetivo</figcaption>

* * *

## Boas práticas utilizando CORS

É claro que quando conhecemos mais sobre o que estamos usando, configurando e trabalhando consequentemente já teremos uma nova visão e um senso crítico para tal. Mas como tudo, as configurações de **CORS** também exigem algumas boas práticas para manter a segurança e consistência da comunicação entre diferentes origens.

- Na configuração de _Access-Control-Allow-Origin_, ao invés de utilizar ”\*” que libera qualquer origem de acessar o seu servidor, coloque exatamente a(s) origens que realmente devem ter acesso.
- O princípio do **CORS** é manter sua conexão segura, e por isso se liberarmos qualquer origem, qualquer método e/ou qualquer cabeçalho, este princípio não será atingido. Portanto precisamos deixar nossas requisições seguras e apenas autorizar as origens, métodos e cabeçalhos necessários para a conexão.
- Existem alguns _workarounds_/gambiarras, como vimos nos posts do stack overflow, ou resolver apenas no lado do cliente usando técnicas como jsonp do jquery e _no-cors_ na [API Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) (não é recomendável). O ideal é estar configurado com o mínimo de abertura, apenas o necessário.

## Conclusão

Bom, o objetivo desse artigo é compartilhar alguns conhecimentos que adquiri ao me deparar com problemas relacionados a política cross-origin e, como sei que pra mim foi difícil entender o problema e encontrar soluções, imagino que outras pessoas também tenham essa dificuldade e por isso eu resolvi ajudar com esse texto. Espero que tenham gostado e comentários e/ou informações a acrescentar são sempre bem vindos!

## Referências

- [Cross-Origin Resource Sharing — Mozilla](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
- [Enable CORS](https://enable-cors.org/index.html)

Agradecimento especial a [Vanessa](https://medium.com/@vanessametonini) e [Diogo](https://medium.com/@drkbral) por revisarem esse texto ❤