close icon
React

O Guia Completo de React User Authentication com Auth0

Aprenda como adicionar autenticação de usuário ao React usando Context and Hooks

April 06, 2022

Procure o emoji 🛠️ se quiser passar pelo conteúdo enquanto se concentra nas etapas de implementação.

O foco deste tutorial é ajudar pessoas desenvolvedoras a aprender como proteger uma aplicação React implementando a autenticação de usuário. Você vai melhorar uma aplicação React inicial para praticar os seguintes conceitos de segurança:

  • Adicionar login e logout de usuário.
  • Recuperar informações de usuário.
  • Proteger a rota da aplicação.
  • Chamar endpoints protegidos de uma API.

Este guia usa o SDK da Auth0 para proteger as aplicações React, que fornece às pessoas desenvolvedoras de React uma maneira mais fácil de adicionar autenticação de usuário para aplicações React, usando uma abordagem centrada em hooks. O SDK da Auth0 para React fornece uma API de alto nível para lidar com muitos detalhes de implementação de autenticação. Agora você pode proteger suas aplicações React usando as melhores práticas de segurança enquanto escreve menos código.

⚠️ Este guia usa React Hooks e componentes de função para construir uma aplicação React segura. Se precisar implementar qualquer componente deste guia usando classes JavaScript, verifique o repositório auth0-react-sample-classes enquanto lê. Existe um arquivo baseado em class equivalente para cada arquivo criado neste guia.

Exemplo de uma aplicação com Auth0 React

Como funciona a Auth0?

Com a ajuda da Auth0, você não precisa ser um especialista em protocolos de identidade, como OAuth 2.0 ou OpenID Connect, para entender como proteger suas aplicações. Primeiro, você integra sua aplicação com a Auth0. Sua aplicação redirecionará os usuários para uma página de login personalizável da Auth0. Após seus usuários fazerem login com sucesso, Auth0 os redireciona de volta para a sua aplicação, retornando JSON Web Tokens (JWTs) com autenticação e informações de usuário.

⏰⚡️ Se você tiver pouco tempo, consulte o Guia de Início Rápido React da Auth0 para começar a usar a autenticação de usuário em React em apenas alguns minutos. Você também pode conferir a nossa playlist do YouTube “React e Auth0”.

Obtenha a Aplicação Inicial

Criamos um projeto inicial usando um create-react-app para aprender os conceitos de segurança do React através da prática. A aplicação inicial usa Bootstrap com um tema personalizado para cuidar do estilo e do layout das suas aplicações. Você pode se concentrar na construção de componentes React para proteger sua aplicação.

🛠 Agora, clone o repositório auth0-react-sample em seu branch starter para começar:

git clone -b starter git@github.com:auth0-blog/auth0-react-sample.git

🛠 Depois de clonar o repositório, torne auth0-react-sample seu diretório atual:

cd auth0-react-sample

🛠 Instale as dependências do projeto React:

npm install

Conecte O React Com a Auth0

A melhor parte da plataforma Auth0 é a simplicidade para começar seguindo estas etapas:

Inscreva-se e crie uma aplicação Auth0

🛠 Se ainda não o fez, cadastre-se para obter uma conta Auth0 gratuita.

Uma conta gratuita oferece:

Durante o cadastro, você cria um Auth0 Tenant, que representa o produto ou serviço ao qual você está adicionando autenticação.

🛠 Depois de se inscrever, Auth0 leva você para o Dashboard. No menu da barra lateral esquerda, clique em "Applications".

🛠 Em seguida, clique no botão "Create Application". Irá abrir um formulário para que você escreva o nome e o tipo da aplicação.

  • Name (nome):
Auth0 React Sample
  • Application type (Tipo da aplicação): Single Page Web Applications (Aplicações Web de Página Única)

🛠 Clique no botão "Create" para concluir o processo. A página da sua aplicação Auth0 é carregada.

Na próxima etapa, você aprenderá como fazer o React e a Auth0 se comunicarem.

Qual é a relação entre Auth0 Tenants e aplicações Auth0?

Digamos que você tenha uma aplicação React de compartilhamento de fotos chamado “Rectogram”. Você então criaria um Auth0 tenant chamado reactogram. Do ponto de vista do cliente, o Reactogram é o produto ou serviço desse cliente.

Agora, digamos que o Reactogram esteja disponível em três plataformas: na web como uma aplicação de página única (Single Page Application ou SPA em inglês) e como um aplicativo móvel nativo para Android e iOS. Se cada plataforma precisar de autenticação, você precisará criar três aplicações Auth0 para fornecer ao produto tudo o que a pessoa precisa para autenticar os usuários por meio dessa plataforma.

Os usuários do Reactogram pertenceriam ao Auth0 tentant Reactogram, que os compartilha em suas aplicações Auth0. Se você lançasse outro produto chamado "Reactiktok" que precisa de autenticação, precisaria criar outro tenant, reactiktok, e criar aplicações Auth0 para dar suporte às plataformas em que ele é suportado.

Crie uma ponte de comunicação entre React e Auth0

Ao usar a Auth0, você não precisa criar formulários de login. A Auth0 oferece uma página de login universal para reduzir a sobrecarga de adição e gerenciamento de autenticação.

Como funciona o Login Universal?

Sua aplicação React redirecionará os usuários para a Auth0 sempre que eles dispararem uma requisição de autenticação. A Auth0 irá apresentar uma página de login. Assim que eles fizerem login, a Auth0 os redirecionará de volta para a sua aplicação React. Para que o redirecionamento ocorra com segurança, você deve especificar nas suas configurações de aplicações Auth0 as URLs para os quais a Auth0 pode redirecionar usuários, uma vez que estejam autenticados.

🛠 Clique na guia "Settings" da página da aplicação Auth0 e preencha os seguintes valores:

🛠 URLs de callback permitidas (Allowed Callback URLs em inglês)

http://localhost:4040

O valor acima é a URL que a Auth0 pode usar para redirecionar seus usuários depois que eles fizerem login.

🛠 URLs de logout permitidas (Allowed Logout URLs em inglês)

http://localhost:4040

O valor acima é a URL que a Auth0 pode usar para redirecionar seus usuários após o logout.

🛠 Origens Web permitidas (Allowed Web Origins em inglês)

