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 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
cd nome-da-aplicação
code .
Em seguida, no diretório do projeto, execute o seguinte comando para iniciar um servidor de desenvolvimento a partir do terminal.
npx expo start
Você verá as seguintes informações na tela:
Se o seu ambiente estiver corretamente configurado (este tutorial não inclui a configuração do ambiente de desenvolvimento), pressione a letra a
no teclado do seu computador para abrir o app em um emulador Android.
Em alguns instantes o app será executado e teremos então a seguinte tela:
Antes de instalar as bibliotecas necessárias ao projeto suspenda o servidor de desenvolvimento com CTRL + C.
npx expo install @react-native-firebase/app
npx expo install @react-native-google-signin/google-signin
npm install @react-navigation/native @react-navigation/drawer @react-navigation/native-stack @react-native-firebase/auth
npx expo install react-native-screens react-native-safe-area-context react-native-gesture-handler react-native-reanimated
Execute o seguinte comando no terminal na raiz do seu projeto. Quando for perguntado sobre o nome do pacote, responda com o nome fornecido no passo 4 deste tutorial.
npx expo prebuild --clean
google-services.json
que obtivemos no passo 4 deve ser copiado para dentro de seu projeto no seguinte local: /android/app/./android/app/build.gradle
e adicione o seguinte código:apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // <- Adicione esta linha
google-services
como uma dependência dentro do arquivo /android/build.gradle
:buildscript {
dependencies {
// ... outras dependencias
classpath ('com.google.gms:google-services:4.4.0') // <- Adicione esta linha
}
}
cd android && gradlew signingReport
Isso gera duas chaves. Você precisa copiar as chaves 'SHA1' e 'SHA-256' que pertencem à variante 'debugAndroidTest
' indicando o diretório de sua aplicação.
Em seguida, você pode adicionar essas chaves às "impressões digitais do certificado SHA" em seu aplicativo no Firebase console. Veja sequencia de imagens a seguir:
Role a página até aparecer o seu aplicativo Android
Copie as chaves geradas no campo Impressão digital do certificado
.
app.json
{
"expo": {
"android": {
"googleServicesFile": "./android/app/google-services.json" // <- Adicione esta linha
},
"plugins": [ // <- Adicione estas linhas
"@react-native-firebase/app",
"@react-native-google-signin/google-signin"
] // <- até aqui
}
}
module.exports = function(api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: ["react-native-reanimated/plugin"],
};
};
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.
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.
Desta foram o código completo do app fica assim:
Arquivo App.js
import { StyleSheet, Text, View, Button } from "react-native";
import { useState } from "react";
// Telas
const LoginScreen = ({ login }) => {
return (
<View style={styles.layout}>
<Text style={styles.title}>Login</Text>
<Button title="Entrar" onPress={() => login(true)} />
</View>
);
};
const HomeScreen = ({ login }) => (
<View style={styles.layout}>
<Text style={styles.title}>Home</Text>
<Button title="Sair" onPress={() => login(false)} />
</View>
);
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<View style={styles.container}>
{isAuthenticated
? <HomeScreen login={setIsAuthenticated} />
: <LoginScreen login={setIsAuthenticated} />}
</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
},
layout: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
title: {
fontSize: 32,
marginBottom: 16,
},
});
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 } 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 = () => {
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(true);
});
}}
/>
</View>
);
};
const HomeScreen = ({ login }) => (
<View style={styles.layout}>
<Text style={styles.title}>Home</Text>
<Button title="Sair" onPress={() => onLogout().then(() => login(false))} />
</View>
);
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<View style={styles.container}>{isAuthenticated ? <HomeScreen login={setIsAuthenticated} /> : <LoginScreen login={setIsAuthenticated} />}</View>
);
};
export default App;
const styles = StyleSheet.create({
container: {
flex: 1,
},
layout: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
title: {
fontSize: 32,
marginBottom: 16,
},
});
A função onLogin
faz o seguinte:
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.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.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:
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
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 && <ActivityIndicator />}: 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,
},
});