Autenticação no servidor com Node js: Tokens vs. JWT

Autenticação no servidor com Node js: Tokens vs. JWT
AdsTerra, Junte-se ao AdsTerra

No desenvolvimento web moderno, a autenticação é uma parte crítica para garantir a segurança das aplicações. As duas abordagens mais populares incluem a autenticação no servidor usando tokens e a autenticação no cliente usando JSON Web Tokens (JWT).

Ambos os métodos têm suas próprias vantagens e desvantagens exclusivas, e decidir qual deles usar depende dos requisitos da sua aplicação específica. Neste artigo, vamos comparar ambos os métodos e destacar os benefícios e as desvantagens de cada um. Vamos começar!

Autenticação Stateless vs. Autenticação Stateful

Nas aplicações web, a autenticação é o processo de verificar a identidade de um usuário que deseja acessar um recurso restrito. É possível utilizar vários tipos diferentes de autenticação, como autenticação por nome de usuário e senha, login social ou autenticação biométrica.

Autenticação Stateless

Na autenticação Stateless, o servidor não armazena nenhuma informação de sessão sobre o usuário. Em vez disso, cada solicitação feita pelo usuário ao servidor contém todas as informações necessárias para autenticação, geralmente na forma de um JWT (JSON Web Token). O servidor, então, valida o token e responde de acordo.

A autenticação Stateless é popular em aplicações web modernas porque é escalável e pode ser usada com a arquitetura de microsserviços.

Autenticação Stateful

Na autenticação Stateful, o servidor armazena informações de sessão sobre o usuário em um banco de dados ou em um cache em memória. Quando o usuário faz login, o servidor cria um ID de sessão e o armazena no lado do servidor. Esse ID de sessão é então usado para autenticar solicitações subsequentes feitas pelo usuário.

A autenticação Stateful é menos escalável do que a autenticação Stateless, porque requer que o servidor mantenha um estado, o que pode se tornar um problema com grandes bases de usuários.

O que são tokens no lado do servidor?

O uso de tokens no lado do servidor, também conhecido como autenticação baseada em sessão, é um exemplo de autenticação com estado que envolve o armazenamento de dados de autenticação do usuário no servidor. Após a autenticação bem-sucedida, o servidor gera um token único para o usuário, que é então armazenado na memória ou no banco de dados do servidor. O token é posteriormente enviado de volta para o cliente, seja como um cookie ou no corpo da resposta.

A autenticação no lado do servidor com tokens envolve a criação de um token de sessão exclusivo para cada usuário quando eles fazem login. O token é armazenado no lado do servidor e usado para autenticar solicitações subsequentes do mesmo usuário.

Em contraste, a autenticação no lado do cliente usando JWT envolve a emissão de um token assinado para o cliente após o login bem-sucedido, que é então armazenado no lado do cliente e enviado de volta ao servidor com cada solicitação subsequente.

Vantagens do uso da autenticação no lado do servidor

Fácil de invalidar

A autenticação no lado do servidor é fácil de invalidar porque temos controle total sobre os dados de sessão armazenados no servidor. Se suspeitarmos de atividade fraudulenta, podemos encerrar rapidamente a sessão de um usuário ou revogar um token, fornecendo assim uma camada adicional de segurança.

Sem limitações de armazenamento

Ao contrário da autenticação no lado do cliente, onde o espaço de armazenamento é limitado a cookies ou armazenamento local, podemos armazenar qualquer quantidade de dados de sessão no servidor com a autenticação no lado do servidor. Isso facilita o armazenamento de grandes quantidades de dados, como preferências do usuário ou histórico.

Melhor para conformidade

Para atender às regulamentações de conformidade, alguns órgãos reguladores exigem que os dados de sessão sejam armazenados no lado do servidor. Isso tem como objetivo aprimorar a segurança de dados, privacidade e controle sobre informações sensíveis dentro de um ambiente controlado e seguro. Em tais casos, a autenticação no lado do servidor é uma opção melhor.

Não é necessário reautenticação

Com a autenticação no lado do servidor, não é necessário reautenticar o usuário a cada solicitação, o que pode melhorar o desempenho de sua aplicação.

Desvantagens da autenticação no lado do servidor

Problemas de escalabilidade

À medida que o número de usuários e sessões aumenta, armazenar todos os dados de sessão no servidor pode causar problemas de escalabilidade. Isso requer mais recursos de memória e CPU, o que pode resultar em tempos de resposta mais lentos e diminuir o desempenho geral.

Complexidade

A autenticação no lado do servidor pode ser complexa de implementar e manter, especialmente se você precisar armazenar os dados de sessão em vários servidores ou instâncias. Isso requer mais código, configuração e infraestrutura.

Custo

