Autenticação é uma daquelas coisas que sempre parece exigir muito mais esforço do que queremos. Para configurar a autenticação, é preciso pesquisar novamente tópicos sobre os quais você não pensou desde a última vez em que fez a autenticação, e a natureza acelerada do espaço significa que as coisas mudaram com frequência nesse meio tempo. Novas ameaças, novas opções e novas atualizações podem tê-lo deixado na dúvida e vasculhando documentos em seus projetos anteriores.

Neste artigo, apresentamos uma abordagem diferente para autenticação (e controle de acesso, SSO e muito mais) em aplicativos React. Em vez de adicionar uma biblioteca estática que você precisa manter atualizada ou pesquisar novamente sempre que quiser implementar a autenticação, usaremos um serviço que se mantém atualizado automaticamente e é uma alternativa muito mais simples ao Auth0, Okta e outros.

Autenticação React

Normalmente, usamos uma abordagem semelhante ao escrever a autenticação no React: nosso aplicativo React faz uma solicitação ao nosso servidor de autenticação, que retorna um token de acesso. Esse token é salvo no navegador e pode ser usado em solicitações subsequentes ao seu servidor (ou a outros servidores, se necessário). Seja escrevendo um e-mail e um carimbo padrão; autenticação por senha ou usando links mágicos ou logins de logon único (SSO) como Google, Azure ou Facebook, queremos que nosso aplicativo React envie uma solicitação inicial a um servidor de autenticação e que esse servidor lide com toda a complexidade da geração de um token.

Portanto, a responsabilidade do React na autenticação é:

  1. Enviar a solicitação inicial para o servidor de autenticação
  2. Receber e armazenar o token de acesso
  3. Enviar o token de acesso ao seu servidor com cada solicitação subsequente

Tokens de acesso JWT

Os JSON Web Tokens (JWTs) são tokens compactos e seguros para URL que podem ser usados para autenticação e controle de acesso em aplicativos React. Cada JWT tem um objeto JSON simples como sua “carga útil” e é assinado de forma que o servidor possa verificar se a carga útil é autêntica. Um exemplo de JWT seria parecido com:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9.f7iKN-xi24qrQ5NQtOe0jiriotT-rve3ru6sskbQXnA

A carga desse token é a seção do meio (separada por pontos):

eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9

A carga útil do JWT pode ser decodificada a partir da base64 para gerar o objeto JSON:

JSON.parse(atob("eyJ1c2VySWQiOjEsImF1dGhvcml6YXRpb24iOiJhZG1pbiJ9"));

// =>
{
  “userId”: 1,
  “authorization”: “admin”
}

É importante observar que essa carga útil pode ser lida por qualquer pessoa com o JWT, incluindo seu aplicativo React ou um terceiro.

Qualquer pessoa que tenha o JWT pode ler seu conteúdo. No entanto, somente o servidor de autenticação pode gerar JWTs válidos – seu aplicativo React, seu servidor de aplicativos ou um terceiro mal-intencionado não podem gerar JWTs válidos. Portanto, além de ler o JWT, o servidor também precisa verificar se o JWT é autêntico, comparando-o com uma chave pública. Isso permite que o servidor de aplicativos verifique os JWTs recebidos e rejeite quaisquer tokens que não tenham sido criados pelo servidor de autenticação ou que tenham expirado.

O fluxo para usar um JWT em um aplicativo React tem a seguinte aparência:

  1. Seu aplicativo React solicita um JWT sempre que o usuário quiser se conectar.
  2. O servidor de autenticação gera um JWT usando uma chave privada e, em seguida, envia o JWT de volta ao seu aplicativo React.
  3. O aplicativo React armazena esse JWT e o envia ao servidor de aplicativos sempre que o usuário precisar fazer uma solicitação.
  4. O servidor de aplicativos verifica o JWT usando uma chave pública e, em seguida, lê a carga útil para determinar qual usuário está fazendo a solicitação.