http://localhost:4040

Usando o SDK da Auth0 para React, sua aplicação React fará requisições internas a uma URL da Auth0 para lidar com as requisições de autenticação. Você precisa adicionar a URL de origem da aplicação React para evitar problemas de Compartilhamento de recursos de origem cruzada (Cross-Origin Resource Sharing - CORS).

🛠 Role para baixo e clique no botão "Save changes".

🛠 Não feche esta página ainda. Você precisará de algumas informações na próxima seção.

Adicione as variáveis de configuração da Auth0 ao React

Na aba "Settings" da página da aplicação Auth0, você precisa dos valores de Domínio Auth0 (Auth0 Domain em inglês) e ID do Cliente (Client ID em inglês) para permitir que sua aplicação React use a ponte de comunicação que você criou.

O que exatamente é um domínio Auth0 e um ID de cliente Auth0?

Domínio

Quando você criou uma nova conta Auth0, a Auth0 pediu para escolher um nome para seu tenant. Este nome, anexado com auth0.com, é o seu domínio Auth0 (Auth0 Domain em inglês). É a URL base que você usará para acessar as APIs Auth0 e a URL para onde você redirecionará os usuários para fazer login.

Você também pode usar domínios personalizados para permitir que a Auth0 faça o trabalho pesado de autenticação para você sem comprometer sua experiência de branding.

ID de cliente

Cada aplicação recebe um ID de cliente (Client ID) ao ser criada, que é uma string alfanumérica, e é o identificador exclusivo da sua aplicação (como q8fij2iug0CmgPLfTfG1tZGdTQyGaTUA). Você não pode modificar o ID do cliente. Você usará o ID do cliente para identificar a aplicação Auth0 que o SDK da Auth0 para React precisa se conectar.

Atenção: Outra informação crítica presente nas "Settings" é o Segredo do Cliente (Client Secret em inglês). Esse segredo protege seus recursos concedendo tokens apenas para solicitantes se estiverem autorizados. Pense nisso como a senha da sua aplicação, que deve ser mantida em sigilo o tempo todo. Se alguém obtiver acesso ao seu Client Secret, poderá se passar pela sua aplicação e acessar recursos protegidos.

🛠 Abra o projeto inicial do React, auth0-react-sample e crie um arquivo .env o diretório do projeto:

touch .env

🛠 Preencha o .env da seguinte forma:

REACT_APP_AUTH0_DOMAIN=
REACT_APP_AUTH0_CLIENT_ID=

🛠 Volte para a página da aplicação Auth0. Siga estas etapas para obter os valores REACT_APP_AUTH0_DOMAIN eREACT_APP_AUTH0_CLIENT_ID:

Configurações da aplicação Auth0 para habilitar autenticação de usuário

  1. 🛠 Clique na guia "Settings", se ainda não tiver feito isso.
  2. 🛠 Use o valor "Domain" em "Settings" como o valor de REACT_APP_AUTH0_DOMAIN no .env.
  3. 🛠 Use o valor "Client ID" em "Settings" como o valor de REACT_APP_AUTH0_CLIENT_ID no .env.

Essas variáveis permitem que sua aplicação React se identifique como uma parte autorizada para interagir com o servidor de autenticação Auth0.

Conexão Auth0 e React

Você concluiu a configuração de um serviço de autenticação que sua aplicação React pode consumir. Continue construindo o projeto inicial ao longo deste guia, implementando componentes para acionar e gerenciar o fluxo de autenticação.

Descubra mais na documentação da Auth0 para saber mais sobre como a Auth0 ajuda a economizar tempo na implementação e gerenciamento de identidade.

Configure O SDK Da Auth0 Para React

🛠 Você precisa seguir estas etapas para integrar o SDK da Auth0 com sua aplicação React.

Instale o SDK da Auth0 para React

🛠 Execute o seguinte comando:

npm install @auth0/auth0-react

Configure o componente Auth0Provider

Por baixo dos panos, o SDK da Auth0 para React usa o React Context. O SDK usa um componente Auth0Context para gerenciar o estado de autenticação de seus usuários. Por sua vez, o SDK expõe o componente Auth0Provider, que fornece esse Auth0Context para seus componentes filhos. Como tal, você pode envolver seu componente raiz, como o App, com Auth0Provider para integrar a Auth0 com sua aplicação React.

<Auth0Provider>
  <App />
</Auth0Provider>

No entanto, a autenticação de usuário é um mecanismo para monitorar quem está acessando sua aplicação e controlar o que essas pessoas podem fazer. Por exemplo, você pode impedir que usuários que não tenham efetuado login acessem partes da sua aplicação. Nesse cenário, Auth0 pode atuar como bouncer da aplicação.

Um bouncer é uma pessoa empregada por uma boate ou estabelecimento similar para impedir que encrenqueiros entrem ou os expulsem do local. A segurança do React não é muito diferente da segurança de uma boate.

Se os usuários quiserem acessar uma rota protegida da sua aplicação, Auth0 irá interrompê-los e solicitar que apresentem suas credenciais. Se a Auth0 puder verificar quem são e se devem entrar lá, a Auth0 os deixará entrar. Caso contrário, a Auth0 os levará de volta para uma rota pública da aplicação.

Agora, é importante reiterar que o processo de autenticação não acontecerá na sua aplicação. Sua aplicação React redirecionará seus usuários para a página de Login Universal da Auth0, onde a Auth0 solicita as credenciais e redireciona o usuário de volta à sua aplicação com o resultado do processo de autenticação.

O Auth0Provider lembra para onde o usuário queria ir e, se a autenticação for bem-sucedida, leva o usuário para essa rota. Por isso, o Auth0Provider precisa ter acesso ao histórico de sessão da aplicação. A aplicação inicial React usa React Router para gerenciar seu roteamento. O React Router expõe um React Hook que facilita o acesso ao histórico da sessão por meio de um objeto history, useHistory().

Consequentemente, você precisa envolver o Auth0Provider com BrowserRouter do React Router, que usa um componente RouterContext.Provider por baixo dos panos para manter o estado de roteamento:

<BrowserRouter>
  <Auth0Provider>
    <App />
  </Auth0Provider>
</BrowserRouter>

Aqui, o que você vê em jogo é um pilar da arquitetura do React: você estende os componentes, não por herança, mas por composição.

