Autenticar com o Firebase usando links por e-mail

Use o Firebase Authentication para fazer o login de um usuário por meio de um link enviado a ele por e-mail. Nesse processo, o endereço de e-mail do usuário também é verificado.

O login por e-mail tem inúmeros benefícios:

  • Inscrição e login simplificados.
  • Menos chances de reutilizar senhas entre os aplicativos, o que pode diminuir a segurança até mesmo das senhas mais fortes.
  • Capacidade de autenticar o usuário e verificar se ele é o legítimo proprietário do endereço de e-mail.
  • O usuário só precisa de uma conta de e-mail acessível para fazer login. Não são necessários números de telefone nem contas em redes sociais.
  • O usuário faz login com segurança sem precisar fornecer ou lembrar de uma senha. Em dispositivos móveis, isso é bem importante porque facilita a vida do usuário.
  • O usuário que já tiver feito login com um identificador de e-mail (senha ou federado) poderá se conectar depois apenas com o e-mail. Por exemplo, se ele se esquecer da senha, ainda será possível fazer login sem precisar redefini-la.

Antes de começar

  1. Siga as etapas no guia Primeiros passos, caso ainda não tenha feito isso.

  2. Ativar o login por link de e-mail no projeto do Firebase.

    Para que os usuários façam login usando um link enviado por e-mail, primeiro ative o provedor de e-mail e o método de login por link no projeto do Firebase:

    1. No Console do Firebase, abra a seção Auth.
    2. Na guia Método de login, ative o provedor de E-mail/senha. Lembre-se de que você precisa ativar esse método para poder usar o login por link.
    3. Na mesma seção, ative o método Link do e-mail (login sem senha).
    4. Clique em Salvar.

Para iniciar o fluxo de autenticação, apresente uma interface que solicite que o usuário forneça seu endereço de e-mail e, em seguida, chame sendSignInLinkToEmail() para solicitar que o Firebase envie o link de autenticação para o e-mail do usuário.

  1. Crie o objeto ActionCodeSettings, que fornece instruções ao Firebase para gerar o link do e-mail. Defina os seguintes campos:

    • url: o link direto para incorporar e qualquer estado adicional a ser transmitido. É preciso que o domínio do link esteja na lista de permissões de domínios autorizados do Console do Firebase, encontrada na guia Método de login (Autenticação -> Método de login). O link redirecionará o usuário para esse URL se o aplicativo não estiver instalado e não for possível instalar ele no dispositivo.

    • androidPackageName e IOSBundleId: os aplicativos que devem ser usados quando o link para login é aberto em um dispositivo Android ou iOS. Saiba mais sobre como configurar o Firebase Dynamic Links para abrir links de ação por e-mail via apps para dispositivos móveis.

    • Defina handleCodeInApp como true. É necessário sempre concluir o processo de login no app, ao contrário de outras ações fora de banda, como redefinição de senha e verificação de e-mail. Isso acontece porque o usuário precisa estar conectado e com o estado Auth mantido no app ao final do fluxo.

    • dynamicLinkDomain: quando vários domínios de link dinâmico personalizados forem definidos para um projeto, especifique qual deles usar quando o link for aberto por um app específico para dispositivos móveis (por exemplo, example.page.link). Caso contrário, o primeiro domínio será selecionado automaticamente.

    var acs = ActionCodeSettings(
        // URL you want to redirect back to. The domain (www.example.com) for this
        // URL must be whitelisted in the Firebase Console.
        url: 'https://www.example.com/finishSignUp?cartId=1234',
        // This must be true
        handleCodeInApp: true,
        iOSBundleId: 'com.example.ios',
        androidPackageName: 'com.example.android',
        // installIfNotAvailable
        androidInstallApp: true,
        // minimumVersion
        androidMinimumVersion: '12');
    
  2. Solicite o e-mail ao usuário.

  3. Envie o link de autenticação a esse endereço e salve o e-mail caso o usuário conclua o login no mesmo dispositivo.

    var emailAuth = 'someemail@domain.com';
    FirebaseAuth.instance.sendSignInLinkToEmail(
            email: emailAuth, actionCodeSettings: acs)
        .catchError((onError) => print('Error sending email verification $onError'))
        .then((value) => print('Successfully sent email verification'));
    });
    

Preocupações com segurança

No Firebase Auth, o usuário precisa fornecer o endereço de e-mail ao concluir o fluxo de login. Isso evita que o link seja usado para fazer o login de um usuário indesejado ou em um dispositivo não intencional. Para realizar o login, esse endereço precisa ser igual ao e-mail que recebeu o link.