Cada uma dessas etapas é simples de escrever, mas cada etapa tem suas próprias armadilhas quando o senhor realmente deseja implementá-la e mantê-la segura. Especialmente com o passar do tempo, à medida que surgem novos vetores de ameaças e novas plataformas precisam ser corrigidas ou receber suporte, a sobrecarga de segurança pode aumentar rapidamente.

Userfront elimina a complexidade da autenticação em aplicativos React

O Userfront é uma estrutura que abstrai a complexidade da autenticação. Isso facilita muito o trabalho com a autenticação em um aplicativo React e, talvez o mais importante, mantém todos os protocolos de autenticação atualizados para você automaticamente ao longo do tempo.

A filosofia subjacente do Userfront é que a autenticação de classe mundial não deve exigir esforço – deve ser fácil de configurar, e as atualizações de segurança devem ocorrer automaticamente para o usuário. O Userfront tem todos os recursos de autenticação, Single Sign On (SSO), controle de acesso e multilocação, com uma camada gratuita pronta para produção de até 10.000 usuários ativos mensais. Para a maioria dos aplicativos React modernos, é uma ótima solução.

Configurando a autenticação no React

Agora, vamos analisar a criação de todos os principais aspectos da autenticação em um aplicativo React. O código final para este exemplo está disponível aqui.

Use seu boilerplate favorito para configurar seu aplicativo React e colocar seu pipeline de compilação em ordem. Neste artigo, usaremos o Criar aplicativo Reactque faz grande parte do trabalho de configuração para nós, e também adicionaremos Roteador React para nosso roteamento no lado do cliente. Comece instalando o Create React App e o React Router:

npx create-react-app my-app
cd my-app
npm install react-router-dom --save
npm start

Agora nosso aplicativo React está disponível em http://localhost:3000

Criar autenticação de aplicativo React

Como ele diz, agora podemos editar o arquivo src/App.js para começar a trabalhar.

Substitua o conteúdo de src/App.js pelo seguinte, com base no início rápido do React Router:

// src/App.js

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
            <li>
              <Link to="/reset">Reset</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/reset">
            <PasswordReset />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function Login() {
  return <h2>Login</h2>;
}

function PasswordReset() {
  return <h2>Password Reset</h2>;
}

function Dashboard() {
  return <h2>Dashboard</h2>;
}

Agora temos um aplicativo muito simples com roteamento:

RoteamentoDescrição
/Página inicial
/loginPágina de login
/resetPágina de redefinição de senha
/dashboardPainel de controle do usuário, somente para usuários conectados
Autenticação do React Router

Essa é toda a estrutura de que precisamos para começar a adicionar a autenticação.

Inscrição, login e redefinição de senha com o Userfront

Primeiro, crie uma conta do Userfront em https://userfront.com. Isso lhe dará um formulário de inscrição, um formulário de login e um formulário de redefinição de senha que o senhor poderá usar nas próximas etapas.

Na seção Toolkit (Kit de ferramentas) do painel do Userfront, você encontrará as instruções para instalar o formulário de inscrição:

Instruções de instalação do Userfront

Siga as instruções instalando o pacote de reação do Userfront com:

npm install @userfront/react --save
npm start

Em seguida, adicione o formulário à sua página inicial importando e inicializando o Userfront e, depois, atualizando o Home() para renderizar o formulário.

// src/App.js

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Userfront from "@userfront/react";

Userfront.init("demo1234");

const SignupForm = Userfront.build({
  toolId: "nkmbbm",
});

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
            <li>
              <Link to="/reset">Reset</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/reset">
            <PasswordReset />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
      <SignupForm />
    </div>
  );
}

function Login() {
  return <h2>Login</h2>;
}

function PasswordReset() {
  return <h2>Password Reset</h2>;
}

function Dashboard() {
  return <h2>Dashboard</h2>;
}

Agora a página inicial tem seu formulário de inscrição. Tente inscrever um usuário:

