Tokens e Segurança

Introdução aos Tokens

Após implementar a autenticação básica, é fundamental garantir a segurança e a eficiência na gestão das sessões dos usuários. Para isso, utilizamos tokens, que são chaves de acesso que permitem aos usuários interagirem com a aplicação de maneira segura.

O que é um Token?

Um token é uma sequência de caracteres que atua como uma chave de acesso. Ele deve ser único, seguro e difícil de adivinhar, podendo ser gerado de forma aleatória ou por meio de algoritmos de hash. Os tokens são amplamente utilizados para:

  • Autorizar Acessos: Garantir que apenas usuários autenticados possam acessar certas rotas ou funcionalidades.

  • Manter Sessões: Manter o usuário autenticado durante a navegação na aplicação sem a necessidade de reautenticação constante.

  • Realizar Transações Seguras: Autorizar operações sensíveis, como transações financeiras.

Exemplos de Uso de Tokens

  • Aplicativos Bancários: Geram tokens para autorizar transações seguras.

  • Redes Sociais: Mantêm o usuário autenticado durante a navegação.

  • Autenticação em Duas Etapas (2FA): Geram códigos temporários para confirmar logins.

  • Carteirinha de Estudante: Utilizam tokens para acesso em universidades ou escolas.

JWT (JSON Web Token)

O JWT (JSON Web Token) é um padrão amplamente utilizado para a autenticação em serviços web. Ele permite o envio seguro de informações entre as partes como um objeto JSON, que pode ser verificado e confiável graças à sua assinatura digital.

Estrutura do JWT

Um JWT é composto por três partes, separadas por pontos (.):

  1. Header (Cabeçalho): Contém as informações sobre o tipo de token e o algoritmo de criptografia utilizado.

  2. Payload (Carga): Contém as declarações (claims) que são os dados que você deseja transmitir, como informações do usuário.

  3. Signature (Assinatura): Garante que o token não foi alterado. É gerada a partir do header, payload e uma chave secreta.

Exemplo de JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VybmFtZSI6ImpvaG5kb2UiLCJpYXQiOjE2MTc5MjM5MDJ9
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Recursos Úteis

  • JWT.io Debugger: Ferramenta online para decodificar, verificar e depurar tokens JWT. Acessar JWT.io

Implementação do JWT

Vamos implementar a autenticação utilizando JWT no nosso projeto Node.js, garantindo que apenas usuários autenticados possam acessar rotas protegidas.

1. Instalação da Biblioteca jsonwebtoken

Primeiramente, instale a biblioteca jsonwebtoken, que nos permitirá gerar e verificar tokens JWT.

npm install jsonwebtoken

2. Configuração das Chaves Secretas

Crie um arquivo de configuração para armazenar a chave secreta utilizada para assinar os tokens. Essa chave NUNCA DEVE estar exposta no código. Utilizaremos variáveis de ambiente para mantê-la segura.

Passos:

  1. Instale a biblioteca dotenv para gerenciar variáveis de ambiente:

    npm install dotenv
  2. Crie um arquivo .env na raiz do projeto e adicione a chave secreta:

    SECRET_KEY=codelabMelhorGrupo
  3. Atualize o arquivo index.js para carregar as variáveis de ambiente:

    // index.js
    
    require('dotenv').config();
    const express = require('express');
    const app = express();
    const port = 3000;
    
    const produtosRouter = require('./routes/produtos');
    const authRoutes = require('./routes/authRoutes');
    
    app.use(express.json());
    
    app.use('/produtos', produtosRouter);
    app.use('/auth', authRoutes);
    
    app.listen(port, () => {
        console.log(`Servidor rodando em http://localhost:${port}`);
    });

3. Geração e Validação de Tokens

Crie um serviço para gerenciar a geração e a validação dos tokens JWT.

// services/tokenService.js

const jwt = require('jsonwebtoken');

const SECRET_KEY = process.env.SECRET_KEY;

// Função para gerar token
exports.generateToken = (payload) => {
    return jwt.sign(payload, SECRET_KEY, { expiresIn: '1h' });
};

// Função para verificar token
exports.verifyToken = (token) => {
    return jwt.verify(token, SECRET_KEY);
};

4. Atualização do Service de Autenticação

Atualize o serviço de autenticação para utilizar o token JWT ao realizar o login.

// services/authService.js

const userRepository = require('../repositories/userRepository');
const hash = require('../utils/hash');
const tokenService = require('../services/tokenService');