Como você cria um Auth0Provider com acesso ao histórico de sessão da aplicação?

🛠 Comece criando um diretório auth no diretório src:

mkdir src/auth

🛠 Crie um arquivo auth0-provider-with-history.js no diretório src/auth para definir um componente Auth0ProviderWithHistory que usa composição para disponibilizar React Router Hooks para Auth0Provider:

touch src/auth/auth0-provider-with-history.js

🛠 Preencha o src/auth/auth0-provider-with-history.js com o seguinte:

// src/auth/auth0-provider-with-history.js

import React from "react";
import { useHistory } from "react-router-dom";
import { Auth0Provider } from "@auth0/auth0-react";

const Auth0ProviderWithHistory = ({ children }) => {
  const domain = process.env.REACT_APP_AUTH0_DOMAIN;
  const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;

  const history = useHistory();

  const onRedirectCallback = (appState) => {
    history.push(appState?.returnTo || window.location.pathname);
  };

  return (
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      redirectUri={window.location.origin}
      onRedirectCallback={onRedirectCallback}
    >
      {children}
    </Auth0Provider>
  );
};

export default Auth0ProviderWithHistory;

O que está acontecendo no Auth0ProviderWithHistory?

  • Você precisa do SDK da Auth0 para React para se conectar à aplicação Auth0 correta para processar a autenticação. Assim, você precisa do Auth0 Domain e Client ID para configurar o Auth0Provider.
  • Use o método onRedirectCallback() para manipular o evento em que Auth0 redireciona seus usuários da página de login universal da Auth0 para a aplicação React. Use o hook useHistory() para obter o objeto history do React Router. Use o método history.push() para levar os usuários de volta à rota que pretendiam acessar antes da autenticação.

É isso! Envolver qualquer árvore de componentes com Auth0ProviderWithHistory dará acesso ao Auth0Context.

Como você usa o Auth0ProviderWithHistory

O Auth0ProviderWithHistory requer que o componente BrowserRouter do React Router seja seu pai, avô ou tataravô.

O Context do React Router deve estar presente na árvore de componentes em um nível superior para que Auth0ProviderWithHistory acesse o hook useHistory() do React Router.

🛠 Abra o arquivo src/index.js e atualize-o da seguinte forma para construir a árvore de componentes adequada para alimentar os recursos de roteamento e autenticação de usuário da sua aplicação React:

// src/index.js

import React from "react";
import ReactDOM from "react-dom";
import App from "./app";
import { BrowserRouter as Router } from "react-router-dom";
import Auth0ProviderWithHistory from "./auth/auth0-provider-with-history";

import "./index.css";

ReactDOM.render(
  <Router>
    <Auth0ProviderWithHistory>
      <App />
    </Auth0ProviderWithHistory>
  </Router>,
  document.getElementById("root")
);

🛠 Execute o seguinte comando para executar sua aplicação React:

npm start

O SDK da Auth0 para React está configurado. Agora você tem tudo que você precisa para implementar a autenticação do usuário na próxima seção.

Adicione Autenticação De Usuários

Você precisa fornecer elementos de interface de usuário (IU) para que seus usuários acionem eventos de autenticação: login, logout e inscrição.

Crie um botão de login

🛠 Crie um arquivo login-button.js no diretório src/components/:

touch src/components/login-button.js

🛠 Preencha src/components/login-button.js assim:

// src/components/login-button.js

import React from "react";
import { useAuth0 } from "@auth0/auth0-react";

const LoginButton = () => {
  const { loginWithRedirect } = useAuth0();
  return (
    <button
      className="btn btn-primary btn-block"
      onClick={() => loginWithRedirect()}
    >
      Log In
    </button>
  );
};

export default LoginButton;

loginWithRedirect() é um método exposto pelo Auth0Context. Chamar esse método solicita que um usuário autentique e dê consentimento para que sua aplicação React acesse determinados dados em nome deste usuário. Em sua arquitetura atual, isso significa que sua aplicação React redireciona o usuário para a página de login universal da Auth0 para realizar o processo de autenticação. Você verá isso em ação nas próximas seções.

Você pode passar um objeto de configuração para loginWithRedirect() para personalizar a experiência de login. Por exemplo, você pode passar opções para redirecionar usuários a uma página de login universal Auth0 otimizada para se inscrever na sua aplicação React. Consulte RedirectLoginOptions (em inglês) para obter mais detalhes sobre essas opções.

Crie um botão de inscrição

Você pode fazer com que os usuários acessem diretamente uma página de inscrição em vez de uma página de login, especificando a propriedade screen_hint=signup no objeto de configuração loginWithRedirect():

{
  screen_hint: "signup",
}

🛠 Crie um arquivo signup-button.js no diretório src/components/:

touch src/components/signup-button.js

🛠 Preencha src/components/signup-button.js da mesma forma para definir um componente SignupButton:

// src/components/signup-button.js

import React from "react";
import { useAuth0 } from "@auth0/auth0-react";

const SignupButton = () => {
  const { loginWithRedirect } = useAuth0();
  return (
    <button
      className="btn btn-primary btn-block"
      onClick={() =>
        loginWithRedirect({
          screen_hint: "signup",
        })
      }
    >
      Sign Up
    </button>
  );
};

export default SignupButton;

O uso do recurso de inscrição exige que você ative o Auth0 New Universal Login Experience em seu tenant.

🛠 Abra a seção Login Universal do dashboard Auth0 e escolha a opção "New" na subseção "Experience".

opções da Auth0 Universal Login Experience

🛠 Role para baixo e clique no botão "Save changes".

A diferença entre a experiência de usuário em LoginButton e SignupButton ficará mais evidente quando você integrar esses componentes à aplicação React e vê-los em ação. Você fará isso nas próximas seções.

Crie um botão de logout

🛠 Crie um arquivo logout-button.js no diretório src/components/:

touch src/components/logout-button.js

Preencha src/components/logout-button.js assim:

// src/components/logout-button.js

import React from "react";
import { useAuth0 } from "@auth0/auth0-react";

const LogoutButton = () => {
  const { logout } = useAuth0();
  return (
    <button
      className="btn btn-danger btn-block"
      onClick={() =>
        logout({
          returnTo: window.location.origin,
        })
      }
    >
      Log Out
    </button>
  );
};

