Skip to content

Instantly share code, notes, and snippets.

@gabmontes
Created November 24, 2015 11:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabmontes/e90fb45e2340e1dd20e8 to your computer and use it in GitHub Desktop.
Save gabmontes/e90fb45e2340e1dd20e8 to your computer and use it in GitHub Desktop.

CORS, Express y AngularJS

Cuando una página web realiza un pedido a un recurso en otro dominio (diferente protocolo, subdiminio, dominio o puerto), realiza lo que se llama un "pedido HTTP cross-origin". Por razones de seguridad, los navegadores restringen los pedidos HTTP cross-origin que se inician desde un script (JavaScript), como pueden ser los realizados utilizando el objeto XMLHttpRequest. Para realizar pedidos legítimos a diferentes dominios, hay que implementar el mecanismo CORS o Cross Origin Resource Sharing.

El estándar CORS consiste en agregar headers específicos en los pedidos HTTP y en las respuestas correspondientes para que el servidor informe al navegador qué recursos están disponibles. Solo aquellos recursos disponibles serán accesibles desde dicho navegador.

Adicionalmente, ciertos pedidos como PUT o DELETE deben ser precedidos de un pedido del tipo OPTIONS, llamado preflight, que debe ser aprobado por el servidor antes del envío del pedido original.

Pedido simple

Un pedido simple es aquel que usa verbos GET, POST o HEAD y no contiene headers especiales. Estos pedidos tienen esta forma:

GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]

El header de respuesta Access-Control-Allow-Origin es el que indica si el recurso es accesible. Al ser *, el acceso está garantizado. Si se deseara, en cambio, bloquar el acceso, se podría especificar el valor http://foo.example.

Pedidos preflight

Si, en cambio, se usa otro verbo o se incluyen headers propios, el pedido se vería así:

OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 1728000
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache
<?xml version="1.0"?><person><name>Arun</name></person>

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
[payload]

En este caso, el pedido OPITONS pide autorización utilizando los headers Access-Control-Request-Method y Access-Control-Request-Headers. La respuesta, autioriza respondiendo con los headers Access-Control-Allow-Origin, Access-Control-Allow-Methods y Access-Control-Allow-Headers.

Pedidos con credenciales

En particular, las cookies y los headers de autenticación no son enviados en pedidos cross-origin. Para permitir el envío de esta información, es necesario activar la propiedad withCredentials del objeto XMLHttpRequest.

Es importante notar que cuando el servidor contesta un pedido con credenciales, debe especificar el dominio en el header Access-Control-Allow-Origin y no responder solo con *.

Módulo cors de Express

El módulo cors que se encuentra en npm, expone un middleware que facilita la operatoria con los pedios y las respuestas que deben seguir el estandard CORS.

Se instala vía npm:

npm install cors

Para habilitar los pedidos CORS a todo nivel, solo se necesita incluir el middleware de esta manera:

var cors = require('cors');

app.use(cors());

Para más información y opciones de configuración, referirse a la documentación de dicho módulo.

CORS y AngularJS

Para que los pedidos relizados con el servcio $http incluyan credenciales (cookies, etc), es necesario incluir la opción withCredentials: true en cada pedido:

$http({
  method: 'GET',
  url: 'server.com',
  withCredentials: true
});

También es posible configurar el envío de credenciales de manera global durante la etapa de configuración de AngularJS:

app.config(function($httpProvider) {
  $httpProvider.defaults.withCredentials = true;
});

Más información

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment