Desarrollar Webapps Realtime: Auth Con Passport Y Proveedores
Código en GitHub: building-realtime-webapp. Release: auth-passport-providers
.
Entorno de desarrollo en Heroku: building-realtime-webapp.
- Instalar y configurar dependencias
- Modificar Modelo
User
- Modificaciones en
UserController
- Personalización de rutas
Tras ver cómo implementar un sistema de autenticación básica y cómo utilziar Passport para autenticar usuarios vamos a dar un poso más y ver cómo autenticar usuarios usando otros proveedores como Github, Facebook o Twitter.
Instalar y configurar dependencias
Como hemos dicho Passport es un sistema de autenticación modular y nos permite ir ampliando las formas de autenticar a nuestros usuarios. Puedes consultar la lista de proveedores de Passport para ver todos los que están disponibles. Instalamos los paquetes que necesitemos:
npm install --save passport-github passport-facebook passport-twitter
Una vez instalado los paquetes tenemos que configurar las estrategias. Como no quería depender de variables de entorno en local ni exponer las credenciales OAuth en un fichero de configuración, sin mencionar que necesitaremos credenciales diferentes para local y para producción. Por estas razones la solución que os planteo puede ser un poco rebuscada, pero por internet podéis encontrar formas más sencillas si no vais a hacer público vuestro código o decidis depender de variables de entorno.
Fichero de configuración
Lo primero sería pensar en modificar el fichero de configuración. Pero hemos decidido dejar la configuración básica en este fichero y la declaración de estartegias externas en un servicio, debido a la necesidad de usar las variables de configuración de Sails (sails.config
). Dado que la estrategia local funciona de fomra diferente y no requiere variables de configuración la hemos mantenido en el fichero de configuración. Los únicos cambios realizados a /config/passport.js
son por temas de experiencia de usuario y para declarar los proveedores, que nos servirán para abstraer los proveedores que empleamos.
Pensando en el flujo de autenticación y que hacer cuando no se encuenta al usuario, independientemente del proveedor utilizado, la mejor solución es llevar al usuario a la página de registro, como veremos en el UserController
. Si el usuario ha introducido su usuario (username o email) y contraseña, sería un detalle dejarle ese campo del formulario de registro relleno. Esto nos supone los siguientes cambios en nuestra estrategia local cuando no se encuenta el usuario:
1 2 3 4 5 6 7 8 |
|
Servicio con extrategias externas
Hemos creado un fichero en /api/services/PassportService.js
, declarando las dependencias de proveedores de autenticación externos. Además declaramos la función, providersHandler
, la cual se ejecutará una vez se haya autenticado en el servidor del proveedor. Esta función tiene como parámetros el token, el tokenSecret, el perfil de usuario y una función de callback que definimos cuando solicitamos la autenticación en nuestro controlador. En providersHandler
se suele buscar o crear un usuario nuevo si no se encuentra, pero como nosotros tenemos nuestro sistema de usuarios y vamos a implementar varios proveedores, comprobaremos si el perfil está asociado a alguno de nuestros usuarios. Si el id del perfil está en nuestro Modelo devolvemos el usuario, en caso contraro, creamos un usuario un usuario temporal (sin guardarlo en la base de datos). Este usuario lo pasaremos al controlador mediante la función de callback para mantenerlo mientras el usuario termina de registrarse en nuestra plataforma y así poder asociar el perfil recibido sin necesidad de solicitar de nuevo la autenticación al proveedor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Por último, el servicio exporta un módulo con una función que configura los proveedores con las variables de entorno o las de configuración de sails. Lo hacemos dentro de una función para disponer del objeto sails
y por tanto de la configuración declarada en /config/local.js
(hay una copia de referencia en /config/local.ex.js
). Estas propiedades y métodos estarán disponibles en `sails.config
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Esta nueva función, configProviders
debemos llamarla al iniciar la aplicaicón para que se configuren nuestros proveedores de autenticación. Para ello incluimos la llamada en /config/bootstrap.js
:
1 2 3 4 |
|
Modificar Modelo User
En el modelo solo tendremos que añadir un atributo más que guarde los perfiles del usuario. Los resultados se normalizan siguiendo el esquema de contacto establecido por Portable Contacts y puedes consultar en el perfil de usuario en Passport. De momento Sails no permite las asociaciones, disponibles en la versión 0.10, así definiremos un array y procuraremos mantener el schema. Cuando pasemos a Sails.js 0.10 implementaremos asociaciones las cuales nos permitirán crear un modelo Profile en el que definieremos este schema.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Modificaciones en UserController
Vamos a ampliar la funcionalidad de las acciones login
y logout
, las cuales nos permitirán realizar el login de un usuario y unir un perfil con el usuario registrado. Obtenemos el proveedor por parámetro (ver Personalización de rutas) y si es válido procederemos a la unión o eliminación del perfil. La url a la que haremos las peticiones será del tipo /user/login/github
, siendo github
lo que recibiremos como parámetro provider
.
1 2 3 4 5 6 7 8 9 10 11 |
|
Esto unido a tres funciones auxiliares resuelven de manera bastante elegante la autenticación de usuarios, enlace y desenlace de perfiles. La primera función auxiliar, isProvider
, es para comprobar si es un proveedor de authenticación válido.
1 2 3 |
|
La segunda, linkProfile
, es la encargada de gestionar el login con dicho proveedor. Tendrá en cuenta si el usuario está registrado, si hemos encontrado un usuario con dicho perfil en nuestra base de datos y actuará en consecuencia en cada uno de los 5 casos:
- Usuario registrado, intenta enlazar de nuevo un perfil ya enlazado.
- Usuario registrado, intenta enlazar un perfil asociado a otro usuario.
- Usuario registrado, intenta enlazar un perfil no asociado a ningún usuario.
- Usuario no registrado, se autentica con un perfil asociado a su usuario.
- Usuario no registrado, se autentica con un perfil no asociado a ningún usuario.
Personalización de rutas
Un fichero que no habíamos visto hasta hora /config/routes.js
hace aparición para que definamos las url de login
y logout
. El indicar en la url :provider
establecemos provider
como un parámetro y con ?
lo hacemos opcional. De esta forma estas urls nos sirven para el login general y para el login con un proveedor concreto.
1 2 3 4 5 6 7 |
|
## Cambios en las vistas
Ya tenemos toda la parte de lógica de negocio lista. Todo lo que hemos realizado en los controladores, modelos, cofig, servicios… tenemos que refejarlo en las vistas así que vamos a listar los cambios que hay que realizar.
Formulario de autenticación
- Renombramos el fichero
/view/user/auth.ejs
a/view/auth/index.ejs
. - Cambiamos el action del formulario de autenticación a
/auth/login/local
. - Añadimos los botones de autenticación de los diferentes proveedores que vamos a utilizar como formularios con action
/auth/login/[provider]
, siendo[provider]
el proveedor que corresponda en cada caso. Para ello utilizamos utilizamos la variablesails.config.providers
y un bucleeach
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Nuevo usuario
Como hemos comentado guardamos un usuario temporal cuando no hemos encontrado el usuario en nuestra base de datos. Este es el momento de mostrarle al usuario los datos que había rellenado al intentar autenticarse. Hemos omitido poner la contraseña, porque es mejor introducirla de manera consciente. Este sería un ejemplo de como introducimos el valor del usuario temporal.
1 2 3 4 5 6 7 8 9 10 |
|
Mostrar usuario
En la vista del usuario ahora tendremos que informarle de qué perfiles ha unido a su cuenta y darle la posibilidad de unir nuevos o eliminar los que tenga unidos. Así que iteramos los proveedores, sails.config.providers
y si encontramos un perfil de ese proveedor entre los perfiles del usuario ofrecemos la opción de eliminar y en caso contrario la de añadir. Solo vamos a mostrar el condicional para mostrar el uso del método find:
1 2 3 4 5 6 |
|
Con esto hemos terminado nuestro sistema de autenticación con proveedores externos y creado un sistema que facilita el deshabilitar o habilitar nuevos sistemas de autenticación.
Commit en GitHub: c3e544e2ca: Auth with Passport and providers (GitHub, Facebook, Twitter).
Commit en GitHub: 7eb75b35f1: Delete verb from login and logout routes.
Commit en GitHub: 0f78d3898b: Update read me and index to show read me info.
Código en GitHub: building-realtime-webapp. Release: auth-passport-providers
.
Entorno de desarrollo en Heroku: building-realtime-webapp.