export default LogoutButton;

O método logout() exposto por Auth0Context limpa a sessão da aplicação e redireciona para o endpoint Auth0 /v2/logout para limpar a sessão Auth0. Com os métodos de login, você pode passar um argumento de objeto para logout() para definir parâmetros para a chamada /v2/logout. Este processo é praticamente invisível para o usuário. Consulte LogoutOptions para obter mais detalhes.

Aqui, você passa a opção returnTo para especificar a URL onde Auth0 deve redirecionar seus usuários após o logout. No momento, você está trabalhando localmente e as "Allowed Logout URLs" da sua aplicação Auth0 apontam para http://localhost:4040.

No entanto, se você for fazer o deploy da sua aplicação React para produção, será necessário adicionar a URL de logout de produção à lista "Allowed Logout URLs" e garantir que a Auth0 redirecione seus usuários para essa URL de produção e não para o localhost. Definir returnTo como window.location.origin fará exatamente isso.

Leia mais sobre como funciona o Logout na Auth0 (em inglês).

Integre os botões de login e logout

Vamos envolver o LoginButton e o LogoutButton em um componente chamado AuthenticationButton.

🛠 Crie um arquivo authentication-button.js no diretório src/components/:

touch src/components/authentication-button.js

🛠 Preencha src/components/authentication-button.js com o seguinte código:

// src/components/authentication-button.js

import React from "react";
import LoginButton from "./login-button";
import LogoutButton from "./logout-button";
import { useAuth0 } from "@auth0/auth0-react";

const AuthenticationButton = () => {
  const { isAuthenticated } = useAuth0();

  return isAuthenticated ? <LogoutButton /> : <LoginButton />;
};

export default AuthenticationButton;

isAuthenticated é um valor booleano exposto pelo Auth0Context. Seu valor é true quando Auth0 autenticou o usuário e false quando não o fez.

Existem algumas vantagens em usar este wrapper de componente AuthenticationButton:

Você pode construir interfaces flexíveis. AuthenticationButton serve como uma opção de "login/logout" que você pode colocar em qualquer lugar que precisar dessa funcionalidade de troca. No entanto, você ainda tem componentes LoginButton e LogoutButton separados para os casos em que você precise de sua funcionalidade de forma isolada. Por exemplo, você pode ter um LogoutButton em uma página que somente usuários autenticados podem ver.

Você pode construir interfaces extensíveis. Você pode facilmente trocar o componente LoginButtonpelo componente SignupButton em AuthenticationButton para criar uma opção de "sign up/log out". Você também pode envolver a opção "sign up/log out" em um componente NewAuthenticationButton.

Você pode construir interfaces declarativas. Usando AuthenticationButton, você pode adicionar a funcionalidade de login e logout ao seu componente NavBar, por exemplo, sem pensar nos detalhes de implementação de como a troca de autenticação funciona.

🛠 Com isso em mente, crie um arquivo auth-nav.js no diretóriosrc/components/:

touch src/components/auth-nav.js

🛠 Preencha src/components/auth-nav.js assim:

// src/components/auth-nav.js

import React from "react";
import AuthenticationButton from "./authentication-button";

const AuthNav = () => (
  <div className="navbar-nav ml-auto">
    <AuthenticationButton />
  </div>
);

export default AuthNav;

🛠 Finalmente, abra o nav-bar.js no diretório src/components/ e atualize-o assim:

// src/components/nav-bar.js

import React from "react";

import MainNav from "./main-nav";
import AuthNav from "./auth-nav";

const NavBar = () => {
  return (
    <div className="nav-container mb-3">
      <nav className="navbar navbar-expand-md navbar-light bg-light">
        <div className="container">
          <div className="navbar-brand logo" />
          <MainNav />
          <AuthNav />
        </div>
      </nav>
    </div>
  );
};

export default NavBar;

Por ter diferentes tipos de subcomponentes da barra de navegação, você pode estender cada um conforme necessário, sem reabrir e modificar o componente principal NavBar.

🛠 Vá em frente e tente fazer o login. Sua aplicação React redireciona você para a página de login universal da Auth0. Você pode usar um formulário para fazer login com um nome de usuário e senha ou um provedor de identidade social como o Google. Observe que esta página de login também oferece a opção de se inscrever.

Novo formulário da nova experiência do Login Universal Auth0

Faça um teste: use o componente SignupButton

Troque o componente LoginButton pelo componente SignupButton na operação ternária definida no corpo do componente AuthenticationButton.

Ao clicar no botão "Sign Up" (Inscreva-se), você será direcionado para uma página com linguagem otimizada para incentivar a se inscrever em sua aplicação React.

Experimente isso!

aNew Auth0 Universal Login Experience Signup Page Nova página de inscrição na Experiência de Login Universal Auth0

Depois de concluir esse teste, troque SignupButton por LoginButton para continuar com o restante deste guia.

Você pode personalizar a aparência das novas páginas de Login Universal. Você também pode substituir qualquer texto na "New Experience" usando a API de Personalização de Texto.

Observe que, quando você termina de fazer login e a Auth0 redireciona você para sua aplicação React, o botão de login aparece rapidamente (cor azul) e, em seguida, o botão de logout é renderizado (cor vermelha).

A interface do usuário "pisca" porque sua aplicação React não sabe se a Auth0 já autenticou o usuário. Sua aplicação saberá o status de autenticação do usuário depois que o SDK da Auth0 para React for carregado.

🛠 Para corrigir a intermitência da IU, use o valor booleano isLoadingexposto pelo Auth0Context para renderizar o componente do App assim que o SDK da Auth0 para React terminar de carregar.

🛠 Abra o arquivo src/app.js e atualize-o da seguinte forma:

// src/app.js

import React from "react";
import { Route, Switch } from "react-router-dom";
import { useAuth0 } from "@auth0/auth0-react";

import { NavBar, Footer, Loading } from "./components";
import { Home, Profile, ExternalApi } from "./views";

import "./app.css";

const App = () => {
  const { isLoading } = useAuth0();

  if (isLoading) {
    return <Loading />;
  }

  return (
    <div id="app" className="d-flex flex-column h-100">
      <NavBar />
      <div className="container flex-grow-1">
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/profile" component={Profile} />
          <Route path="/external-api" component={ExternalApi} />
        </Switch>
      </div>
      <Footer />
    </div>
  );
};

export default App;

Enquanto o SDK está carregando, o componente de carregamento Loading, que é animado, é renderizado.

Recupere Informações De Usuário

Depois que um usuário efetua login com sucesso, Auth0 envia um ID token para sua aplicação React. Os sistemas de autenticação, como Auth0, usam ID tokens na autenticação baseada em token para armazenar em cache as informações do perfil do usuário e fornecê-las a uma aplicação cliente. O armazenamento em cache de ID tokens pode contribuir para melhorias no desempenho e capacidade de resposta da sua aplicação React.

Você pode usar os dados do ID token para personalizar a interface do usuário da sua aplicação React. O SDK da Auth0 para React decodifica o ID token e armazena seus dados no objeto do user exposto pelo Auth0Context. Algumas das informações do ID token incluem o nome, apelido, imagem e e-mail do usuário conectado.

Como você pode usar o ID token para criar uma página de perfil para seus usuários?

🛠 Atualize o componente Profile em src/views/profile.js da seguinte forma:

// src/views/profile.js

import React from 'react';

import { useAuth0 } from '@auth0/auth0-react';

const Profile = () => {
  const { user } = useAuth0();
  const { name, picture, email } = user;

  return (
    <div>
      <div className="row align-items-center profile-header">
        <div className="col-md-2 mb-3">
          <img
            src={picture}
            alt="Profile"
            className="rounded-circle img-fluid profile-picture mb-3 mb-md-0"
          />
        </div>
        <div className="col-md text-center text-md-left">
          <h2>{name}</h2>
          <p className="lead text-muted">{email}</p>
        </div>
      </div>
      <div className="row">
        <pre className="col-12 text-light bg-dark p-4">
          {JSON.stringify(user, null, 2)}
        </pre>
      </div>
    </div>
  );
};

export default Profile;

O que está acontecendo no componente Profile?

  • Você desestrutura o objeto user para obter o name, picture e o email.
  • Em seguida, você exibe essas três propriedades na interface do usuário. Como os dados vêm de um objeto simples, você não precisa buscá-los usando uma chamada assíncrona.
  • Por fim, você exibe o conteúdo completo do ID token decodificado em uma caixa de código. Agora você pode ver todas as outras propriedades disponíveis para uso.

O componente Profile renderiza informações do usuário que você pode considerar protegidas. Além disso, a propriedade do user é null se não houver um usuário conectado. Portanto, de qualquer forma, este componente só deve renderizar se a Auth0 tiver autenticado o usuário.

Como tal, você deve proteger a rota que renderiza este componente, http://localhost:4040/profile. Você aprenderá a fazer exatamente isso na próxima seção.

Proteja Rotas

O SDK da Auth0 para React expõe um componente de ordem superior (High-Order Component - HOC), withAuthenticationRequired, que você pode usar para proteger as rotas.Você também pode usar withAuthenticationRequired para criar um componente ProtectedRoute para proteger as rotas de uma forma mais declarativa usando o React Router.

Use um Higher-Order Component para proteger uma rota

🛠 Abra src/views/profile.js e atualize-o da seguinte maneira:

// src/views/profile.js

import React from 'react';

import { useAuth0, withAuthenticationRequired } from '@auth0/auth0-react';
import { Loading } from '../components';

const Profile = () => {
  const { user } = useAuth0();
  const { name, picture, email } = user;

  return (
    <div>
      <div className="row align-items-center profile-header">
        <div className="col-md-2 mb-3">
          <img
            src={picture}
            alt="Profile"
            className="rounded-circle img-fluid profile-picture mb-3 mb-md-0"
          />
        </div>
        <div className="col-md text-center text-md-left">
          <h2>{name}</h2>
          <p className="lead text-muted">{email}</p>
        </div>
      </div>
      <div className="row">
        <pre className="col-12 text-light bg-dark p-4">
          {JSON.stringify(user, null, 2)}
        </pre>
      </div>
    </div>
  );
};

export default withAuthenticationRequired(Profile, {
  onRedirecting: () => <Loading />,
});

Quando você envolve seus componentes no componente de ordem superior withAuthenticationRequired e os usuários que não efetuaram login visitam uma página que renderiza esse componente, sua aplicação React redireciona esse usuário para a página de login. Depois que o usuário faz login, a Auth0 redireciona o usuário para a aplicação React e o Auth0Provider leva os usuários para a página que pretendiam acessar antes do login.

withAuthenticationRequired leva os seguintes argumentos:

  • O componente que você deseja proteger.
  • Um objeto de configuração para customizar o fluxo de autenticação, WithAuthenticationRequiredOptions. Este objeto assume as seguintes propriedades opcionais:
    • loginOptions: ele se comporta exatamente como as opções de configuração que você pode passar para loginWithRedirect() para personalizar a experiência de login.
    • returnTo: permite especificar um caminho para o React redirecionar um usuário após a transação de login que o usuário acionou neste componente.
    • onRedirecting: renderiza um componente enquanto a aplicação React redireciona o usuário para a página de login.

No exemplo acima, os usuários que não efetuaram login veem o componente de carregamento Loading assim que acessam a rota /profile:

export default withAuthenticationRequired(Profile, {
  onRedirecting: () => <Loading />,
});

O componente onRedirecting melhora a experiência do usuário, evitando o aparecimento de componentes de IU mistos (componentes protegidos e públicos).

🛠 Experimente. Faça o log out e visite http://localhost:4040/profile. Sua aplicação React deve redirecionar você para a página de login universal da Auth0.

E se você estiver usando o React Router?

Usar withAuthenticationRequired para envolver o componente diretamente não é a maneira mais declarativa de construir uma aplicação React. Se você olhasse as rotas definidas no componente App, não seria capaz de dizer quais rotas estão protegidas e quais são públicas.

const App = () => {
  const { isLoading } = useAuth0();

  if (isLoading) {
    return <Loading />;
  }

  return (
    <div id="app" className="d-flex flex-column h-100">
      <NavBar />
      <div className="container flex-grow-1">
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/profile" component={Profile} />
          <Route path="/external-api" component={ExternalApi} />
        </Switch>
      </div>
      <Footer />
    </div>
  );
};

🛠 Em vez de usar withAuthenticationRequired diretamente, você pode envolvê-lo em um componente ProtectedRoute que aproveita os recursos do React Router.

Crie um componente para proteger os caminhos do React Router

A aplicação inicial usa React Router como sua biblioteca de roteamento. Este exemplo se aplica apenas a essa biblioteca.

Nesta seção, você criará um componente ProtectedRoute que usa o componente Route do React Router para renderizar o componente de ordem superior withAuthenticationRequired. A vantagem dessa abordagem é que seu ProtectedRoute terá a mesma API de um componente Route pronto para uso. Dessa forma, você pode compor o ProtectedRoute com outros componentes do React Router organicamente.

🛠 Para começar, crie um arquivo protected-route.js no diretório src/auth:

touch src/auth/protected-route.js

🛠 Preencha src/auth/protected-route.js da seguinte forma:

// src/auth/protected-route.js

import React from 'react';
import { Route } from 'react-router-dom';
import { withAuthenticationRequired } from '@auth0/auth0-react';
import { Loading } from '../components/index';

const ProtectedRoute = ({ component, ...args }) => (
  <Route
    component={withAuthenticationRequired(component, {
      onRedirecting: () => <Loading />,
    })}
    {...args}
  />
);

export default ProtectedRoute;

🛠 Por último, abra o arquivo src/app.js. Localize o componente Switch e altere os componentes Route para os caminhos /profile e /external-api para um componente ProtectedRoute:

// src/app.js

import React from 'react';
import { Route, Switch } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';

import { NavBar, Footer, Loading } from './components';
import { Home, Profile, ExternalApi } from './views';
import ProtectedRoute from './auth/protected-route';

import './app.css';

const App = () => {
  const { isLoading } = useAuth0();

  if (isLoading) {
    return <Loading />;
  }

  return (
    <div id="app" className="d-flex flex-column h-100">
      <NavBar />
      <div className="container flex-grow-1">
        <Switch>
          <Route path="/" exact component={Home} />
          <ProtectedRoute path="/profile" component={Profile} />
          <ProtectedRoute path="/external-api" component={ExternalApi} />
        </Switch>
      </div>
      <Footer />
    </div>
  );
};

export default App;

Você não precisa mais usar componente de ordem superior withAuthenticationRequired diretamente no componente Profile.

🛠 Abra src/views/profile.js e reverta o arquivo ao seu conteúdo anterior:

// src/views/profile.js

import React from 'react';

import { useAuth0 } from '@auth0/auth0-react';

const Profile = () => {
  const { user } = useAuth0();
  const { name, picture, email } = user;

  return (
    <div>
      <div className="row align-items-center profile-header">
        <div className="col-md-2 mb-3">
          <img
            src={picture}
            alt="Profile"
            className="rounded-circle img-fluid profile-picture mb-3 mb-md-0"
          />
        </div>
        <div className="col-md text-center text-md-left">
          <h2>{name}</h2>
          <p className="lead text-muted">{email}</p>
        </div>
      </div>
      <div className="row">
        <pre className="col-12 text-light bg-dark p-4">
          {JSON.stringify(user, null, 2)}
        </pre>
      </div>
    </div>
  );
};

export default Profile;

🛠 Agora você pode testar se esses dois caminhos exigem que os usuários façam login antes de acessá-los. Faça o log out e tente acessar a página Profile ou External API. Se funcionar, o React redireciona você para fazer o login com Auth0.

Os protetores do lado do cliente melhoram a experiência do usuário em sua aplicação React, não sua segurança.

No Security StackExchange, Conor Mancone explica que as proteções do lado do servidor visam proteger os dados, enquanto as proteções do lado do cliente visam melhorar a experiência do usuário.

As principais conclusões de sua resposta são:

  • Você não pode confiar em restrições do lado do cliente, como protetores de navegação e rotas protegidas, para proteger informações confidenciais.
    • Os invasores podem eventualmente contornar as restrições do lado do cliente.
  • Seu servidor não deve retornar nenhum dado que um usuário não deva acessar.
    • Retornar todos os dados do usuário do servidor e permitir que o framework de front-end decida o que exibir e o que ocultar com base no status de autenticação do usuário é uma abordagem errada.
    • Qualquer pessoa pode abrir as ferramentas para pessoa desenvolvedoras do navegador e inspecionar as solicitações de rede para visualizar todos os dados.
  • O uso de protetores de navegação ajuda a melhorar a experiência do usuário, não a segurança do usuário.
    • Sem protetores, um usuário que não fez login pode entrar em uma página com informações restritas e ver um erro, como "Acesso negado".
    • Com protetores que correspondem às permissões do servidor, você pode impedir que os usuários vejam erros, impedindo-os de visitar a página restrita.

Qual estratégia de proteção de rota você prefere usar nas suas aplicações React? O componente de ordem superior withAuthenticationRequired ou o componente ProtectedRoute? Por favor, conte nos comentários.

Chame Uma API

Esta seção se concentra em mostrar como obter um token de acesso (access token, em inglês) na sua aplicação React e como usá-lo para fazer chamadas de API para endpoints protegidos.

Ao usar a Auth0, você delega o processo de autenticação a um serviço centralizado. A Auth0 oferece a funcionalidade de fazer login e logout de usuários da aplicação React. No entanto, sua aplicação pode precisar acessar recursos protegidos de uma API.

Você também pode proteger uma API com Auth0. Existem vários guias de início rápido de API para te ajudar a integrar a Auth0 com sua plataforma de back-end.

Ao usar a Auth0 para proteger sua API, você também delega o processo de autorização a um serviço centralizado que garante que apenas aplicações de cliente aprovadas possam acessar recursos protegidos em nome de um usuário.

Como você pode fazer chamadas de API seguras a partir do React?

Sua aplicação React autentica o usuário e recebe um token de acesso da Auth0. A aplicação pode então passar esse token de acesso para sua API como uma credencial. Por sua vez, sua API pode usar bibliotecas Auth0 para verificar o token de acesso que recebe da aplicação e emitir uma resposta com os dados desejados.

Em vez de criar uma API do zero para testar os fluxos de autenticação e autorização entre o cliente e o servidor, você usará uma API de demonstração Express API que preparei para você.