Devido à necessidade de mais recursos e infraestrutura, a autenticação no lado do servidor pode ser mais cara do que a autenticação no lado do cliente.

Sem acesso offline

Como todos os dados de sessão são armazenados no servidor, não há acesso offline disponível, o que pode ser uma desvantagem em alguns cenários.

Autenticação do lado do servidor com Node JS

Aqui está uma revisão da implementação de autenticação no lado do servidor usando uma aplicação Node.js. Primeiro, é necessário instalar o express-session e o pacote Express para Node.js, o que pode ser feito executando o seguinte código:

npm install express express-session

Em seguida, criaremos um arquivo index.js simples:

const express = require("express");
const session = require("express-session");
const app = express();
// Objeto de usuário fictício para demonstração
const users = [
  { id: 1, username: "john", password: "password" },
  { id: 2, username: "jane", password: "password" },
];
// Array para armazenar IDs de usuários na lista negra
const blacklistedUsers = [];
// Middleware para analisar corpos de solicitação JSON
app.use(express.json());
// Middleware para inicializar a sessão
app.use(
  session({
    secret: "minhachaveprivada",
    resave: false,
    saveUninitialized: true,
  })
);

// Endpoint de login
app.post("/login", (req, res) => {
  const { username, password } = req.body;
  // Encontre o usuário pelo nome de usuário e senha
  const user = users.find(
    (u) => u.username === username && u.password === password
  );
  if (user) {
    if (blacklistedUsers.includes(user.id)) {
      return res.status(403).send("Usuário está na lista negra");
    }
    // Salve o ID do usuário na sessão
    req.session.userId = user.id;
    // Envie o objeto do usuário de volta para o cliente
    res.json({ user });
  } else {
    // Envie uma resposta de erro se o usuário não for encontrado
    res.status(401).json({ message: "Nome de usuário ou senha inválido" });
  }
});
// Endpoint de logout
app.post("/logout", (req, res) => {
  // Destrói a sessão para desconectar o usuário
  req.session.destroy();
  // Envie uma resposta de sucesso
  res.json({ message: "Desconectado com sucesso" });
});
// Endpoint protegido
app.get("/perfil", (req, res) => {
  console.log(req.session.userId);
  // Verifique se o usuário está logado verificando se o ID do usuário está presente na sessão
  if (req.session.userId) {
    // Encontre o usuário pelo ID
    const user = users.find((u) => u.id === req.session.userId);
    // Envie o objeto do usuário de volta para o cliente
    res.json({ user });
  } else {
    // Envie uma resposta de erro se o usuário não estiver logado
    res.status(401).json({ message: "Não autorizado" });
  }
});
  // Endpoint da lista negra
  app.post("/lista-negra", (req, res) => {
    const { userId } = req.body;
    blacklistedUsers.push(userId);
    res.send(`ID do usuário ${userId} foi colocado na lista negra`);
});
// Inicie o servidor
app.listen(3000, () => {
  console.log("Servidor iniciado na porta 3000");
});

O código acima implementa a autenticação no lado do servidor usando o Express e o middleware express-session. Ele define um endpoint de login e logout simples e um endpoint de perfil protegido que só pode ser acessado por usuários autenticados.

Quando um usuário faz login com um nome de usuário e senha válidos, o ID do usuário é salvo na sessão. Este ID de usuário recupera o objeto do usuário do array de usuários fictícios e o envia de volta para o cliente. Quando o usuário faz logout, sua sessão é destruída.

Uma desvantagem dessa implementação é que a sessão e os usuários na lista negra são armazenados no lado do servidor na memória, o que pode se tornar um problema de escalabilidade à medida que o número de usuários aumenta. Além disso, se o servidor falhar ou for reiniciado, todas as sessões ativas serão perdidas. Para evitar esses problemas, você pode usar um sistema de cache distribuído como o Redis para armazenar as sessões em vez de armazená-las na memória.

Autenticação do lado do servidor: Casos de uso

Vamos revisar os cenários em que a autenticação do lado do servidor usando tokens é geralmente preferível.

Segurança é uma prioridade máxima

Porque o servidor tem controle total sobre a criação e gerenciamento de tokens de sessão, é mais fácil implementar medidas avançadas de segurança, como bloqueio de IP, limitação de taxa e revogação de tokens.

A aplicação requer atualizações em tempo real

Se a sua aplicação requer atualizações em tempo real ou notificações, a autenticação do lado do servidor pode ser mais eficiente, pois o servidor pode enviar atualizações para o cliente com base no ID da sessão.

Escalabilidade é uma preocupação

Na autenticação do lado do servidor, o estado da sessão é armazenado no lado do servidor, o que pode ser escalonado horizontalmente em vários servidores usando ferramentas como o Redis ou o Memcached.

O que é autenticação JWT?

