Neste tutorial iremos desenvolver um aplicativo usando React Native. Este aplicativo terá como principal funcionalidade a autenticação de usuários através do Login do Google, utilizando o Firebase como nosso backend.

React Native é uma framework que nos permite desenvolver aplicativos nativos para Android e iOS usando JavaScript e React. É uma ótima escolha para desenvolvedores que desejam criar aplicativos móveis de alta qualidade com agilidade e eficiência.

Neste tutorial, vamos focar na criação de um sistema de autenticação de usuário. A autenticação é um aspecto crucial de muitos aplicativos modernos, e o Login do Google é uma das maneiras mais populares de realizar essa tarefa.

Para tornar as coisas ainda melhores, vamos integrar nosso aplicativo React Native com o Firebase. O Firebase é uma plataforma de desenvolvimento de aplicativos da Google que fornece uma variedade de serviços, incluindo um sistema de autenticação robusto. Ao usar o Firebase, podemos facilmente configurar e gerenciar a autenticação do usuário em nosso aplicativo.

O que você vai fazer

O que você vai aprender

O que você precisa

O Firebase vai funcionar como back-end da aplicação. Vamos começar criando um projeto através do console do Firebase.

Insira um nome de projeto e pressione para continuar.

Agora você será perguntado se deseja habilitar o Google Analytics para seu projeto. Não habilite e continue.

Clique em Criar Projeto. Agora o Firebase alocará recursos para o projeto.

Quando terminar, pressione Continuar para prosseguir para o painel do projeto. Agora, vamos configurar a autenticação.

Na barra de navegação lateral clique em Criação/Autentication.

e será apresentada a seguinte tela e você deverá clicar em Vamos começar. Isso habilitará o módulo de autenticação:

Vamos utilizar a autenticação do Google. Clique em Google:

Depois, na tela abaixo ative o provedor de login Google e informe o nome do projeto e e-mail:

Salve para finalizar a configuração da autenticação do Google em seu projeto do Firebase.

Neste ponto vamos configurar o firebase para responder a um aplicativo Android. Clique no ícone de engrenagem na barra lateral e vá para as configurações do projeto.

Role a tela até aparecer o seguinte:

Clique no segundo ícone, que representa uma aplicação Android. Forneça um nome de pacote para o aplicativo e clique em Registrar app.

Após você deverá fazer o download do arquivo de configuração:

Ignore todas as próximas etapas.

Vamos criar uma aplicação React native. Primeiro abra um terminal e use os seguintes comandos:

npx create-expo-app nome-da-aplicação --template blank
cd nome-da-aplicação
code .

Antes de instalar as bibliotecas necessárias ao projeto suspenda o servidor de desenvolvimento com CTRL + C.

npx expo install @react-native-google-signin/google-signin
  1. O arquivo de configuração google-services.json que obtivemos no passo 4 deve ser copiado para a raiz de seu projeto.

  1. Abra o arquivo /app.json e adicione o seguinte código:
{
  "expo": {

    ...

    "plugins": ["@react-native-google-signin/google-signin"],
    "android": {

      ...

      "googleServicesFile": "./google-services.json"
    },
  }
}
  1. Execute o comando para criar o projeto Android
npx expo prebuild
  1. Adicione a linha indicada abaixo no arquivo /android/build.gradle:
buildscript {
    ext {
        buildToolsVersion = "a.b.c"
        minSdkVersion = x
        compileSdkVersion = y
        targetSdkVersion = z
        googlePlayServicesAuthVersion = "20.7.0" // <--- 
    }
// ...

}
  1. Adicione as linhas abaixo no fim do arquivo /android/gradle.properties
# config para otimizar o build
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.caching=true

Para executar o App temos que usar o seguinte comando:

npx expo run:android

Para limpar o cache:

npx expo run:android --no-build-cache

Um fluxo de autenticação em um mobile app é um processo que permite ao usuário acessar recursos protegidos do aplicativo, como dados pessoais, configurações ou funcionalidades restritas.

Para implementar um fluxo de autenticação em um app feito com React Native, é preciso usar bibliotecas específicas que facilitam a integração com serviços de autenticação externos, como Firebase, Auth0 ou AWS Amplify. Essas bibliotecas fornecem componentes e funções que permitem criar telas de login, cadastro, recuperação de senha e verificação de e-mail, além de gerenciar tokens, sessões e permissões de acesso. Alguns exemplos de bibliotecas que podem ser usadas para criar um fluxo de autenticação em um app React Native são: react-native-firebase, react-native-amplify e react-navigation.

Fluxo de autenticação

A renderização condicional é a técnica mais indicada para construir uma lógica que protege as telas do app com acesso restrito. Considere o código abaixo:

