# Como Integrar APIs de CPF em um Sistema Ruby on Rails com Devise

> Aprenda a integrar validação de CPF via API em um sistema Ruby on Rails com Devise, adicionando verificação de identidade ao fluxo de autenticação.

**Publicado:** 08/08/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/integrar-apis-cpf-ruby-rails-devise

---


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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
# 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.

```ruby
<%# 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](https://owasp.org/www-project-cheat-sheets/cheatsheets/Secrets_Management_Cheat_Sheet.html) 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](https://www.gov.br/anpd) recomenda o princípio da minimização: armazene apenas o que é estritamente necessário para cada finalidade.

### Leia também

- [Diferença entre validação de CPF e consulta de CPF: quando usar cada uma](https://cpfhub.io/blog/diferenca-entre-validacao-de-cpf-e-consulta-de-cpf-quando-usar-cada-uma)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [Onboarding digital em fintechs: como validar CPF em menos de 30 segundos](https://cpfhub.io/blog/onboarding-digital-em-fintechs-como-validar-cpf-em-menos-de-30-segundos)
- [KYC no Brasil: quais setores são obrigados a validar CPF por lei](https://cpfhub.io/blog/kyc-no-brasil-quais-setores-sao-obrigados-a-validar-cpf-por-lei)

---

## 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](https://www.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.