Obtenha a API de demonstração Express API

🛠 Abra o terminal em uma nova janela e clone o repositório auth0-express-js-sample em algum lugar do sistema. Certifique-se de fazer o clone fora do diretório do projeto React.

git clone git@github.com:auth0-blog/auth0-express-js-sample.git

🛠 Depois de clonar este repositório, torne o diretório auth0-express-js-sample seu diretório atual:

cd auth0-express-js-sample

🛠 Instale as dependências do projeto Node.js:

npm install

Conecte a Express API com Auth0

Crie uma ponte de comunicação entre React e Auth0

Este processo é semelhante a como você conectou o React à Auth0.

🛠 Vá para a seção APIs no Dashboard da Auth0 e clique no botão "Create API".

🛠 Então, a Auth0 mostra:

  • Adicione um nome à sua API:
Auth0 Express Sample
  • Defina seu valor de identificador:
https://express.sample
  • Deixe o algoritmo de assinatura como RS256 , pois é a melhor opção do ponto de vista de segurança.

formulário de nova API no Auth0 Dashboard

Identificadores são strings únicas que ajudam a Auth0 a diferenciar suas APIs. Recomendamos o uso de URLs para facilitar a criação de identificadores únicos de maneira previsível; no entanto, a Auth0 nunca chama essas URLs.

🛠 Com esses valores preenchidos, clique no botão "Create". Mantenha esta página aberta, pois você precisará de alguns de seus valores na próxima seção.

Adicione as variáveis de configuração da Auth0 à Express

🛠 Crie um arquivo .env para o servidor da API no diretório auth0-express-js-sample:

touch .env

🛠 Preencha este arquivo auth0-express-js-sample/.env da seguinte forma:

SERVER_PORT=6060
CLIENT_ORIGIN_URL=http://localhost:4040
AUTH0_AUDIENCE=
AUTH0_DOMAIN=

🛠 Volte para a página da API no dashboard da Auth0 e siga estas etapas para obter a Auth0 Audience:

Obtenha a Auth0 Audience para configurar uma API

  1. 🛠 Clique na guia "Settings".
  2. 🛠 Localize o campo "Identifier" e copie seu valor.
  3. 🛠 Cole o valor "Identifier" como o valor de AUTH0_AUDIENCE no arquivo .env.

Agora, siga estas etapas para obter o valor do Auth0 Domain:

Obtenha o Auth0 Domain para configurar uma API

  1. 🛠 Clique na aba "Test".
  2. 🛠 Localize a seção chamada "Asking Auth0 for tokens from my application".
  3. 🛠 Clique na guia cURL para mostrar uma requisição POST simulada.
  4. 🛠 Copie seu domínio Auth0, que faz parte do valor do parâmetro --url: nome-do-tenant.regiao.auth0.com.
  5. 🛠 Cole o valor do domínio Auth0 como o valor de AUTH0_DOMAIN no arquivo .env.
Dicas para obter o Auth0 Domain
  • O Auth0 Domain é a substring entre o protocolo, https:// e o caminho /oauth/token.
  • O Auth0 Domain segue este padrão: nome-do-tenant.regiao.auth0.com.
  • O subdomínio da região (au, us ou eu) é opcional. Alguns domínios Auth0 não o possuem.
  • Clique na imagem acima, por favor, se tiver alguma dúvida de como obter o valor do Auth0 Domain.

🛠 Com os valores de configuração .env definidos, rode o servidor da API executando o seguinte comando:

npm start

Configure o React para se conectar com a Express API

🛠 Volte para o diretório do projeto auth0-react-sample que armazena sua aplicação React.

🛠 Localize o arquivo auth0-react-sample/.env e adicione seus valores Auth0 Audience e Server URL a ele:

REACT_APP_AUTH0_DOMAIN=YOUR-AUTH0-DOMAIN
REACT_APP_AUTH0_CLIENT_ID=YOUR-AUTH0-APP-CLIENT-ID
REACT_APP_AUTH0_AUDIENCE=https://express.sample
REACT_APP_SERVER_URL=http://localhost:6060

🛠 O valor do REACT_APP_AUTH0_AUDIENCE é o mesmo que AUTH0_AUDIENCE que você colocou no arquivo auth0-express-js-sample/.env.

Por que todas as variáveis no arquivo React .env começam com REACT_APP_?

create-react-app exige que você crie variáveis de ambiente personalizadas começando com REACT_APP_ ao usar um arquivo .env. create-react-app irá ignorar quaisquer outras variáveis, exceto NODE_ENV.

O prefixo REACT_APP_ reduz o risco de exposição acidental de uma chave privada da sua máquina que pode ter o mesmo nome de uma variável do arquivo .env.

Sua aplicação React precisa passar um token de acesso ao chamar uma API de destino para acessar recursos protegidos. Você pode solicitar um token de acesso em um formato que a API pode verificar passando as propriedades da audiência e escopo para Auth0Provider.

Quaisquer mudanças feitas nas variáveis de ambiente React exigem que você reinicie o servidor de desenvolvimento se ele estiver em execução.

🛠 Reinicie sua aplicação React para que ela possa usar os novos valores que você definiu em auth0-react-sample/.env.

🛠 Atualize o arquivo auth0-provider-with-history.js no diretórioauth0-react-sample/src/auth para adicionar a propriedade audience:

// src/auth/auth0-provider-with-history.js

import React from 'react';
import { useHistory } from 'react-router-dom';
import { Auth0Provider } from '@auth0/auth0-react';

const Auth0ProviderWithHistory = ({ children }) => {
  const history = useHistory();
  const domain = process.env.REACT_APP_AUTH0_DOMAIN;
  const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
  const audience = process.env.REACT_APP_AUTH0_AUDIENCE;

  const onRedirectCallback = (appState) => {
    history.push(appState?.returnTo || window.location.pathname);
  };

  return (
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      redirectUri={window.location.origin}
      onRedirectCallback={onRedirectCallback}
      audience={audience}
    >
      {children}
    </Auth0Provider>
  );
};

export default Auth0ProviderWithHistory;

Por que o valor de audiência da Auth0 é o mesmo para as duas aplicações? Auth0 usa o valor da propriedade audience para determinar qual servidor de recursos (API) o usuário está autorizando sua aplicação React a acessar. É como um número de telefone. Você deseja garantir que sua aplicação React "envie o texto para a API certa".