A autenticação JWT (JSON Web Token) é um método de autenticação baseado em token e sem estado. Envolve a geração de um token contendo informações de identidade do usuário, que é então enviado para o cliente para ser armazenado. O cliente envia esse token com cada solicitação ao servidor para autenticar o usuário. Para garantir que o usuário esteja autorizado a acessar o recurso solicitado, o token é verificado no servidor.

Para implementar a autenticação do lado do cliente usando JWT, você precisará emitir um token assinado para o cliente após o login bem-sucedido e armazená-lo no lado do cliente. Você também precisará incluir o token em cada solicitação subsequente para autenticar o usuário.

Vantagens da autenticação JWT (JSON Web Token)

Sem estado (Stateless)

Como o token contém todas as informações necessárias para autenticar o usuário, o servidor não precisa manter nenhum dado de sessão ou consultas de banco de dados. O JWT é um método de autenticação sem estado que pode simplificar a manutenção do servidor e reduzir o uso de recursos.

Escalabilidade

Os Tokens JSON Web permitem dimensionar os recursos do servidor, pois o servidor não precisa manter nenhum dado de estado.

Cross-Domain

O token é autocontido e não requer acesso ao servidor para validação, então o JWT pode ser usado em diferentes domínios.

Desvantagens da autenticação JWT (JSON Web Token)

Tamanho do Token

Na autenticação JWT, o tamanho do token pode ser grande. Isso pode afetar negativamente o desempenho, especialmente se o token for enviado com cada solicitação.

Riscos de Segurança

Se um token for comprometido, um atacante pode se passar pelo usuário e obter acesso a recursos protegidos. Além disso, se o token não for devidamente assinado, um atacante pode modificar os dados contidos no token.

Expiração do Token

Se o token não expirar, ele pode ser usado indefinidamente. No entanto, se o token expirar com muita frequência, pode causar inconveniência aos usuários, que teriam que fazer login com frequência. Equilibrar o tempo de expiração do token é um aspecto crítico a ser considerado.

Autenticação JWT com uma aplicação Node.js

O código abaixo mostra um exemplo de implementação de autenticação JWT usando Node.js e o framework Express:

const express = require("express");
const jwt = require("jsonwebtoken");

const app = express();

// Objeto de usuário fictício para demonstração
const users = [
  { id: 1, username: "john", password: "password" },
  { id: 2, username: "jane", password: "password" },
];

// Chave secreta para assinar e verificar JWTs
const secretKey = "minhachaveprivada";

// Endpoint de login
app.post("/login", (req, res) => {
  const { username, password } = req.body;

  // Encontrar o usuário pelo nome de usuário e senha
  const user = users.find(
    (u) => u.username === username && u.password === password
  );

  if (user) {
    // Criar um token JWT com o ID do usuário como payload
    const token = jwt.sign({ userId: user.id }, secretKey);

    // Enviar o token de volta para o cliente
    res.json({ token });
  } else {
    // Enviar uma resposta de erro se o usuário não for encontrado
    res.status(401).json({ message: "Nome de usuário ou senha inválidos" });
  }
});

// Endpoint protegido
app.get("/perfil", (req, res) => {
  // Obter o cabeçalho de autorização da solicitação
  const authHeader = req.headers.authorization;

  if (authHeader) {
    // Extrair o token JWT do cabeçalho de autorização
    const token = authHeader.split(" ")[1];

    try {
      // Verificar o token JWT com a chave secreta
      const decodedToken = jwt.verify(token, secretKey);

      // Obter o ID do usuário do token decodificado
      const userId = decodedToken.userId;

      // Encontrar o usuário pelo ID
      const user = users.find((u) => u.id === userId);

      // Enviar o objeto de usuário de volta para o cliente
      res.json({ user });
    } catch (error) {
      // Enviar uma resposta de erro se o token for inválido
      res.status(401).json({ message: "Token inválido" });
    }
  } else {
    // Enviar uma resposta de erro se o cabeçalho de autorização não estiver presente
    res.status(401).json({ message: "Não autorizado" });
  }
});

// Iniciar o servidor
app.listen(3000, () => {
  console.log("Servidor iniciado na porta 3000");
});

O código utiliza a biblioteca jsonwebtoken para gerar e verificar Tokens JWT (JSON Web Tokens). Ele fornece dois endpoints, /login e /perfil.

O endpoint /login espera uma solicitação POST com o nome de usuário e a senha de um usuário no corpo da solicitação. Ele encontra o usuário no array de usuários e cria um token JWT com o ID do usuário como payload. Em seguida, envia o token de volta para o cliente como um objeto JSON.