{isAuthenticated
    ? (<HomeScreen />)
    : (<LoginScreen />)
}

Quando a variável `isAuthenticated` é verdadeira, o React Navigation exibe apenas a tela HomeScreen. Quando é falsa, exibe a tela LoginScreen. Isso impede a navegação para a tela protegidas quando o usuário não está logado, e para a tela de login quando está logado. Esse padrão é conhecido como Rotas protegidas.

As telas que requerem login estão "protegidas" e não podem ser acessadas se o usuário não estiver logado. Quando o valor de `isAuthenticated` muda, o comportamento do React Navigation também muda.

Quando usamos este padrão podemos implementar o fluxo de autenticação de forma simples, sem lógica adicional para garantir que a tela correta seja exibida.

O trabalho de configuração e instalação de bibliotecas foi feito nos passos 6, 7 e 8 deste codelab. Desta foram podemos nos concentrar agora no código necessário para implem entar a autenticação de usuário utilizando uma conta Google.

Foram adicionadas as funções onLogin e onLogout e também adicionamos um código para configurar o objeto GoogleSignin.

Para obter o valor do atributo webClientId do objeto GoogleSigin, você deve consultar o arquivo google-services.json (conforme mencionado no passo 8). Neste arquivo, você encontrará o valor desejado navegando pela estrutura de chaves da seguinte maneira: "client" -> "oauth_client" -> "client_id". Certifique-se de que o "client_type" associado a este "client_id" seja 3.

import {
  StyleSheet,
  Text,
  View,
  Button,
  ActivityIndicator,
  Image,
} from 'react-native';
import { useState } from 'react';
import { GoogleSignin } from '@react-native-google-signin/google-signin';

//funções de autenticação
export const onLogin = async () => {
  const user = await GoogleSignin.signIn();
  return user;
};

export const onLogout = async () => {
  await GoogleSignin.signOut();
};

// valor obtido no arquivo google-services.json
GoogleSignin.configure({
  webClientId: "438872457138-miq1lm1c3cue420bg81k8erkqd9o3cij.apps.googleusercontent.com",
});

// Telas
const LoginScreen = ({ login, setUser }) => {
  const [isSigninInProgress, setIsSigninInProgress] = useState(false);

  return (
    <View style={styles.layout}>
      {isSigninInProgress && <ActivityIndicator />}
      <Text style={styles.title}>Login</Text>
      <Button
        title="entrar"
        onPress={() => {
          setIsSigninInProgress(true);
          onLogin().then(dadosAuth => {
            console.log(dadosAuth);
            setUser(dadosAuth.data.user);
            login(true);
          });
        }}
      />
    </View>
  );
};

const HomeScreen = ({ login, user }) => (
  <View style={styles.layout}>
    <Text style={styles.title}>Home</Text>
    <Text style={styles.text}>Bem vindo {user.name}</Text>
    <Image
      source={{ uri: user.photo }}
      style={{ width: 100, height: 100, borderRadius: 50, marginBottom: 16 }}
    />
    <Button title="Sair" onPress={() => onLogout().then(() => login(false))} />
  </View>
);

const App = () => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [user, setUser] = useState(null);

  return (
    <View style={styles.container}>
      {isAuthenticated ? (
        <HomeScreen login={setIsAuthenticated} user={user} />
      ) : (
        <LoginScreen login={setIsAuthenticated} setUser={setUser} />
      )}
    </View>
  );
};

export default App;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  layout: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 32,
    marginBottom: 16,
  },
  text: {
    fontSize: 14,
    marginBottom: 16,
  },
});

A função onLogin faz o seguinte:

  1. Chama a função GoogleSignin.signIn(), que inicia o processo de login do Google. Esta função retorna um objeto promise que resolve para um objeto `user` quando o login é bem-sucedido.
  2. A palavra-chave await é usada para pausar a execução da função `onLogin` até que a promessa seja resolvida. Isso significa que a função onLogin retorna o objeto user somente depois que o login do Google é concluído.
  3. 3. O objeto user contém informações sobre o usuário que fez login, como nome de usuário, e-mail, foto de perfil, etc.

Portanto, você pode usar esta função onLogin para iniciar o processo de login do Google e obter informações sobre o usuário que fez login. Por ser uma função assíncrona, você deve usar a palavra-chave `await` ao chamar `onLogin` ou usar a função dentro de uma cadeia de promessas com `.then()` e `.catch()`.