exports.cadastrarUsuario = async (dados) => {
    const { nome, email, telefone, cpf, senha } = dados;

    // Verifica se o usuário já existe
    const existeUsuario = await userRepository.findByEmail(email);
    if (existeUsuario) {
        throw new Error('Este email já está cadastrado');
    }

    // Gera o hash da senha
    const senhaHash = hash.generate(senha);

    // Cria o novo usuário
    const novoUsuario = await userRepository.create({
        nome,
        email,
        telefone,
        cpf,
        senha: senhaHash,
    });

    return novoUsuario;
};

exports.loginUsuario = async (dados) => {
    const { email, senha } = dados;

    // Busca o usuário pelo email
    const usuario = await userRepository.findByEmail(email);
    if (!usuario) {
        throw new Error('Usuário/senha incorretos');
    }

    // Compara a senha fornecida com o hash armazenado
    const senhaValida = hash.compare(senha, usuario.senha);
    if (!senhaValida) {
        throw new Error('Usuário/senha incorretos');
    }

    // Gera o token JWT
    const tokenJWT = tokenService.generateToken({ id: usuario.id, email: usuario.email });
    return tokenJWT;
};

5. Atualização do Controller de Autenticação

Atualize o controller para retornar o token JWT ao realizar o login.

// controllers/authController.js

const authService = require('../services/authService');

exports.cadastro = async (req, res) => {
    try {
        const usuario = await authService.cadastrarUsuario(req.body);
        res.status(201).json(usuario);
    } catch (error) {
        res.status(400).json({ mensagem: error.message });
    }
};

exports.login = async (req, res) => {
    try {
        const token = await authService.loginUsuario(req.body);
        res.status(200).json({ token });
    } catch (error) {
        res.status(400).json({ mensagem: error.message });
    }
};

6. Implementação do Middleware de Autenticação

Crie um middleware para proteger as rotas, verificando se a requisição possui um token válido.

// middleware/authenticate.js

const tokenService = require('../services/tokenService');

function authenticate(req, res, next) {
    const authHeader = req.headers.authorization;

    if (!authHeader) {
        return res.status(403).json({ mensagem: 'Sem autorização' });
    }

    const token = authHeader.split(' ')[1];
    if (!token) {
        return res.status(403).json({ mensagem: 'Sem autorização' });
    }

    try {
        const decoded = tokenService.verifyToken(token);
        req.user = decoded; // Adiciona os dados do usuário à requisição
        next();
    } catch (err) {
        return res.status(403).json({ mensagem: 'Token inválido' });
    }
}

module.exports = authenticate;

7. Protegendo Rotas com Middleware

Utilize o middleware de autenticação para proteger rotas que requerem usuário autenticado.

// routes/produtos.js

const express = require('express');
const router = express.Router();
const prisma = require('../prismaClient');
const authenticate = require('../middleware/authenticate');

// Rota para obter todos os produtos (pública)
router.get('/', async (req, res) => {
    try {
        const produtos = await prisma.produto.findMany();
        res.json(produtos);
    } catch (error) {
        console.error(error);
        res.status(500).send('Erro no servidor');
    }
});

// Rota para adicionar um novo produto (protegida)
router.post('/', authenticate, async (req, res) => {
    const { nome, quantidade, preco } = req.body;
    try {
        const novoProduto = await prisma.produto.create({
            data: {
                nome,
                quantidade,
                preco,
            },
        });
        res.json(novoProduto);
    } catch (error) {
        console.error(error);
        res.status(500).send('Erro no servidor');
    }
});

module.exports = router;

Fluxo de Autenticação com JWT

  1. Cadastro de Usuário:

    • O usuário envia seus dados para o endpoint /auth/cadastro.

    • A senha é criptografada utilizando bcrypt.

    • Os dados do usuário são armazenados no banco de dados sem gerar um token.

  2. Login:

    • O usuário envia suas credenciais para o endpoint /auth/login.

    • As credenciais são verificadas e, se válidas, um token JWT é gerado e retornado.

    • O token deve ser armazenado no cliente (por exemplo, no localStorage) e enviado nas requisições subsequentes nas headers para acessar rotas protegidas.

Exemplo Prático de Cadastro e Login

Cadastro de Usuário:

  • Endpoint: POST /auth/cadastro

  • Corpo da Requisição:

    {
        "nome": "João Silva",
        "email": "joao.silva@example.com",
        "telefone": "11999999999",
        "cpf": "12345678901",
        "senha": "senhaSegura123"
    }
  • Resposta:

    {
        "id": 1,
        "nome": "João Silva",
        "email": "joao.silva@example.com",
        "telefone": "11999999999",
        "cpf": "12345678901"
    }

Login de Usuário:

  • Endpoint: POST /auth/login

  • Corpo da Requisição:

    {
        "email": "joao.silva@example.com",
        "senha": "senhaSegura123"
    }
  • Resposta:

    {
        "token": "seu_jwt_token_aqui"
    }

Last updated

Was this helpful?