Introdução ao JWT em Ruby on Rails

O JSON Web Tokens é uma maneira segura e compacta de transmitir informações entre partes como um objeto JSON. Eles são comumente utilizados para autenticação e autorização em aplicativos web e móveis. Neste artigo, exploraremos como implementar JWT em uma aplicação Ruby on Rails, detalhando suas partes, vantagens e fornecendo…

O JSON Web Tokens é uma maneira segura e compacta de transmitir informações entre partes como um objeto JSON. Eles são comumente utilizados para autenticação e autorização em aplicativos web e móveis. Neste artigo, exploraremos como implementar JWT em uma aplicação Ruby on Rails, detalhando suas partes, vantagens e fornecendo um exemplo prático.

O que é JWT?

O JSON Web Tokens é um padrão aberto que define uma maneira compacta e autocontida de transmitir informações entre partes como um objeto JSON. Ele consiste em três partes principais: Header, Payload e Signature. Estas partes são codificadas em Base64Url e concatenadas por pontos (‘.’).

Estrutura do JWT

O JWT tem a seguinte estrutura:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2MTk3NTg4MDB9.tC9omCmt5rmn-ZzA5DUnI8bkL9GpZZKnPipKv2vHBEw

Header

Contém o tipo de token (JWT) e o algoritmo de criptografia usado.

{
  "alg": "HS256",
  "typ": "JWT"
}
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Contém as declarações (claims), que são afirmações sobre uma entidade (geralmente o usuário) e metadados adicionais.

{  
  "user_id": 1,
  "exp": 1619758800
}
eyJ1c2VyX2lkIjoxLCJleHAiOjE2MTk3NTg4MDB9

Signature

É usada para verificar se o token foi alterado durante a transmissão, garantindo a integridade dos dados.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)
tC9omCmt5rmn-ZzA5DUnI8bkL9GpZZKnPipKv2vHBEw

Por que usar JWT?

JWT oferece várias vantagens para a autenticação e troca de informações em aplicações web:

  1. Segurança: Os tokens JWT podem ser assinados digitalmente para verificar sua autenticidade e garantir que não tenham sido alterados.
  2. Compacto e Eficiente: Devido ao seu formato compacto em JSON, JWT é fácil de transmitir via URL, parâmetros POST ou dentro de cabeçalhos HTTP.
  3. Autocontido: Todas as informações necessárias sobre o usuário podem ser armazenadas no token JWT, eliminando a necessidade de consultar o banco de dados a cada requisição.

Exemplo Prático com Ruby on Rails

Vamos criar um exemplo básico de como implementar JWT em uma aplicação Ruby on Rails para autenticação de usuários.

1. Configuração do Projeto

Primeiro, crie um novo projeto Rails:

rails new jwt_example
cd jwt_example

2. Adicionando Dependências

Adicione as gems jwt e bcrypt ao seu Gemfile e execute bundle install para instalar:

gem 'jwt'
gem 'bcrypt', '~> 3.1.7'

3. Criando um Serviço JWT

Crie um serviço para gerar e validar tokens JWT. Crie um arquivo app/services/json_web_token.rb:

require 'jwt'

class JsonWebToken
  SECRET_KEY = Rails.application.secrets.secret_key_base.to_s

  def self.encode(payload, exp = 24.hours.from_now)
    payload[:exp] = exp.to_i
    JWT.encode(payload, SECRET_KEY)
  end

  def self.decode(token)
    decoded = JWT.decode(token, SECRET_KEY)[0]
    HashWithIndifferentAccess.new decoded
  rescue JWT::DecodeError
    nil
  end
end

O serviço JsonWebToken encapsula a lógica para gerar e decodificar tokens JWT (JSON Web Token). A constante SECRET_KEY armazena a chave secreta usada para assinar os tokens, obtida da configuração da aplicação Rails (Rails.application.secrets.secret_key_base), garantindo que seja única e segura.

O método self.encode recebe um payload (dados do usuário) e um tempo de expiração (exp). Ele adiciona a expiração ao payload, codifica o payload usando a chave secreta e retorna o token JWT.

