Para integrar validação de CPF via API em um sistema Ruby on Rails com Devise, adicione o campo cpf ao model de usuário, processe a verificação em um ActiveJob assíncrono usando Faraday para chamar GET https://api.cpfhub.io/cpf/{CPF} com o header x-api-key, e condicione o acesso a funcionalidades sensíveis ao status cpf_verificado. O registro ocorre sem bloqueio enquanto a verificação acontece em background — garantindo boa experiência ao usuário e identidade validada antes de operações críticas.
Introdução
O Devise é a gem de autenticação mais utilizada em aplicações Rails, gerenciando registro, login e recuperação de senha. Integrar a validação de CPF via API ao fluxo do Devise adiciona uma camada de verificação de identidade que fortalece a segurança do cadastro, sem adicionar fricção perceptível ao usuário no momento do registro.
Adicionando CPF ao model de usuário
Primeiro, adicione o campo de CPF ao model de usuário e configure as validações.
# db/migrate/XXXXXX_add_cpf_to_users.rb
class AddCpfToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :cpf, :string
add_column :users, :cpf_verificado, :boolean, default: false
add_column :users, :nome_cpf, :string
add_column :users, :genero_cpf, :string
add_column :users, :data_nascimento_cpf, :date
add_index :users, :cpf, unique: true
end
end
# app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates :cpf, presence: true, uniqueness: true
validate :cpf_formato_valido
validate :cpf_algoritmo_valido
before_save :limpar_cpf
private
def limpar_cpf
self.cpf = cpf.gsub(/\D/, "") if cpf.present?
end
def cpf_formato_valido
return if cpf.blank?
cpf_limpo = cpf.gsub(/\D/, "")
unless cpf_limpo.match?(/\A\d{11}\z/)
errors.add(:cpf, "deve conter 11 digitos")
end
end
def cpf_algoritmo_valido
return if cpf.blank?
cpf_limpo = cpf.gsub(/\D/, "")
return if cpf_limpo.length != 11
if cpf_limpo.chars.uniq.size == 1
errors.add(:cpf, "nao pode ter todos os digitos iguais")
return
end
unless CpfValidator.digitos_validos?(cpf_limpo)
errors.add(:cpf, "possui digitos verificadores invalidos")
end
end
end
| Campo | Tipo | Descrição |
|---|---|---|
| cpf | string | CPF do usuário (11 dígitos, único) |
| cpf_verificado | boolean | Se o CPF foi verificado via API |
| nome_cpf | string | Nome retornado pela API |
| genero_cpf | string | Gênero retornado pela API |
| data_nascimento_cpf | date | Data de nascimento retornada pela API |
Customizando o controller de registro do Devise
Para permitir o campo de CPF no registro, customize o controller do Devise.
# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
def create
super do |user|
if user.persisted?
VerificarCpfJob.perform_later(user.id)
end
end
end
private
def configure_sign_up_params
devise_parameter_sanitizer.permit(
:sign_up,
keys: [:cpf, :nome]
)
end
def configure_account_update_params
devise_parameter_sanitizer.permit(
:account_update,
keys: [:cpf, :nome]
)
end
end
# config/routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: {
registrations: "users/registrations"
}
end
Job de verificação de CPF via API
Após o registro, um job verifica o CPF via API e atualiza o perfil do usuário.
# app/jobs/verificar_cpf_job.rb
class VerificarCpfJob < ApplicationJob
queue_as :default
retry_on Faraday::TimeoutError, wait: :exponentially_longer, attempts: 3
def perform(user_id)
user = User.find(user_id)
return if user.cpf_verificado?
cliente = Faraday.new(url: "https://api.cpfhub.io") do |conn|
conn.headers["x-api-key"] = ENV["CPFHUB_API_KEY"]
conn.options.timeout = 10
conn.adapter Faraday.default_adapter
end
resposta = cliente.get("/cpf/#{user.cpf}")
resultado = JSON.parse(resposta.body)
if resultado["success"]
dados = resultado["data"]
user.update!(
cpf_verificado: true,
nome_cpf: dados["name"],
genero_cpf: dados["gender"],
data_nascimento_cpf: dados["birthDate"]
)
UserMailer.cpf_verificado(user).deliver_later
Rails.logger.info("[VerificarCpfJob] CPF #{user.cpf} verificado")
else
Rails.logger.warn(
"[VerificarCpfJob] CPF #{user.cpf} nao encontrado na base"
)
end
end
end
# app/services/cpf_validator.rb
class CpfValidator
def self.digitos_validos?(cpf)
numeros = cpf.chars.map(&:to_i)
# Primeiro dígito verificador
soma = (0..8).sum { |i| numeros[i] * (10 - i) }
resto = (soma * 10) % 11
resto = 0 if resto == 10
return false unless resto == numeros[9]
# Segundo dígito verificador
soma = (0..9).sum { |i| numeros[i] * (11 - i) }
resto = (soma * 10) % 11
resto = 0 if resto == 10
resto == numeros[10]
end
end
Middleware de verificação de CPF
Crie um middleware que exige CPF verificado para acessar determinadas funcionalidades.
# app/controllers/concerns/cpf_verificado_concern.rb
module CpfVerificadoConcern
extend ActiveSupport::Concern
included do
before_action :exigir_cpf_verificado, if: :usuario_logado?
end
private
def exigir_cpf_verificado
return unless acao_requer_cpf_verificado?
unless current_user.cpf_verificado?
if request.format.json?
render json: {
erro: "CPF nao verificado",
mensagem: "Aguarde a verificacao do seu CPF para acessar esta funcionalidade"
}, status: :forbidden
else
redirect_to verificacao_pendente_path,
alert: "Seu CPF ainda esta sendo verificado."
end
end
end
def acao_requer_cpf_verificado?
self.class.const_defined?(:ACOES_REQUEREM_CPF) &&
self.class::ACOES_REQUEREM_CPF.include?(action_name.to_sym)
end
def usuario_logado?
user_signed_in?
end
end
# app/controllers/pedidos_controller.rb
class PedidosController < ApplicationController
include CpfVerificadoConcern
ACOES_REQUEREM_CPF = %i[create update].freeze
def create
# Apenas usuários com CPF verificado chegam aqui
end
end
| Ação | Requer CPF Verificado | Motivo |
|---|---|---|
| Navegar no site | Não | Acesso público |
| Criar conta | Não | CPF será verificado após registro |
| Realizar compra | Sim | Segurança da transação |
| Alterar dados | Sim | Proteção contra fraude |
| Ver histórico | Não | Dados do próprio usuário |
Formulário de registro com CPF
Customize a view de registro do Devise para incluir o campo de CPF com máscara.
<%# app/views/devise/registrations/new.html.erb %>
<%= form_for(resource, as: resource_name,
url: registration_path(resource_name)) do |f| %>
<div class="campo">
<%= f.label :nome, "Nome completo" %>
<%= f.text_field :nome, required: true, autofocus: true %>
</div>
<div class="campo">
<%= f.label :email %>
<%= f.email_field :email, required: true %>
</div>
<div class="campo">
<%= f.label :cpf, "CPF" %>
<%= f.text_field :cpf,
required: true,
inputmode: "numeric",
maxlength: 14,
placeholder: "000.000.000-00",
data: { controller: "mascara-cpf" } %>
<span class="info">
Seu CPF sera verificado para seguranca da sua conta.
</span>
</div>
<div class="campo">
<%= f.label :password, "Senha" %>
<%= f.password_field :password, required: true,
minlength: Devise.password_length.min %>
</div>
<div class="acoes">
<%= f.submit "Criar conta" %>
</div>
<% end %>
Perguntas frequentes
Por que usar um ActiveJob assíncrono para verificar o CPF em vez de fazer a chamada na hora do registro?
A chamada à API tem latência de aproximadamente 900ms e pode ocasionalmente exceder esse tempo. Fazer a verificação em background evita que o usuário espere durante o registro e protege o fluxo de cadastro contra falhas de rede ou timeout da API. O acesso a funcionalidades sensíveis fica condicionado ao campo cpf_verificado, garantindo segurança sem prejudicar a experiência.
Como armazenar a chave de API com segurança em uma aplicação Rails?
Armazene a chave em uma variável de ambiente (ENV["CPFHUB_API_KEY"]) e nunca a coloque diretamente no código-fonte. Em produção, utilize ferramentas como Rails credentials (rails credentials:edit), secrets managers do provedor de nuvem (AWS Secrets Manager, Google Secret Manager) ou serviços como Doppler. Consulte as diretrizes da OWASP sobre gestão de segredos para boas práticas adicionais.
O que acontece se o CPF não for encontrado na API após o registro?
O job registra um aviso no log e o campo cpf_verificado permanece false. O usuário consegue fazer login normalmente, mas é bloqueado nas ações que requerem CPF verificado (como realizar compras ou alterar dados sensíveis). Você pode configurar um retry com backoff exponencial ou acionar um fluxo de revisão manual para esses casos.
Como garantir conformidade com a LGPD ao armazenar dados retornados pela API?
Armazene apenas os campos necessários para as finalidades declaradas (nome para exibição, data de nascimento para verificação de idade). Implemente controle de acesso aos registros contendo dados do CPF, defina período de retenção e documente a base legal para o tratamento. A ANPD recomenda o princípio da minimização: armazene apenas o que é estritamente necessário para cada finalidade.
Conclusão
Integrar a validação de CPF via API ao fluxo de autenticação do Devise fortalece a segurança do cadastro sem adicionar fricção excessiva ao usuário. O registro ocorre normalmente enquanto a verificação acontece em background. Funcionalidades sensíveis ficam condicionadas à verificação, criando uma camada adicional de proteção contra fraudes e cadastros fraudulentos.
Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e adicione verificação de identidade ao seu sistema Rails com Devise ainda hoje.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



