Continuando desde Future Social Authentication Improvements…
Ahora estamos en proceso de mover toda la información de ‘cuentas asociadas’ a una única tabla de base de datos. Esto ayudará a reducir significativamente la lógica duplicada y permitirá un desarrollo más rápido en el futuro. Por ejemplo, migrar nuestra lógica central de Twitter al nuevo sistema redujo la cantidad de líneas de código de 136 a solo 24
.
Esta publicación no está diseñada para ser un manual de instrucciones paso a paso para agregar un nuevo proveedor de autenticación, pero tiene como objetivo proporcionar una visión general, señalando el código fuente relevante donde sea necesario.
Implementando un autenticador
Cada autenticador debe implementar una subclase de Auth::Authenticator. Para usar la nueva lógica compartida, el autenticador puede en su lugar extender Auth::ManagedAuthenticator. Un ejemplo de una implementación básica se puede encontrar en el autenticador central de Facebook:
name, enabled? y register_middleware deben ser sobrescritos por las clases que implementen esta interfaz.
Nota: para la compatibilidad multisitio, es importante que cualquier información específica del sitio se proporcione a omniauth en una lambda
setup, en lugar de ser fija en el momento de la definición. Consulte todos los autenticadores centrales para ver ejemplos de esto.
Toda la lógica para vincular cuentas externas con cuentas de Discourse es manejada por Auth::ManagedAuthenticator. Esto depende de que el proveedor omniauth devuelva datos en el formato definido en su documentación. Si se requiere alguna manipulación de estos datos, los Autenticadores pueden sobrescribir el método after_authenticate y manipular el auth_token según sea necesario. Por ejemplo, el autenticador central de Twitter elimina toda la información extra del token:
Los datos se almacenan en la tabla de base de datos user_associated_accounts. provider_uid, info, credentials y extra se toman directamente de los datos devueltos por omniauth.
Una vez que se ha definido una clase Authenticator, necesita ser registrada. Esto debe ocurrir temprano en el ciclo de vida de la aplicación y no puede ocurrir dentro del método after_initialize de un plugin. El registro mínimo puede contener simplemente una referencia al autenticador. En un plugin, el registro se puede realizar usando la función auth_provider. Por ejemplo:
auth_provider authenticator: OpenIDConnectAuthenticator.new()
En el núcleo, el registro tiene lugar en discourse.rb. Una lista completa de las opciones posibles de AuthProvider se puede encontrar aquí. El contenido de texto puede ser definido usando estas opciones, pero es mejor proporcionar cadenas localizables en client.en.yml siguiendo las claves estándar. Por ejemplo:
Notas adicionales sobre ManagedAuthenticator por @fantasticfears
ManagedAuthenticator en detalle
Es posible que necesites trabajar en algo especial para la autenticación. Y te gustaría saber más sobre ManagedAuthenticator. Básicamente, tiene varias operaciones, opciones y controla cómo se utilizarán los datos.
Discourse gestiona la información de usuario con dos controladores. Users::OmniauthCallbacksController gestiona la carga útil una vez que la autenticación OAuth2 está completada. after_authenticate se llama aquí. can_connect_existing_user? también se utiliza aquí.
Hay algunos métodos privados que puedes leer para entender cómo funcionan los diferentes campos de datos.
if authenticator.can_connect_existing_user? && current_user
@auth_result = authenticator.after_authenticate(auth, existing_account: current_user)
else
@auth_result = authenticator.after_authenticate(auth)
end
UsersController tiene revoke_account que usa can_revoke? y revoke. Pero para que el método revoke funcione de forma remota, necesitas construir tu propia implementación.
UserAuthenticator es una clase de servicio que ayuda a autenticar (verificando la confirmación de correo electrónico o la ruta OAuth2) usuarios. after_create_account se llama aquí.
La lógica central permanece en after_authenticate con la clase de datos Auth::Result. Seguimos la estructura de datos aquí. extra_data se pasará a after_create_account para crear registros relacionados.
result.extra_data = {
provider: auth_token[:provider],
uid: auth_token[:uid],
info: auth_token[:info],
extra: auth_token[:extra],
credentials: auth_token[:credentials]
}
Intentará coincidir y conectarse con una cuenta existente.
Quizás te preguntes por qué la creación automática de cuentas es posible pero no hay User.create. Esto se hace en UsersController#create.
authentication = UserAuthenticator.new(user, session)
El usuario es una instancia nueva que se poblará con datos de sesión preparados por el proveedor de autenticación. Confía en mí, es solo magia.
Migración al nuevo sistema
Para proporcionar una transición sin problemas al nuevo sistema, los datos deben migrarse desde la ubicación de almacenamiento antigua. Para los proveedores de autenticación centrales, esto pueden ser tablas dedicadas. Para los plugins, esto puede ser plugin_store_rows, o oauth2_user_infos. Los datos mínimos requeridos en una fila de user_associated_accounts son provider_name, provider_uid y user_id. Para un ejemplo de migración ver:
Una vez que el sistema ManagedAuthenticator haya sido liberado en la rama estable con v2.2.0, comenzaremos a migrar los plugins de autenticación oficiales. En ese punto, se añadirá aquí un ejemplo de migración de plugin_store_row.
Este documento está bajo control de versiones - sugiera cambios en github.