Formulário de inscrição React

O formulário está no “Modo de teste” por padrão, o que criará registros de usuários em um ambiente de teste que pode ser visualizado separadamente no painel do Userfront:

Modo de teste do Userfront

Continue adicionando seus formulários de login e redefinição de senha da mesma forma que adicionou o formulário de inscrição:

// src/App.js

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import Userfront from "@userfront/react";

Userfront.init("demo1234");

const SignupForm = Userfront.build({
  toolId: "nkmbbm",
});
const LoginForm = Userfront.build({
  toolId: "alnkkd",
});
const PasswordResetForm = Userfront.build({
  toolId: "dkbmmo",
});

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/login">Login</Link>
            </li>
            <li>
              <Link to="/reset">Reset</Link>
            </li>
            <li>
              <Link to="/dashboard">Dashboard</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/reset">
            <PasswordReset />
          </Route>
          <Route path="/dashboard">
            <Dashboard />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
      <SignupForm />
    </div>
  );
}

function Login() {
  return (
    <div>
      <h2>Login</h2>
      <LoginForm />
    </div>
  );
}

function PasswordReset() {
  return (
    <div>
      <h2>Password Reset</h2>
      <PasswordResetForm />
    </div>
  );
}

function Dashboard() {
  return <h2>Dashboard</h2>;
}

Nesse ponto, a inscrição, o login e a redefinição de senha devem estar funcionando.

Seus usuários podem se inscrever, fazer login e redefinir a senha.

Inscrição, login e redefinição de senha em React

Rota protegida no React

Normalmente, não queremos que os usuários consigam visualizar o painel a menos que estejam conectados. Isso é conhecido como proteger uma rota.

Sempre que um usuário não estiver conectado, mas tentar visitar o /dashboard, podemos redirecioná-lo para a tela de login.

Podemos fazer isso atualizando o Dashboard no componente src/App.js para lidar com a lógica condicional.

Quando um usuário estiver conectado ao Userfront, ele terá um token de acesso disponível como Userfront.accessToken(). Podemos verificar esse token para determinar se o usuário está conectado.

Adicione o Redirect ao componente import para o React Router e, em seguida, atualize a declaração Dashboard para redirecionar se nenhum token de acesso estiver presente.

// src/App.js

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  Redirect, // Be sure to add this import
} from "react-router-dom";

// ...

function Dashboard() {
  function renderFn({ location }) {
    // If the user is not logged in, redirect to login
    if (!Userfront.accessToken()) {
      return (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: location },
          }}
        />
      );
    }

    // If the user is logged in, show the dashboard
    const userData = JSON.stringify(Userfront.user, null, 2);
    return (
      <div>
        <h2>Dashboard</h2>
        <pre>{userData}</pre>
        <button onClick={Userfront.logout}>Logout</button>
      </div>
    );
  }

  return <Route render={renderFn} />;
}

Observe também que adicionamos um botão de logout chamando Userfront.logout() diretamente:

<button onClick={Userfront.logout}>Logout</button>

Agora, quando um usuário estiver conectado, ele poderá visualizar o painel. Se o usuário não estiver conectado, ele será redirecionado para a página de login.

Rota protegida pelo React

Autenticação React com uma API

O senhor provavelmente desejará recuperar informações específicas do usuário do seu backend. Para proteger esses pontos de extremidade da API, seu servidor deve verificar se os JWTs recebidos são válidos.

Há muitas bibliotecas para ler e verificar JWTs em vários idiomas; aqui estão algumas bibliotecas populares para lidar com JWTs:

Para o Userfront, o token de acesso está disponível no seu aplicativo React como Userfront.accessToken().

Seu aplicativo React pode enviar isso como um Bearer dentro do token Authorization header. Por exemplo:

// Example of calling an endpoint with a JWT

