passport.js RESTful auth

¿Cómo se maneja la autenticación (local y Facebook, por ejemplo) usando pasaporte.js, a través de una API RESTful en lugar de a través de una interfaz web?

Las preocupaciones específicas son manejar el paso de datos de devoluciones de llamadas a una respuesta REST (JSON) vs usar un res.send típico ({data: req.data}), establecer un punto final inicial / de inicio de sesión que redirige a Facebook (/ login no puede ser se accede a través de AJAX, porque no es una respuesta JSON; se trata de un redireccionamiento a Facebook con una callback).

He encontrado https://github.com/halrobertson/test-restify-passport-facebook , pero tengo problemas para entenderlo.

Además, ¿cómo almacena passport.js las credenciales de autenticación? El servidor (¿o es el servicio?) Está respaldado por MongoDB, y espero que las credenciales (inicio de sesión y hash salado de pw) se almacenen allí, pero no sé si pasaporte.js tiene este tipo de capacidad.

Aquí se formulan muchas preguntas, y parece que, aunque las preguntas se formulan en el contexto de Node y passport.js, las preguntas reales son más sobre el flujo de trabajo que sobre cómo hacerlo con una tecnología en particular.

Usemos la configuración de ejemplo de @Keith, modificada un poco para mayor seguridad:

  • El servidor web en https://example.com sirve una aplicación de cliente de JavaScript de una sola página
  • El servicio web RESTful en https://example.com/api proporciona soporte de servidor a la aplicación de cliente enriquecido
  • Servidor implementado en Node y passport.js.
  • El servidor tiene una base de datos (de cualquier tipo) con una tabla de “usuarios”.
  • Nombre de usuario / contraseña y Facebook Connect se ofrecen como opciones de autenticación
  • El cliente enriquecido realiza solicitudes REST en https://example.com/api
  • Puede haber otros clientes (aplicaciones telefónicas, por ejemplo) que usan el servicio web en https://example.com/api pero no conocen el servidor web en https://example.com .

Tenga en cuenta que estoy usando HTTP seguro. Esto es, en mi opinión, una necesidad para cualquier servicio que esté disponible de manera abierta, ya que la información confidencial, como las contraseñas y los tokens de autorización, están pasando entre el cliente y el servidor.

Autenticación de nombre de usuario / contraseña

Veamos cómo funciona la vieja autenticación simple primero.

  • El usuario se conecta a https://example.com
  • El servidor sirve una aplicación de Javascript enriquecida que representa la página inicial. Somehwerere en la página hay un formulario de inicio de sesión.
  • Muchas de las secciones de esta aplicación de una sola página no se han llenado con datos debido a que el usuario no ha iniciado sesión. Todas estas secciones tienen un detector de eventos en un evento de “inicio de sesión”. Todo esto es material del lado del cliente, el servidor no sabe de estos eventos.
  • El usuario ingresa su nombre de usuario y contraseña, y presiona el botón de enviar, lo que activa un controlador de Javascript para registrar el nombre de usuario y la contraseña en las variables del lado del cliente. Luego, este controlador activa el evento de “inicio de sesión”. De nuevo, esta es toda la acción del lado del cliente, las credenciales aún no se enviaron al servidor .
  • Los oyentes del evento de “inicio de sesión” son invocados. Ahora, cada uno de estos debe enviar una o más solicitudes a la API RESTful en https://example.com/api para obtener los datos específicos del usuario para su representación en la página. Cada solicitud que envían al servicio web incluirá el nombre de usuario y la contraseña, posiblemente en forma de autenticación HTTP básica , ya que el servicio RESTful no puede mantener el estado del cliente de una solicitud a la siguiente. Dado que el servicio web está en HTTP seguro, la contraseña se cifra de forma segura durante el tránsito.
  • El servicio web en https://example.com/api recibe un montón de solicitudes individuales, cada una con información de autenticación. El nombre de usuario y la contraseña en cada solicitud se comparan con la base de datos del usuario y, si se encuentran correctos, la función solicitada se ejecuta y los datos se devuelven al cliente en formato JSON. Si el nombre de usuario y la contraseña no coinciden, se envía un error al cliente en forma de un código de error HTTP 401.
  • En lugar de obligar a los clientes a enviar un nombre de usuario y contraseña con cada solicitud, puede tener una función “get_access_token” en su servicio RESTful que toma el nombre de usuario y la contraseña y responde con un token, que es una especie de hash criptográfico único y con vencimiento fecha asociada a ella. Estos tokens se almacenan en la base de datos con cada usuario. Luego, el cliente envía el token de acceso en las solicitudes posteriores. El token de acceso se validará contra la base de datos en lugar del nombre de usuario y la contraseña.
  • Las aplicaciones de cliente que no son de navegador, como las aplicaciones de teléfono, hacen lo mismo que arriba, le piden al usuario que ingrese sus credenciales y luego las envía (o un token de acceso generado a partir de ellas) con cada solicitud al servicio web.

El punto importante de este ejemplo es que los servicios web RESTful requieren autenticación con cada solicitud .