Por outro lado, o método self.decode recebe um token JWT, decodifica o token usando a chave secreta e retorna o payload. Se a decodificação falhar, seja porque o token está inválido ou expirado, ele retorna nil.

4. Controlador de Autenticação

Crie um controlador para gerenciar a autenticação dos usuários. Por exemplo, app/controllers/auth_controller.rb:

class AuthController < ApplicationController
  before_action :authorize_request, except: :login

  def login
    user = User.find_by(email: params[:email])
    if user&.authenticate(params[:password])
      token = JsonWebToken.encode(user_id: user.id)
      render json: { token: token }, status: :ok
    else
      render json: { error: 'Invalid email or password' }, status: :unauthorized
    end
  end

  private

  def authorize_request
    header = request.headers['Authorization']
    header = header.split(' ').last if header
    begin
      @decoded = JsonWebToken.decode(header)
      @current_user = User.find(@decoded[:user_id])
    rescue ActiveRecord::RecordNotFound => e
      render json: { errors: e.message }, status: :unauthorized
    rescue JWT::DecodeError => e
      render json: { errors: e.message }, status: :unauthorized
    end
  end
end

O AuthController gerencia as operações de login e autorização dos usuários. O método login recebe o email e a senha do usuário. Se as credenciais estiverem corretas, ele gera um token JWT e o retorna no corpo da resposta. Caso as credenciais estejam incorretas, é retornada uma mensagem de erro e status 401 Unauthorized.

O método authorize_request é um filtro de antes da ação (before_action) que verifica se a solicitação contém um token JWT válido. Ele obtém o token do cabeçalho da solicitação, decodifica o token e define o usuário atual (@current_user) com base no user_id presente no token. Se o token for inválido ou o usuário não for encontrado, retorna uma mensagem de erro e status 401 Unauthorized.

5. Configuração de Rotas

Defina as rotas para a autenticação em config/routes.rb:

Rails.application.routes.draw do
  post 'auth/login', to: 'auth#login'
end

6. Modelo de Usuário

Crie um modelo de usuário com as colunas necessárias para autenticação:

rails generate model User email:string password_digest:string
rails db:migrate

Certifique-se de ter um modelo de usuário configurado corretamente.

class User < ApplicationRecord
  has_secure_password
end

A macro has_secure_password adiciona funcionalidades para armazenar uma senha segura. Ela exige que a coluna password_digest esteja presente no modelo.

7. Criando um Usuário

Crie um usuário diretamente via console Rails para testar a implementação de autenticação com JWT:

rails console

No console, execute:

User.create(email: '[email protected]', password: 'password', password_confirmation: 'password')

8. Testando a Implementação

Você pode testar a implementação usando ferramentas como Postman ou curl.

Testando com Postman

  1. Realizar Login
  • Método: POST
  • URL: http://localhost:3000/auth/login
  • Body (raw, JSON):
    json { "email": "[email protected]", "password": "password" }
  • Resposta esperada:
    json { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2MTk3NTg4MDB9.tC9omCmt5rmn-ZzA5DUnI8bkL9GpZZKnPipKv2vHBEw" }
  1. Autorização de Requisição
  • Método: GET
  • URL: http://localhost:3000/some_protected_route
  • Headers:
    • Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2MTk3NTg4MDB9.tC9omCmt5rmn-ZzA5DUnI8bkL9GpZZKnPipKv2vHBEw

Testando com curl

Realizar Login

    curl -X POST -H "Content-Type: application/json" -d '{"email":"[email protected]","password":"password"}' http://localhost:3000/auth/login

    Autorização de Requisição

      curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE2MTk3NTg4MDB9.tC9omCmt5rmn-ZzA5DUnI8bkL9GpZZKnPipKv2vHBEw" http://localhost:3000/some_protected_route

      Conclusão

      Neste artigo, exploramos o conceito de JWT, suas partes (Header, Payload, Signature), vantagens de uso e como implementar JWT em uma aplicação Ruby on Rails para autenticação de usuários. Com JWT, podemos criar sistemas seguros e eficientes que permitem a troca de informações de maneira segura e escalável.

      Referências

      0 Comentário