Continuando de Future Social Authentication Improvements…
Estamos agora no processo de mover todas as informações de ‘conta associada’ para uma única tabela de banco de dados. Isso ajudará a reduzir significativamente a lógica duplicada e permitirá um desenvolvimento mais rápido no futuro. Por exemplo, migrar nossa lógica central do twitter para o novo sistema reduziu o número de linhas de código de 136 para apenas 24
.
Esta postagem não foi projetada para ser um manual de instruções passo a passo para adicionar um novo provedor de autenticação, mas visa fornecer uma visão geral, apontando para o código-fonte relevante quando necessário.
Implementando um autenticador
Cada autenticador deve implementar uma subclasse de Auth::Authenticator. Para usar a nova lógica compartilhada, o autenticador pode estender Auth::ManagedAuthenticator. Um exemplo de implementação básica pode ser encontrado no autenticador principal do Facebook:
name, enabled? e register_middleware devem ser substituídos pelas classes implementadoras.
Observação: para compatibilidade com múltiplos sites, é importante que quaisquer informações específicas do site sejam fornecidas ao omniauth em uma lambda
setup, em vez de serem fixadas no momento da definição. Veja todos os autenticadores principais para exemplos disso.
Toda a lógica para vincular contas externas a contas do Discourse é tratada por Auth::ManagedAuthenticator. Isso depende do provedor omniauth retornar dados no formato definido em sua documentação. Se qualquer manipulação desses dados for necessária, os Autenticadores podem substituir o método after_authenticate e manipular o auth_token conforme necessário. Por exemplo, o autenticador principal do Twitter remove todas as informações extra do token:
Os dados são armazenados na tabela de banco de dados user_associated_accounts. provider_uid, info, credentials e extra são retirados diretamente dos dados retornados pelo omniauth.
Depois que uma classe Authenticator é definida, ela precisa ser registrada. Isso deve ocorrer no início do ciclo de vida do aplicativo e não pode ocorrer dentro do método after_initialize de um plugin. O registro mínimo pode simplesmente conter uma referência ao autenticador. Em um plugin, o registro pode ser feito usando a função auth_provider. Por exemplo:
auth_provider authenticator: OpenIDConnectAuthenticator.new()
No núcleo, o registro ocorre em discourse.rb. Uma lista completa das opções possíveis de AuthProvider pode ser encontrada aqui. O conteúdo de texto pode ser definido usando essas opções, mas é melhor fornecer strings localizáveis em client.en.yml seguindo as chaves padrão. Por exemplo:
Notas adicionais sobre ManagedAuthenticator por @fantasticfears
ManagedAuthenticator em detalhes
Você pode precisar trabalhar em algo especial para autenticação. E você gostaria de saber mais sobre ManagedAuthenticator. Basicamente, ele tem várias operações, opções e controla como os dados serão usados.
Discourse gerencia informações do usuário com dois controladores. Users::OmniauthCallbacksController gerencia a carga útil depois que a autenticação OAuth2 é concluída. after_authenticate é chamado aqui. can_connect_existing_user? também é usado aqui.
Existem alguns métodos privados que você pode ler para entender como diferentes campos de dados funcionam.
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 tem revoke_account que usa can_revoke? e revoke. Mas para o método revoke funcionar remotamente, você precisa construir sua própria implementação.
UserAuthenticator é uma classe de serviço que ajuda a autenticar (verificando confirmação de e-mail ou caminho OAuth2) usuários. after_create_account é chamado aqui.
A lógica principal permanece em after_authenticate com a classe de dados Auth::Result. Seguimos a estrutura de dados aqui. extra_data será passado para after_create_account para criar 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]
}
Ele tentará corresponder e se conectar a uma conta existente.
Você pode se perguntar por que a criação automática de conta é possível, mas não há User.create. Isso é feito em UsersController#create.
authentication = UserAuthenticator.new(user, session)
O usuário é uma nova instância que será preenchida com dados de sessão que são preparados pelo provedor de autenticação. Acredite, é pura mágica.
Migração para o novo sistema
Para fornecer uma transição suave para o novo sistema, os dados devem ser migrados do local de armazenamento antigo. Para provedores de autenticação principais, isso pode ser tabelas dedicadas. Para plugins, isso pode ser plugin_store_rows ou oauth2_user_infos. Os dados mínimos necessários em uma linha user_associated_accounts são provider_name, provider_uid e user_id. Para um exemplo de migração, veja:
Assim que o sistema ManagedAuthenticator for lançado para o branch estável com a v2.2.0, começaremos a migrar os plugins de autenticação oficiais. Neste ponto, um exemplo de migração de plugin_store_row será adicionado aqui.
Este documento é controlado por versão - sugira alterações no github.