É possível simplificar o fluxo dos usuários que abrem o link no mesmo dispositivo usado para solicitá-lo. Basta armazenar o endereço deles localmente quando enviar o e-mail de login. Por exemplo, você pode usar o SharedPreferences. Depois, utilize o endereço para concluir o fluxo. Não transmita o e-mail do usuário nos parâmetros de URL de redirecionamento e o reutilize, já que isso pode ativar injeções de sessão.

Após a conclusão do login, qualquer mecanismo anterior de login não verificado será removido do usuário, e todas as sessões atuais serão invalidadas. Por exemplo, se alguém tiver criado uma conta não verificada com a mesma senha e e-mail, a senha do usuário será removida. Isso impede que a pessoa que reivindicou a propriedade e criou essa conta faça login novamente com o e-mail e senha não confirmados.

Além disso, não deixe de usar um URL HTTPS na produção para impedir que o link possa ser interceptado por servidores intermediários.

O Firebase Authentication usa o Firebase Dynamic Links para enviar o link por e-mail a um dispositivo móvel. No aplicativo para dispositivos móveis, ele precisa estar configurado para detectar o link de entrada, analisar o link direto e então concluir o login.

  1. Configure seu app para receber links dinâmicos no Flutter no guia.

  2. No gerenciador de links, verifique se o link é destinado à autenticação por e-mail e, em caso afirmativo, conclua o processo de login.

    // Confirm the link is a sign-in with email link.
    if (FirebaseAuth.instance.isSignInWithEmailLink(emailLink)) {
      try {
        // The client SDK will parse the code from the link for you.
        final userCredential = await FirebaseAuth.instance
            .signInWithEmailLink(email: emailAuth, emailLink: emailLink);
    
        // You can access the new user via userCredential.user.
        final emailAddress = userCredential.user?.email;
    
        print('Successfully signed in with email link!');
      } catch (error) {
        print('Error signing in with email link.');
      }
    }
    

Também é possível vincular este método de autenticação a um usuário atual. Por exemplo, um usuário já autenticado com outro provedor, como número de telefone, pode incluir este método de login na conta dele.

A diferença está na segunda metade da operação:

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.linkWithCredential(authCredential);
} catch (error) {
    print("Error linking emailLink credential.");
}

Também é possível usá-lo para reautenticar um usuário de link por e-mail antes de executar uma operação confidencial.

final authCredential = EmailAuthProvider
    .credentialWithLink(email: emailAuth, emailLink: emailLink.toString());
try {
    await FirebaseAuth.instance.currentUser
        ?.reauthenticateWithCredential(authCredential);
} catch (error) {
    print("Error reauthenticating credential.");
}

No entanto, este fluxo pode não ser concluído, já que ele pode terminar em um dispositivo diferente do que o usado para fazer login. Nesse caso, é possível exibir um erro ao usuário para obrigá-lo a abrir o link no mesmo dispositivo. Você pode transmitir um estado no link para fornecer informações sobre o tipo de operação e o UID do usuário.

Caso você tenha suporte para login com senha e login com base em link por e-mail, para diferenciar o método de login de um usuário de senha/link, use fetchSignInMethodsForEmail. Isso é útil nos fluxos que priorizam identificadores, em que o usuário precisa indicar o e-mail e, em seguida, escolher o método de login:

try {
    final signInMethods =
        await FirebaseAuth.instance.fetchSignInMethodsForEmail(emailAuth);
    final userExists = signInMethods.isNotEmpty;
    final canSignInWithLink = signInMethods
        .contains(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD);
    final canSignInWithPassword = signInMethods
        .contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD);
} on FirebaseAuthException catch (exception) {
    switch (exception.code) {
        case "invalid-email":
            print("Not a valid email address.");
            break;
        default:
            print("Unknown error.");
    }
}

Conforme descrito acima, e-mail/senha e e-mail/link são considerados os mesmos EmailAuthProvider (mesmo PROVIDER_ID) com diferentes métodos de login.

Próximas etapas

Depois que um usuário cria uma nova conta, ela é armazenada como parte do seu projeto do Firebase e pode ser usada para identificar um usuário em todos os apps do projeto, seja qual for o método de login usado.

É possível receber as informações básicas de perfil do usuário do objeto User nos seus aplicativos. Consulte Gerenciar usuários.

Nas Regras de segurança do Firebase Realtime Database e do Cloud Storage, é possível receber o ID do usuário único conectado da variável auth e usar esse ID para controlar quais dados um usuário pode acessar.

Os usuários podem fazer login no app usando vários provedores de autenticação. Basta vincular as credenciais desses provedores a uma conta de usuário.

Para desconectar um usuário, chame signOut():

await FirebaseAuth.instance.signOut();