Una capa adicional de seguridad en este escenario agregaría la autorización de la aplicación cliente además de la autenticación del usuario. Por ejemplo, si tiene el cliente web, las aplicaciones iOS y Android que utilizan el servicio web, es posible que desee que el servidor sepa cuál de los tres es el cliente de una solicitud determinada, independientemente de quién sea el usuario autenticado. Esto puede permitir que su servicio web restrinja ciertas funciones a clientes específicos. Para esto, podría utilizar claves y secretos de API, consulte esta respuesta para obtener algunas ideas al respecto.

Autenticación de Facebook

El flujo de trabajo anterior no funciona para Facebook Connect porque el inicio de sesión a través de Facebook tiene un tercero, el propio Facebook. El procedimiento de inicio de sesión requiere que el usuario sea redirigido al sitio web de Facebook donde se ingresan las credenciales fuera de nuestro control.

Entonces veamos cómo cambian las cosas:

  • El usuario se conecta a https://example.com
  • El servidor sirve una aplicación de Javascript enriquecida que representa la página inicial. En algún lugar de la página hay un formulario de inicio de sesión que incluye un botón “Iniciar sesión con Facebook”.
  • El usuario hace clic en el botón “Iniciar sesión con Facebook”, que es solo un enlace al que se redirige (por ejemplo) https://example.com/auth/facebook .
  • La ruta https://example.com/auth/facebook es manejada por pasaporte.js (ver la documentación )
  • Todo lo que el usuario ve es que la página cambia y ahora están en una página alojada en Facebook donde necesitan iniciar sesión y autorizar nuestra aplicación web. Esto está completamente fuera de nuestro control.
  • El usuario inicia sesión en Facebook y otorga permiso a nuestra aplicación, por lo que ahora Facebook redirecciona a la URL de callback que configuramos en la configuración de pasaporte.js, que siguiendo el ejemplo en la documentación es https://example.com/auth/facebook/callback
  • El controlador passport.js para la ruta https://example.com/auth/facebook/callback invocará la función de callback que recibe el token de acceso de Facebook y parte de la información del usuario de Facebook, incluida la dirección de correo electrónico del usuario.
  • Con el correo electrónico podemos ubicar al usuario en nuestra base de datos y almacenar el token de acceso de Facebook con él.
  • Lo último que hace en la callback de Facebook es redireccionar a la aplicación de cliente enriquecido, pero esta vez tenemos que pasar el nombre de usuario y el token de acceso al cliente para que pueda usarlos. Esto se puede hacer de varias maneras. Por ejemplo, las variables de Javascript se pueden agregar a la página a través de un motor de plantillas del lado del servidor, o bien se puede devolver una cookie con esta información. (gracias a @RyanKimber por señalar los problemas de seguridad al pasar estos datos en la URL, como sugerí inicialmente).
  • Ahora comenzamos la aplicación de una sola página una vez más, pero el cliente tiene el nombre de usuario y el token de acceso.
  • La aplicación cliente puede desencadenar el evento de “inicio de sesión” inmediatamente y permitir que las diferentes partes de la aplicación soliciten la información que necesitan del servicio web.
  • Todas las solicitudes enviadas a https://example.com/api incluirán el token de acceso de Facebook para la autenticación, o el token de acceso de la aplicación generado desde el token de Facebook a través de la función “get_access_token” en la API REST.
  • Las aplicaciones que no son de navegador tienen un poco más de dificultad aquí, porque OAuth requiere un navegador web para iniciar sesión. Para iniciar sesión desde un teléfono o una aplicación de escritorio, deberá iniciar un navegador para redireccionar a Facebook, y lo que es peor, usted necesita una forma para que el navegador pase el token de acceso de Facebook a la aplicación a través de algún mecanismo.

Espero que esto responda la mayoría de las preguntas. Por supuesto, puede reemplazar Facebook con Twitter, Google o cualquier otro servicio de autenticación basado en OAuth.

Me interesaría saber si alguien tiene una forma más simple de lidiar con esto.

Agradezco enormemente la explicación de @Miguel con el flujo completo en cada caso, pero me gustaría agregar algo sobre la parte de Autenticación de Facebook.

Facebook proporciona un SDK de Javascript que puede usar para obtener el token de acceso en el extremo del cliente directamente, que luego se pasa al servidor y se utiliza para extraer toda la información del usuario de Facebook. Entonces, no necesitas ningún re-direccionamiento básicamente.

Además, también puede usar el mismo punto final API para aplicaciones móviles. Simplemente use el Android / iOS SDK para Facebook, obtenga Facebook access_token en el extremo del cliente y páselo al servidor.

Con respecto a la naturaleza sin estado como se explicó, cuando get_access_token se usa para generar un token y se pasa al cliente, este token también se almacena en el servidor. Entonces, ¿es tan bueno como un token de sesión y creo que esto lo hace con estado?

Solo mis 2 centavos ..

Aquí hay un artículo increíble que encontré que puede ayudarte a autenticarte con:

  • Facebook
  • Gorjeo
  • Google
  • Auth local

Autenticación de nodo fácil: configuración y local