# Habilitando CORS (Compartilhamento de Recursos entre Origens) no Sisk

Source: https://docs.sisk-framework.org/pt-br/docs/features/cors.html

Sisk possui uma ferramenta que pode ser útil para lidar com [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) ao expor seu serviço publicamente. Esse recurso não faz parte do protocolo HTTP, mas é uma funcionalidade específica dos navegadores web definida pelo W3C. Esse mecanismo de segurança impede que uma página web faça requisições a um domínio diferente daquele que forneceu a página. Um provedor de serviço pode permitir que certos domínios acessem seus recursos, ou apenas um.

## Mesma Origem

Para que um recurso seja identificado como "mesma origem", uma requisição deve indicar o cabeçalho [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin) em sua solicitação:

```http
GET /api/users HTTP/1.1
Host: example.com
Origin: http://example.com
...
```

E o servidor remoto deve responder com um cabeçalho [Access-Control-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) contendo o mesmo valor da origem solicitada:

```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
...
```

Essa verificação é **explícita**: o host, a porta e o protocolo devem ser os mesmos da solicitação. Confira o exemplo:

- Um servidor responde que seu `Access-Control-Allow-Origin` é `https://example.com`:
    - `https://example.net` - o domínio é diferente.
    - `http://example.com` - o esquema é diferente.
    - `http://example.com:5555` - a porta é diferente.
    - `https://www.example.com` - o host é diferente.

Na especificação, apenas a sintaxe é permitida para ambos os cabeçalhos, tanto em requisições quanto em respostas. O caminho da URL é ignorado. A porta também é omitida se for a porta padrão (80 para HTTP e 443 para HTTPS).

```http
Origin: null
Origin: <scheme>://<hostname>
Origin: <scheme>://<hostname>:<port>
```

## Habilitando CORS

Nativamente, você tem o objeto [CrossOriginResourceSharingHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.md) dentro do seu [ListeningHost](https://docs.sisk-framework.org/api/Sisk.Core.Http.ListeningHost.md).

Você pode configurar o CORS ao inicializar o servidor:

```csharp
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UseCors(new CrossOriginResourceSharingHeaders(
            allowOrigin: "http://example.com",
            allowHeaders: ["Authorization"],
            exposeHeaders: ["Content-Type"]))
        .Build();

    await app.StartAsync();
}
```

O código acima enviará os seguintes cabeçalhos para **todas as respostas**:

```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Headers: Authorization
Access-Control-Expose-Headers: Content-Type
```

Esses cabeçalhos precisam ser enviados em todas as respostas para um cliente web, incluindo erros e redirecionamentos.

Você pode notar que a classe [CrossOriginResourceSharingHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.md) possui duas propriedades semelhantes: [AllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigin.md) e [AllowOrigins](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigins.md). Observe que uma está no plural, enquanto a outra está no singular.

- A propriedade **AllowOrigin** é estática: apenas a origem que você especificar será enviada em todas as respostas.
- A propriedade **AllowOrigins** é dinâmica: o servidor verifica se a origem da requisição está contida nesta lista. Se for encontrada, ela é enviada na resposta daquela origem.

Quando a origem permitida depende da requisição, isto é, quando você usa **AllowOrigins** ou o valor [AutoAllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoAllowOrigin.md) descrito abaixo, o Sisk também envia o cabeçalho `Vary: Origin` para que os caches não reutilizem uma resposta para uma origem diferente.

### Curingas e cabeçalhos automáticos

Alternativamente, você pode usar um curinga (`*`) na origem da resposta para especificar que qualquer origem tem permissão para acessar o recurso. Contudo, esse valor não é permitido para requisições que contenham credenciais (cabeçalhos de autorização) e essa operação [resultará em um erro](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors/CORSNotSupportingCredentials).

Você pode contornar esse problema listando explicitamente quais origens serão permitidas através da propriedade [AllowOrigins](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigins.md) ou também usar a constante [AutoAllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoAllowOrigin.md) no valor de [AllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigin.md). Essa propriedade mágica definirá o cabeçalho `Access-Control-Allow-Origin` com o mesmo valor do cabeçalho `Origin` da requisição.

Você também pode usar [AutoFromRequestMethod](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoFromRequestMethod.md) e [AutoFromRequestHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoFromRequestHeaders.md) para um comportamento semelhante ao `AllowOrigin`, que responde automaticamente com base nos cabeçalhos enviados.

```csharp
using var host = HttpServer.CreateBuilder()
    .UseCors(new CrossOriginResourceSharingHeaders(
        
        // Responde com base no cabeçalho Origin da requisição
        allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
        
        // Responde com base no cabeçalho Access-Control-Request-Method ou no método da requisição
        allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],

        // Responde com base no cabeçalho Access-Control-Request-Headers ou nos cabeçalhos enviados
        allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],

        exposeHeaders: [HttpKnownHeaderNames.ContentType, "X-Authenticated-Account-Id"],
        allowCredentials: true))
    .Build();
```

## Outras Formas de Aplicar CORS

Se você está lidando com [service providers](https://docs.sisk-framework.org/pt-br/docs/extensions/service-providers.md), pode sobrescrever valores definidos no arquivo de configuração:

```csharp
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UsePortableConfiguration(...)
        .UseCors(cors => {
            // Substituirá a origem definida na configuração
            // do arquivo.
            cors.AllowOrigin = "http://example.com";
        })
        .Build();

    await app.StartAsync();
}
```

## Desabilitando CORS em Rotas Específicas

A propriedade `UseCors` está disponível tanto para rotas quanto para todos os atributos de rota e pode ser desativada com o exemplo a seguir:

```csharp
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    // GET /api/widgets/colors
    [RouteGet("/colors", UseCors = false)]
    public IEnumerable<string> GetWidgets() {
        return new[] { "Green widget", "Red widget" };
    }
}
```

## Substituindo Valores na Resposta

Você pode substituir ou remover valores explicitamente em uma ação de roteador:

```csharp
[RoutePrefix("api/widgets")]
public class WidgetController : Controller {

    public IEnumerable<string> GetWidgets(HttpRequest request) {

        // Remove o cabeçalho Access-Control-Allow-Credentials
        request.Context.OverrideHeaders.AccessControlAllowCredentials = string.Empty;
        
        // Substitui o cabeçalho Access-Control-Allow-Origin
        request.Context.OverrideHeaders.AccessControlAllowOrigin = "https://contorso.com";

        return new[] { "Green widget", "Red widget" };
    }
}
```

## Requisições Preflight

Uma requisição preflight é uma requisição do método [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/OPTIONS) que o cliente envia antes da requisição real.

Quando uma requisição com o método `OPTIONS` corresponde ao caminho de uma rota, mas nenhuma rota está configurada explicitamente para `OPTIONS`, o servidor Sisk responde com um `200 OK` e os cabeçalhos CORS aplicáveis, e então o cliente pode prosseguir com a requisição real.

- Se uma rota estiver mapeada explicitamente com [RouteMethod.Options](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMethod.md), por exemplo com `MapOptions`, essa rota trata a requisição em vez da resposta automática.
- Se a rota que corresponde ao caminho tem `UseCors = false`, a resposta automática ainda é enviada, porém sem os cabeçalhos CORS.

> [!NOTE]
> Antes do Sisk 1.7, a resposta automática de preflight sempre continha os cabeçalhos CORS, mesmo para rotas com `UseCors = false`.

## Desabilitando CORS Globalmente

Não é possível fazer isso. Para não usar CORS, simplesmente não o configure.