Quando o usuário clica no botão é acionada a função em onPress que faz o seguinte:

  1. setIsSigninInProgress(true): Altera o estado da variável para indicar que o processo de login está em andamento e então a tela mostra um
  2. onLogin().then((user) => {...}): Aqui, a função onLogin() é chamada. Como onLogin() é uma função assíncrona que retorna uma promessa (objeto javascript do tipo Promise), .then() é usado para especificar o que deve acontecer quando a promessa for resolvida. A função dentro de then() recebe o valor resolvido da promessa (neste caso, um objeto de usuário) como seu argumento.
  3. console.log(user): Isso registra as informações do usuário no console. Isso é útil para fins de depuração.
  4. login(true): Altera o estado de login do usuário para "true", indicando que o usuário está logado.

Vamos trocar o nome da variável de estado que estávamos usando para controlar se o usuário estava logado, que antes chamávamos de isAuthenticated, para user.

Quando o usuário realizar o login, devemos agora armazenar o objeto retornado, e assim o novo código do componente LoginScreen fica assim:

const LoginScreen = ({ login }) => {
  const [isSigninInProgress, setIsSigninInProgress] = useState(false);

  return (
    <View style={styles.layout}>
      {isSigninInProgress && <ActivityIndicator />}
      <Text style={styles.title}>Login</Text>
      <Button
        title="entrar"
        onPress={() => {
          setIsSigninInProgress(true);
          onLogin().then((user) => {
            console.log(user);
            login(user);
          });
        }}
      />
    </View>
  );
};

Considerando que este componente será chamada assim:

<LoginScreen login={setUser} />

Vamos analisar o que cada parte deste código faz:

- const LoginScreen = ({ login }) => {...}: Esta é a declaração do componente LoginScreen. Ele recebe login como uma propriedade.

- const [isSigninInProgress, setIsSigninInProgress] = useState(false): Isso é um hook de estado do React. Ele declara uma variável de estado `isSigninInProgress` e uma função para atualizá-la `setIsSigninInProgress`. O estado inicial é `false`, indicando que o processo de login não está em andamento.

- {isSigninInProgress && }: Renderiza um componente ActivityIndicator se isSigninInProgress for `true`. Isso pode ser usado para mostrar um indicador de carregamento enquanto o login está em andamento.

- Button : Este é um botão que, quando pressionado, inicia o processo de login. Ele primeiro define `isSigninInProgress` para `true`, depois chama a função `onLogin()`. Quando `onLogin()` é resolvido, ele registra o usuário no console e chama a função `login` com o usuário como argumento.

Quando a função login é chamada dentro de LoginScreen (por exemplo, após um usuário fazer login com sucesso), ela na verdade está chamando a função setUser com o objeto do usuário como argumento. Isso atualiza o estado da variável user do componente App para refletir que um usuário fez login com sucesso.

Para demonstrar o uso dos dados do usuário logado, o componente <HomeScreen> foi modificado para:

const HomeScreen = ({ user, login }) => (
  <View style={styles.layout}>
    <Text style={styles.title}>Home</Text>
    <Image
      style={{ width: 300, height: 300 }}
      source={{
        uri: user.user.photo,
      }}
    />
    <Button title="Sair" onPress={() => onLogout().then(() => login(false))} />
  </View>
);

E o código completo fica assim:

import { StyleSheet, Text, View, Button, ActivityIndicator, Image } from "react-native";
import { useState } from "react";
import { GoogleSignin } from "@react-native-google-signin/google-signin";

//funções de autenticação
export const onLogin = async () => {
  const user = await GoogleSignin.signIn();
  return user;
};

export const onLogout = async () => {
  return await GoogleSignin.signOut();
};

GoogleSignin.configure({
  webClientId: "676797397237-pjipptjgb1nuaomvcm6rd6dpt19jl06n.apps.googleusercontent.com",
});

// Telas
const LoginScreen = ({ login }) => {
  const [isSigninInProgress, setIsSigninInProgress] = useState(false);

  return (
    <View style={styles.layout}>
      {isSigninInProgress && <ActivityIndicator />}
      <Text style={styles.title}>Login</Text>
      <Button
        title="entrar"
        onPress={() => {
          setIsSigninInProgress(true);
          onLogin().then((user) => {
            console.log(user);
            login(user);
          });
        }}
      />
    </View>
  );
};

const HomeScreen = ({ user, login }) => (
  <View style={styles.layout}>
    <Text style={styles.title}>Home</Text>
    <Image
      style={{ width: 300, height: 300 }}
      source={{
        uri: user.user.photo,
      }}
    />
    <Button title="Sair" onPress={() => onLogout().then(() => login(false))} />
  </View>
);

const App = () => {
  const [user, setUser] = useState(false);
  return <View style={styles.container}>{user ? <HomeScreen user={user} login={setUser} /> : <LoginScreen login={setUser} />}</View>;
};

export default App;

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  layout: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  title: {
    fontSize: 32,
    marginBottom: 16,
  },
});