O endpoint /perfil espera uma solicitação GET com um cabeçalho de autorização contendo um token JWT válido. Ele extrai o token do cabeçalho e verifica-o com a chave secreta. Se o token for válido, ele extrai o ID do usuário do payload e encontra o usuário no array de usuários. Em seguida, envia o objeto de usuário de volta para o cliente como um objeto JSON.

JWT: Casos de Uso

Autenticação e autorização

Podemos usar JWTs para transmitir de forma segura dados de autenticação e autorização entre o cliente e o servidor. Ao incluir a identidade e permissões de um usuário em um JWT, um servidor pode verificar se um usuário está autorizado a acessar determinados recursos.

Login único (SSO - Single Sign-On)

Os JWTs são uma ótima escolha para implementar o login único (SSO), onde um usuário faz login em um único aplicativo e pode, em seguida, acessar outros aplicativos sem precisar fazer login novamente. O JWT pode transmitir de forma segura a identidade do usuário e o estado de autenticação entre os aplicativos.

Aplicativos móveis

Onde métodos tradicionais de autenticação baseados em sessão podem não ser viáveis, você pode usar JWTs para autenticar e autorizar usuários em aplicativos móveis. O JWT pode ser armazenado no dispositivo e usado para autenticar o usuário com o servidor em solicitações subsequentes.

Microservices

Você pode usar JWTs para autenticar e autorizar solicitações entre microservices em um sistema distribuído. Cada microserviço pode usar o JWT para verificar se as solicitações vêm de uma fonte confiável e se o usuário está autorizado a acessar o recurso solicitado.

Resumo das diferenças entre Tokens no lado do servidor e JWT (Tokens de Web JSON):

Tokens no Lado do Servidor:

  • Armazenados no lado do servidor.
  • Fáceis de revogar, pois o servidor tem controle sobre eles.
  • Requer armazenamento no lado do servidor.
  • Adequados para aplicativos em tempo real.
  • Atualizações simples.
  • Escaláveis com Redis ou Memcached.
  • Mais seguros, pois são armazenados no servidor.

JWT (Tokens de Web JSON):

  • Armazenados no lado do cliente.
  • Difíceis de revogar, uma vez que são armazenados no lado do cliente.
  • Stateless (sem estado), não requer armazenamento no lado do servidor.
  • Melhores para aplicativos móveis ou de página única (SPA).
  • Melhor adequação para aplicativos que operam em vários domínios ou microserviços.
  • Requer assinatura ou validação distribuída.
  • Mais rápidos e mais fáceis de implementar.

A escolha entre Tokens no lado do servidor e JWT depende das necessidades específicas do seu aplicativo, incluindo requisitos de segurança, escalabilidade e arquitetura.

Conclusão

Em resumo, a autenticação JWT é uma abordagem sem estado que utiliza tokens digitalmente assinados para comunicação segura. Ela oferece integração fácil, compatibilidade entre domínios e recursos adicionais de segurança. No entanto, é importante monitorar o tamanho do token e a revogação.

A autenticação de token no lado do servidor envolve o armazenamento de informações de sessão no servidor. Ela oferece gerenciamento de sessão fácil, invalidação rápida e controle sobre logins simultâneos. No entanto, requer armazenamento no lado do servidor, o que pode apresentar desafios de escalabilidade.

A escolha entre autenticação JWT e autenticação de token no lado do servidor depende do seu caso de uso, necessidades de segurança e requisitos de escalabilidade. JWT é adequado para cenários sem estado e APIs, enquanto tokens no lado do servidor funcionam melhor para autenticação baseada em sessão em aplicativos web.

Sugestões de cursos

Descubra o caminho para se tornar um especialista em programação web. Aprenda HTML, CSS, JavaScript e os principais frameworks nesta jornada emocionante. Com instrutores experientes e materiais práticos, você desenvolverá habilidades práticas para criar sites impressionantes e aplicativos interativos. Impulsione sua carreira na indústria de tecnologia e abra portas para oportunidades de emprego lucrativas. Garanta sua vaga hoje mesmo e inicie sua jornada para se tornar um desenvolvedor web de sucesso.

Método Para Aprender a Programar do Absoluto ZERO com Node.js, React e React Native.

Curso de Node.js, React e React Native

As tecnologias ensinadas no curso são responsáveis por muitas vagas no mercado de trabalho.

Além da alta demanda, os salários vão de R$47.000,00 até R$197.000,00 anuais tendo empresas que possibilitam o trabalho remoto e até vagas Internacionais.

Para que você possa estar apto a preencher uma dessas vagas eu vou te apresentar o passo a passo para você se tornar um verdadeiro expert nessas tecnologias.

O curso te dará o passo a passo de como criar estruturar de um sistema do zero com Node.js, React e React Native.

Saiba mais sobre o curso de Node.js, React e React Native.

AdsTerra, Junte-se ao AdsTerra