async function getInfo() {
  const res = await window.fetch("/your-endpoint", {
    method: "GET",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${Userfront.accessToken()}`,
    },
  });

  console.log(res);
}

getInfo();

Para lidar com uma solicitação como essa, seu backend deve ler o JWT do cabeçalho Authorization e verificar se ele é válido usando a chave pública encontrada no painel do Userfront.

Aqui está um exemplo de middleware do Node.js para ler e verificar o JWT:

// Node.js example (Express.js) 
const jwt = require("jsonwebtoken"); 

function authenticateToken(req, res, next) {   
    // Read the JWT access token from the request header
    const authHeader = req.headers["authorization"];
    const token = authHeader && authHeader.split(" ")[1];
    if (token == null) return res.sendStatus(401); 
    // Return 401 if no token   
    // Verify the token using the Userfront public key   
    jwt.verify(token, process.env.USERFRONT_PUBLIC_KEY, (err, auth) => {     
        if (err) return res.sendStatus(403); // Return 403 if there is an error verifying
        req.auth = auth;
        next();
    }); 
}

Usando essa abordagem, todos os tokens inválidos ou ausentes seriam rejeitados pelo seu servidor. O senhor também pode fazer referência ao conteúdo do token mais tarde nos manipuladores de rota usando o req.auth :

console.log(req.auth);

// =>
{
  mode: 'test',
  tenantId: 'demo1234',
  userId: 1,
  userUuid: 'ab53dbdc-bb1a-4d4d-9edf-683a6ca3f609',
  isConfirmed: false,
  authorization: {
    demo1234: {
      tenantId: 'demo1234',
      name: 'Demo project',
      roles: ["admin"],
      permissions: []
    },
  },
  sessionId: '35d0bf4a-912c-4429-9886-cd65a4844a4f',
  iat: 1614114057,
  exp: 1616706057
}

Com essas informações, o senhor pode realizar outras verificações, conforme desejado, ou usar o userId ou userUuid para procurar informações do usuário para retornar.

Por exemplo, se o senhor quisesse limitar uma rota a usuários administradores, poderia verificar a variável authorization do token de acesso verificado:

// Node.js example (Express.js)

app.get("/users", (req, res) => {
  const authorization = req.auth.authorization["demo1234"] || {};

  if (authorization.roles.includes("admin")) {
    // Allow access
  } else {
    // Deny access
  }
});

React SSO (Single Sign On)

A partir daqui, o senhor pode adicionar provedores de identidade social, como Google, Facebook e LinkedIn, ao seu aplicativo React, ou provedores de identidade comercial, como Azure AD, Office365 e outros.

O senhor faz isso criando um aplicativo com o provedor de identidade (por exemplo, Google) e, em seguida, adicionando as credenciais desse aplicativo ao painel do Userfront. O resultado é uma experiência de login modificada:

Formulário SSO em React

Não é necessário nenhum código adicional para implementar o Single Sign On usando essa abordagem: o senhor pode adicionar e remover provedores sem atualizar seus formulários ou a maneira como lida com JWTs.

Notas finais

Adicionar autenticação e controle de acesso ao seu aplicativo React não precisa ser um incômodo. Tanto a etapa de configuração quanto, mais importante, a manutenção ao longo do tempo, são tratadas com plataformas modernas como Userfront.

Os tokens da Web JSON permitem que o senhor separe de forma limpa a camada de geração de tokens de autenticação do restante do aplicativo, facilitando o raciocínio e tornando-o mais modular para necessidades futuras. Essa arquitetura também permite que você concentre seus esforços no aplicativo principal, onde é provável que crie muito mais valor para si mesmo ou para seus clientes.

Para obter mais detalhes sobre adicionar autenticação ao seu aplicativo React, visite a seção Guia do Userfrontque abrange tudo, desde a configuração dos formulários de autenticação até a documentação da API, exemplos de repositórios, trabalho com diferentes linguagens e estruturas e muito mais.

Crie um projeto gratuito do Userfront

Patrocinado via Syndicate