As ações que sua aplicação React pode executar na API dependem dos escopos que seu token de acesso contém, que você define como o valor de uma propriedade scope em Auth0Provider.

Lembra daquela tela que você viu quando fez login pela primeira vez com Auth0 pedindo permissão para acessar as informações de seu perfil? Sua aplicação React fará a requisição da autorização do usuário para acessar os escopos requisitados e o usuário aprovará ou negará a requisição. Você pode ter visto algo semelhante ao compartilhar seus contatos ou fotos de uma plataforma de mídia social com uma aplicação de terceiros.

Quando você não passa uma propriedade scope para Auth0Provider como no exemplo acima, o SDK da Auth0 para React usa por padrão os escopos do OpenID Connect: openid profile email.

  • openid: Este escopo informa ao servidor de autorização Auth0 que o cliente está fazendo uma requisição OpenID Connect (OIDC) para verificar a identidade do usuário. OpenID Connect é um protocolo de autenticação.
  • profile: este valor de escopo requer acesso às informações de perfil padrão do usuário, como name, nickname e picture.
  • email: este valor de escopo requer acesso às informações de email e email_verified.

Os detalhes dos escopos do OpenID Connect vão para o ID token. No entanto, você pode definir escopos de API personalizados para implementar o controle de acesso. Você identificará esses escopos personalizados nas chamadas que suas aplicações clientes fazem para essa API. Auth0 inclui escopos de API no token de acesso como o valor de declaração de scope.

Os conceitos sobre escopos ou permissões de API são melhor abordados em um tutorial de API Auth0, como “Use TypeScript to Create a Secure API with Node.js and Express: Role-Based Access Control”.

🛠 Atualize src/views/external-api.js da seguinte forma:

// src/views/external-api.js

import React, { useState } from 'react';
import { useAuth0 } from '@auth0/auth0-react';

const ExternalApi = () => {
  const [message, setMessage] = useState("");
  const serverUrl = process.env.REACT_APP_SERVER_URL;

  const { getAccessTokenSilently } = useAuth0();

  const callApi = async () => {
    try {
      const response = await fetch(`${serverUrl}/api/messages/public-message`);

      const responseData = await response.json();

      setMessage(responseData.message);
    } catch (error) {
      setMessage(error.message);
    }
  };

  const callSecureApi = async () => {
    try {
      const token = await getAccessTokenSilently();

      const response = await fetch(
        `${serverUrl}/api/messages/protected-message`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }
      );

      const responseData = await response.json();

      setMessage(responseData.message);
    } catch (error) {
      setMessage(error.message);
    }
  };

  return (
    <div className="container">
      <h1>External API</h1>
      <p>
        Use these buttons to call an external API. A chamada API protegida tem um
token de acesso em seu cabeçalho de autorização. O servidor API irá validar
o token de acesso usando o valor Auth0 Audience.
      </p>
      <div
        className="btn-group mt-5"
        role="group"
        aria-label="External API Requests Examples"
      >
        <button type="button" className="btn btn-primary" onClick={callApi}>
          Get Public Message
        </button>
        <button
          type="button"
          className="btn btn-primary"
          onClick={callSecureApi}
        >
          Get Protected Message
        </button>
      </div>
      {message && (
        <div className="mt-5">
          <h6 className="muted">Result</h6>
          <div className="container-fluid">
            <div className="row">
              <code className="col-12 text-light bg-dark p-4">{message}</code>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default ExternalApi;

O que está acontecendo agora no componente ExternalApi?

  • Você adiciona um método callApi() que executa uma requisição de API pública.
  • Você adiciona um método callSecureApi() que executa uma requisição de API segura da seguinte maneira:
    • (a) Obtenha o token de acesso da Auth0 usando o método getAccessTokenSilently, que fornece à sua aplicação React um novo token de acesso por baixo dos panos sem exigir que o usuário efetue login novamente.
    • (b) Passe esse token de acesso como uma credencial no cabeçalho de autorização (bearer) da requisição.
  • Use o hook do React useState() para atualizar a interface do usuário sempre que qualquer uma das chamadas de API descritas for concluída com êxito.

Sua requisição de login anterior não incluía um parâmetro de audiência. Como tal, o React SDK não tem um token de acesso armazenado na memória.

Você não deve armazenar tokens em localStorage Por quê?

Armazenar tokens no local storage do navegador fornece persistência nas atualizações de página e guias do navegador, no entanto, se um invasor puder rodar JavaScript na aplicação de página única (Single Page Application ou SPA em inglês) usando um ataque de script entre sites (XSS), ele poderá recuperar os tokens armazenados no local storage.

Uma vulnerabilidade que leva a um ataque XSS bem-sucedido pode estar no código-fonte da SPA ou em qualquer código JavaScript de terceiros incluído no SPA, como Bootstrap, jQuery ou Google Analytics.

🛠 Efetue logout e login novamente para obter um novo token de acesso do Auth0 que inclui as informações de audiência.

🛠 Visite http://localhost:4040/external-api e clique em qualquer um dos botões na página da External API para testar as respostas.

Obtenha a mensagem pública

The API doesn't require an access token to share this message.

Obtenha a mensagem protegida

The API successfully validated your access token.

Conclusão

Você implementou a autenticação de usuário no React para identificar seus usuários, obter informações de perfil de usuário e controlar o conteúdo que seus usuários podem acessar protegendo rotas e recursos de API.

Este tutorial mostrou o caso de uso de autenticação mais comum para uma aplicação React: login e logout simples. No entanto, a Auth0 é uma plataforma extensível e flexível que pode te ajudar a chegar mais longe. Se você tiver um caso de uso mais complexo, verifique os Auth0 Architecture Scenarios para saber mais sobre os cenários de arquitetura típicos que identificamos ao trabalhar com clientes na implementação da Auth0.

Num próximo tutorial, vamos cobrir padrões e ferramentas de autenticação avançados, como usar um pop-up em vez de um redirecionamento para fazer login de usuários, adicionar permissões a ID tokens, usar metadados para aprimorar perfis de usuário e muito mais.

Comente o que você achou deste tutorial. Obrigado pela leitura.

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon