architecture - Arquitectura para fusionar mĂșltiples cuentas de usuario juntas
oauth2 (6)
Ambos enfoques para las cuentas de fusión automática dejan una vulnerabilidad bastante grande que permitiría a alguien hacerse cargo de una cuenta. Ambos parecen suponer que el usuario es quien dicen ser cuando ofrecen la opción de combinación a un usuario que se registra.
Mi recomendación para mitigar la vulnerabilidad es solicitar al usuario que se autentique con uno de los proveedores de identidad conocidos antes de realizar la fusión para verificar la identidad del usuario.
Ejemplo: el usuario A se registra con la identidad de Facebook. Algún tiempo después vuelven a su sitio e intentan acceder con Windows Live ID e inician el proceso de registro. Su sitio le indicará al usuario A que ... Parece que se ha registrado en Facebook anteriormente. Inicie sesión en Facebook (proporcione el enlace) y podemos fusionar su Windows Live ID con su perfil existente.
Otra alternativa es almacenar un secreto compartido (contraseña / pregunta personal) en el registro inicial que el usuario debe proporcionar al fusionar identidades, sin embargo, esto lo lleva nuevamente al negocio de almacenar secretos compartidos. También significa que debe manejar el escenario donde el usuario no recuerda el secreto compartido y el flujo de trabajo que lo acompaña.
De acuerdo, tengo un sitio web donde puedes registrarte e iniciar sesión. También puede iniciar sesión con su cuenta de Facebook, Twitter o LinkedIn.
Es importante que los usuarios solo tengan una cuenta registrada. De alguna manera, quiero fusionar las cuentas de los usuarios si utilizan diferentes métodos para iniciar sesión. ¿Cuál es la mejor solución para resolver esto?
Por ejemplo, el usuario inicia sesión con su cuenta de Facebook. Uso los datos para registrar una cuenta automáticamente. ¿Debo enviar un correo electrónico con un nombre de usuario y contraseña de nuestro sitio web? (Si esto está bien con la política de Facebook). ¿Debo darles una segunda pantalla donde pueden completar un nombre de usuario y una contraseña? Pero esa no es la idea detrás de iniciar sesión con su cuenta de Facebook. Debería simplificar su procedimiento para participar.
También es posible que el usuario se haya registrado en nuestro sitio web y la próxima vez que inicie sesión con su cuenta de Twitter. ¿Cómo puedo fusionar estas 2 cuentas como una sola? ¿Cuál es la mejor manera?
Así que, básicamente, mi pregunta es: obtuve 4 formas diferentes en que un usuario se convierte en miembro de nuestro sitio web. ¿Cómo puedo asegurarme de que estas 4 formas solo creen una cuenta si un usuario decide usarlas de múltiples maneras? ¿Cuál es el mejor flujo para asegurarse de que no se convierta en una molestia para el usuario?
Editar:
Tres años después de haber formulado esta pregunta, estoy respondiendo yo mismo en una serie de artículos: http://www.sitepoint.com/series/using-social-networks-as-a-login-system/
Debe permitir el inicio de sesión desde una cuenta, luego, cuando inicie sesión, brinde la opción de agregar otra cuenta diferente para fusionarse con ella.
He pasado por esto con sled.com. Aquí hay varios problemas con respecto a la creación de cuentas y el soporte de múltiples cuentas de terceros para iniciar sesión. Algunos de ellos son:
- ¿Necesita admitir una contraseña local e inicios de sesión de terceros?
Para sled.com, he decidido soltar la contraseña local debido al pequeño valor que agrega y el costo adicional para asegurar un formulario de ingreso de contraseña. Hay muchos ataques conocidos por romper contraseñas y si va a introducir contraseñas debe asegurarse de que no sean fáciles de romper. También necesita almacenarlos en un hash de un sentido o algo similar para evitar que se filtren.
- ¿Cuánta flexibilidad quiere permitir para soportar múltiples cuentas de terceros?
Parece que ya eligió los tres proveedores de inicio de sesión: Facebook, Twitter y LinkedIn. Eso es genial porque significa que está usando OAuth y trabajando con un conjunto bien definido de proveedores de confianza. No soy fanático de OpenID. La pregunta restante es si necesita admitir varias cuentas de terceros del mismo proveedor (por ejemplo, una cuenta local con dos cuentas de Twitter enlazadas). Asumo que no, pero si lo hace, tendrá que acomodar eso en su modelo de datos.
Para Sled, admitimos el inicio de sesión con Facebook, Twitter y Yahoo! y dentro de cada cuenta de usuario, almacene una clave para cada uno: {"_id": "djdjd99dj", "yahoo": "dj39djdj", twitter: "3723828732", "facebook": "12837287"}. Configuramos un conjunto de limitaciones para garantizar que cada cuenta de terceros solo se pueda vincular a una sola cuenta local.
Si va a permitir varias cuentas del mismo proveedor de terceros, necesitará usar listas u otras estructuras para respaldar eso y, con eso, todas las otras restricciones para garantizar la exclusividad.
- ¿Cómo vincular múltiples cuentas?
La primera vez que el usuario se registra en su servicio, primero va al proveedor de terceros y vuelve con una identificación de terceros verificada. A continuación, cree una cuenta local para ellos y recopile cualquier otra información que desee. Recopilamos su dirección de correo electrónico y también les solicitamos que elijan un nombre de usuario local (tratamos de llenar previamente el formulario con su nombre de usuario existente del otro proveedor). Tener alguna forma de identificador local (correo electrónico, nombre de usuario) es muy importante para la recuperación de la cuenta más adelante.
El servidor sabe que se trata de un inicio de sesión por primera vez si el navegador no tiene una cookie de sesión (válida o caducada) para una cuenta existente y no se encuentra la cuenta de terceros utilizada. Intentamos informar al usuario que no solo está iniciando sesión, sino que está creando una cuenta nueva, de modo que si ya tiene una cuenta, con suerte se detendrá e iniciará sesión con su cuenta existente.
Usamos exactamente el mismo flujo para vincular cuentas adicionales, pero cuando el usuario regresa del tercero, la presencia de una cookie de sesión válida se usa para diferenciar entre un intento de vincular una cuenta nueva con una acción de inicio de sesión. Solo permitimos una cuenta de terceros de cada tipo y, si ya hay uno vinculado, bloqueamos la acción. No debería ser un problema porque la interfaz para vincular una nueva cuenta está deshabilitada si ya tiene una (por proveedor), pero por las dudas.
- Cómo combinar cuentas?
Si un usuario intentó vincular una nueva cuenta de terceros que ya está vinculada a una cuenta local, simplemente solicíteles que confirmen que desean fusionar las dos cuentas (suponiendo que pueda manejar dicha fusión con su conjunto de datos, a menudo más fácil dicho que hecho). También puede proporcionarles un botón especial para solicitar una fusión, pero en la práctica, lo único que están haciendo es vincular otra cuenta.
Esta es una máquina de estado bastante simple. El usuario regresa del tercero con una identificación de cuenta de terceros. Su base de datos puede estar en uno de tres estados:
- La cuenta está vinculada a una cuenta local y no hay ninguna cookie de sesión presente -> Iniciar sesión
- La cuenta está vinculada a una cuenta local y una cookie de sesión está presente -> Fusionar
- La cuenta no está vinculada a una cuenta local y no hay ninguna cookie de sesión presente -> Registrarse
La cuenta no está vinculada a una cuenta local y hay una cookie de sesión presente -> Enlace de cuenta adicional
- ¿Cómo realizar la recuperación de la cuenta con proveedores externos?
Este es un territorio experimental. No he visto un UX perfecto para esto, ya que la mayoría de los servicios brindan una contraseña local junto a las cuentas de terceros y, por lo tanto, se centran en el caso de uso "Olvidé mi contraseña", no en todo lo demás que puede salir mal.
Con Sled, hemos optado por utilizar "¿Necesita ayuda para iniciar sesión?" y cuando haces clic, le pides al usuario su correo electrónico o nombre de usuario. Lo buscamos y si encontramos una cuenta que coincida, envíe un correo electrónico a ese usuario un enlace que puede automáticamente iniciar sesión en el servicio (bueno por una vez). Una vez dentro, los llevamos directamente a la página de vinculación de la cuenta, les decimos que deben echar un vistazo y potencialmente vincular cuentas adicionales, y mostrarles las cuentas de terceros que ya han vinculado.
La mayoría de las publicaciones son bastante antiguas y supongo que el servicio gratuito de Firebase Authentication de Google aún no existía. Después de verificar con OAuth, le pasas el token de OAuth y obtienes una identificación de usuario única que puedes almacenar como referencia. Los proveedores compatibles son Google, Facebook, Twitter, GitHub y existe la opción de registrar proveedores personalizados y anónimos.
Me enfrento con la misma tarea exacta en este momento. El diseño que desarrollé es bastante simple, pero funciona bien.
La idea central es que los modelos para una identidad de sitio local y las identidades de sitio de terceros se mantienen aisladas, pero luego se vinculan. Por lo tanto, cada usuario que inicia sesión en el sitio tiene una identidad local que se asigna a cualquier número de identidades de sitios de terceros.
Un registro de identidad local contiene un mínimo de información, incluso podría ser un solo campo, solo una clave principal. (Para mi solicitud, no me importa el correo electrónico, el nombre o la fecha de nacimiento del usuario; solo quiero saber que es la persona que ha iniciado sesión en esta cuenta desde el principio).
Las identidades de terceros contienen información relevante solo para autenticarse con un tercero. Para OAuth, esto normalmente significa un identificador de usuario (como una identificación, correo electrónico o nombre de usuario) y un identificador de servicio (que indica con qué sitio o servicio se autenticó). En otras partes de la aplicación, fuera de la base de datos, ese identificador de servicio se empareja con un método para recuperar el identificador de usuario relevante de ese servicio, y así es como se realiza la autenticación. Para OpenID, empleamos el mismo enfoque, excepto que el método para la autenticación es más general (porque casi siempre podemos realizar exactamente el mismo protocolo, excepto que usamos una URL de identidad diferente, y ese es nuestro identificador de servicio).
Finalmente, mantengo un registro de las identidades de terceros emparejadas con la identidad local. Para generar estos registros, el flujo se ve así:
- Un usuario inicia sesión por primera vez con una identidad de terceros. Se crea un registro de identidad local, luego un registro de identidad de un tercero y luego se emparejan.
- En un panel de control, al usuario se le ofrece la oportunidad de vincular una cuenta iniciando sesión en servicios de terceros. (Bastante sencillo cómo funciona esto)
- En el escenario donde el usuario inconscientemente hace varias cuentas, la solución es bastante simple. Mientras el usuario inicia sesión en una de las cuentas, inicia sesión en otra que previamente utilizó para iniciar sesión en el sitio (a través de la función del panel de control anterior). El servicio web detecta esta colisión (que la identidad local del usuario conectado difiere de la identidad local que está vinculada a la identidad de terceros que acaba de iniciar sesión) y al usuario se le solicita una combinación de cuentas.
La fusión de cuentas es una cuestión de fusionar cada campo individual de la identidad local (que variará de una aplicación a otra, y debería ser sencillo si solo tiene un par de campos en sus registros de identidad locales), y luego garantizar las identidades de terceros vinculados. están vinculados a la identidad local resultante.
Tiendo a encontrar muchos sitios que se fusionan según el correo electrónico como factor de unión superpuesto.
Puedo ver que esta es una opción viable, pero nuevamente depende de tu preferencia sobre cómo fusionar. La dirección de correo electrónico es la forma principal que usa la gente para verificar cambios importantes de información en su sitio, cambiar su contraseña, finalizar el servicio, el saldo de la cuenta es bajo, etc. Es casi como el sistema de número de seguridad social pero con la capacidad de comunicación . Culturalmente: creo que es razonable suponer que un correo electrónico es una identidad bastante única en todos los servicios de autenticación OAuth. Por supuesto, es lo que solicitan los formularios de inicio de sesión para Facebook y Google.
Mi proceso de pensamiento actual.
La página de inicio de sesión tiene 3 opciones
- La membresía de su propio sitio
- Iniciar sesión con Facebook
- Inicia sesión con google
1) Inicio de sesión de usuario por primera vez: desencadena un flujo de registro donde se crea y completa una cuenta por primera vez.
if the user logins using Facebook (or whatever 3rd party login)
1) call the Facebook api asking for their information (email, name, etc...)
2) create an account membership entry in your database somewhat like this
Table = Users
[ UserId | Email | Password ]
[ 23 | "[email protected]" | *null* ]
3) create an external auths entry like so
*ProviderUserId is the unique id of that user on the provider''s site
Table = ExternalAuths
[ ExternalAuthId | User_UserId | ProviderName | ProviderUserId ]
[ 56 | 23 | Facebook | "max.alexander.9"]
if the user wants to create an account with your own registration it would just be this
Table = Users
[ UserId | Email | Password ]
[ 23 | [email protected] | myCoolPwd ]
2) En otro momento, el usuario regresa pero decide hacer clic en el inicio de sesión de Google
1) call the Google api asking for their information (email, name, etc...)
2) once you get the email, match it up to the userId entry with the existing email
3) create an additional External auth entry as such
Table = ExternalAuths
[ ExternalAuthId | User_UserId | ProviderName | ProviderUserId ]
[ 56 | 23 | Facebook | "max.alexander.9"]
[ 57 | 23 | Google | "1234854368" ]
3) Ahora que se ha fusionado en la cuenta en la que confía, el correo electrónico en sus entradas de la base de datos es el mismo que confía desde los inicios de sesión externos.
Entonces para inicios de sesión subsecuentes
Entonces, ¿qué pasa si primero tiene inicios de sesión externos y luego desea que un usuario pueda iniciar sesión con una contraseña más adelante?
Veo dos maneras fáciles de hacer esto
En cualquier primer inicio de sesión cuando se crea una cuenta desde una autenticación externa, pídales una contraseña para completar su primera entrada en su aplicación
Si ya se han registrado usando Facebook o Google primero, de alguna manera querían registrarse utilizando el formulario de registro de su propio sitio. Detecta si la dirección de correo electrónico que ingresaron ya existe, pídeles una contraseña y envíales una confirmación por correo electrónico una vez que se haya completado el registro.