# Habilitando CORS (Compartir Recursos entre Orígenes) en Sisk

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

Sisk tiene una herramienta que puede ser útil para manejar [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) al exponer tu servicio públicamente. Esta característica no forma parte del protocolo HTTP sino que es una característica específica de los navegadores web definida por el W3C. Este mecanismo de seguridad impide que una página web realice peticiones a un dominio diferente al que proporcionó la página web. Un proveedor de servicios puede permitir que ciertos dominios accedan a sus recursos, o solo a uno.

## Same Origin

Para que un recurso se identifique como "misma origen", una solicitud debe incluir el encabezado [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin) en su petición:

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

Y el servidor remoto debe responder con un encabezado [Access-Control-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) con el mismo valor que el origen solicitado:

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

Esta verificación es **explícita**: el host, puerto y protocolo deben ser los mismos que los solicitados. Revisa el ejemplo:

- Un servidor responde que su `Access-Control-Allow-Origin` es `https://example.com`:
    - `https://example.net` - el dominio es diferente.
    - `http://example.com` - el esquema es diferente.
    - `http://example.com:5555` - el puerto es diferente.
    - `https://www.example.com` - el host es diferente.

En la especificación, solo se permite la sintaxis para ambos encabezados, tanto en solicitudes como en respuestas. La ruta de la URL se ignora. El puerto también se omite si es un puerto predeterminado (80 para HTTP y 443 para HTTPS).

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

## Enabling CORS

De forma nativa, dispones del objeto [CrossOriginResourceSharingHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.md) dentro de tu [ListeningHost](https://docs.sisk-framework.org/api/Sisk.Core.Http.ListeningHost.md).

Puedes configurar CORS al inicializar el 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();
}
```

El código anterior enviará los siguientes encabezados para **todas las respuestas**:

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

Estos encabezados deben enviarse para todas las respuestas a un cliente web, incluidos errores y redirecciones.

Puede que notes que la clase [CrossOriginResourceSharingHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.md) tiene dos propiedades similares: [AllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigin.md) y [AllowOrigins](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigins.md). Ten en cuenta que una es plural y la otra singular.

- La propiedad **AllowOrigin** es estática: solo el origen que especifiques será enviado para todas las respuestas.
- La propiedad **AllowOrigins** es dinámica: el servidor verifica si el origen de la solicitud está contenido en esta lista. Si se encuentra, se envía en la respuesta de ese origen.

Cuando el origen permitido depende de la solicitud, es decir, cuando utilizas **AllowOrigins** o el valor [AutoAllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoAllowOrigin.md) descrito a continuación, Sisk también envía el encabezado `Vary: Origin` para que las cachés no reutilicen una respuesta para un origen diferente.

### Wildcards and automatic headers

Alternativamente, puedes usar un comodín (`*`) en el origen de la respuesta para especificar que cualquier origen está permitido para acceder al recurso. Sin embargo, este valor no está permitido para solicitudes que incluyen credenciales (encabezados de autorización) y esta operación [resultará en un error](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS/Errors/CORSNotSupportingCredentials).

Puedes solucionar este problema enumerando explícitamente qué orígenes estarán permitidos mediante la propiedad [AllowOrigins](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigins.md) o también usar la constante [AutoAllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoAllowOrigin.md) en el valor de [AllowOrigin](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AllowOrigin.md). Esta propiedad mágica definirá el encabezado `Access-Control-Allow-Origin` con el mismo valor que el encabezado `Origin` de la solicitud.

También puedes usar [AutoFromRequestMethod](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoFromRequestMethod.md) y [AutoFromRequestHeaders](https://docs.sisk-framework.org/api/Sisk.Core.Entity.CrossOriginResourceSharingHeaders.AutoFromRequestHeaders.md) para un comportamiento similar a `AllowOrigin`, que responde automáticamente según los encabezados enviados.

```csharp
using var host = HttpServer.CreateBuilder()
    .UseCors(new CrossOriginResourceSharingHeaders(
        
        // Responde según el encabezado Origin de la solicitud
        allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
        
        // Responde según el encabezado Access-Control-Request-Method o el método de la solicitud
        allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],

        // Responde según el encabezado Access-Control-Request-Headers o los encabezados enviados
        allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],

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

## Other Ways to Apply CORS

Si estás trabajando con [service providers](https://docs.sisk-framework.org/es/docs/extensions/service-providers.md), puedes sobrescribir los valores definidos en el archivo de configuración:

```csharp
static async Task Main(string[] args)
{
    using var app = HttpServer.CreateBuilder()
        .UsePortableConfiguration(...)
        .UseCors(cors => {
            // Sobrescribirá el origen definido en la configuración
            // del archivo.
            cors.AllowOrigin = "http://example.com";
        })
        .Build();

    await app.StartAsync();
}
```

## Disabling CORS on Specific Routes

La propiedad `UseCors` está disponible tanto para rutas como para todos los atributos de ruta y puede desactivarse con el siguiente ejemplo:

```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" };
    }
}
```

## Replacing Values in the Response

Puedes reemplazar o eliminar valores explícitamente en una acción del router:

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

    public IEnumerable<string> GetWidgets(HttpRequest request) {

        // Elimina el encabezado Access-Control-Allow-Credentials
        request.Context.OverrideHeaders.AccessControlAllowCredentials = string.Empty;
        
        // Reemplaza el encabezado Access-Control-Allow-Origin
        request.Context.OverrideHeaders.AccessControlAllowOrigin = "https://contorso.com";

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

## Preflight Requests

Una solicitud de preflight es una petición [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/OPTIONS) que el cliente envía antes de la solicitud real.

Cuando una solicitud con el método `OPTIONS` coincide con la ruta de un endpoint, pero no hay una ruta configurada explícitamente para `OPTIONS`, el servidor Sisk responde con un `200 OK` y los encabezados CORS aplicables, y luego el cliente puede continuar con la solicitud real.

- Si una ruta está mapeada explícitamente con [RouteMethod.Options](https://docs.sisk-framework.org/api/Sisk.Core.Routing.RouteMethod.md), por ejemplo con `MapOptions`, esa ruta maneja la solicitud en lugar de la respuesta automática.
- Si la ruta que coincide con la ruta tiene `UseCors = false`, la respuesta automática aún se envía, pero sin encabezados CORS.

> [!NOTE]
> Antes de Sisk 1.7, la respuesta automática de preflight siempre incluía los encabezados CORS, incluso para rutas con `UseCors = false`.

## Disabling CORS Globally

No es posible hacer esto. Para no usar CORS, simplemente no lo configures.
