How to Integrate Cloudflare Turnstile with Devise in Ruby on Rails 7 (Step-by-Step Guide)

October 21, 2024

In this article, we’ll walk you through integrating Cloudflare Turnstile, a user-friendly CAPTCHA alternative, with Devise in a Ruby on Rails 7 application. This guide ensures your sign-up page is protected from spam and bots while using Rails credentials to manage sensitive keys securely.


Prerequisites

  1. A Cloudflare account with access to the Turnstile service.
  2. Site Key and Secret Key from the Cloudflare Turnstile dashboard.
  3. A working Rails 7 application using Devise for user authentication.

Step 1: Configure Cloudflare Credentials in Rails

  1. Open the Rails credentials editor:

    EDITOR="nano" bin/rails credentials:edit
    
  2. Add your Turnstile Site Key and Secret Key under the cloudflare section:

    cloudflare:
      turnstile_site_key: your_site_key_here
      turnstile_secret_key: your_secret_key_here
    
  3. Save and close the editor.


Step 2: Add the Turnstile Widget to the Sign-Up View

  1. Generate Devise views if you haven’t already:

    rails generate devise:views
    
  2. Open the sign-up form located at:

    app/views/devise/registrations/new.html.erb
    
  3. Add the Turnstile widget to the form:

    <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= devise_error_messages! %>
    
      <div class="field">
        <%= f.label :email %><br />
        <%= f.email_field :email, autofocus: true %>
      </div>
    
      <div class="field">
        <%= f.label :password %><br />
        <%= f.password_field :password, autocomplete: "off" %>
      </div>
    
      <div class="field">
        <%= f.label :password_confirmation %><br />
        <%= f.password_field :password_confirmation, autocomplete: "off" %>
      </div>
    
      <div class="cf-turnstile" data-sitekey="<%= Rails.application.credentials.dig(:cloudflare, :turnstile_site_key) %>"></div>
    
      <div class="actions">
        <%= f.submit "Sign up" %>
      </div>
    <% end %>
    
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
    

Step 3: Customize the Devise Registrations Controller

  1. Create a custom RegistrationsController:

    rails generate controller users/registrations
    
  2. Modify the generated file at app/controllers/users/registrations_controller.rb:

       require 'net/http'
       require 'uri'
       require 'json'
    
       class Users::RegistrationsController < Devise::RegistrationsController
         def create
           turnstile_token = params["cf-turnstile-response"]
    
           unless verify_turnstile(turnstile_token)
             flash[:alert] = "Turnstile verification failed. Please try again."
             redirect_to new_user_registration_path and return
           end
    
           super
         end
    
         private
    
         def verify_turnstile(token)
           uri = URI("https://challenges.cloudflare.com/turnstile/v0/siteverify")
           response = Net::HTTP.post_form(uri, {
             "secret" => Rails.application.credentials.dig(:cloudflare, :turnstile_secret_key),
             "response" => token
           })
    
           result = JSON.parse(response.body)
           result["success"]
         rescue StandardError => e
           Rails.logger.error "Turnstile verification failed: #{e.message}"
           false
         end
       end
    
  3. Update the routes to use the custom RegistrationsController. In config/routes.rb, modify the Devise setup:

    devise_for :users, controllers: {
      registrations: 'users/registrations'
    }
    

Step 4: Restart the Rails Server and Test the Integration

  1. Start your Rails server:

    rails server
    
  2. Visit the sign-up page at /users/sign_up. Ensure that:

    • The Turnstile widget is visible.
    • Users cannot sign up unless they complete the Turnstile challenge.
    • Invalid challenges display the appropriate error message.

Conclusion

Integrating Cloudflare Turnstile with Devise in Rails 7 adds a seamless layer of security to your sign-up page. By using Rails credentials, you keep sensitive keys secure, enhancing both application safety and maintainability.

This approach ensures a bot-free user experience while leveraging the simplicity of Devise for authentication. If you encounter any issues or need further customization, refer to the Cloudflare Turnstile documentation or check your Rails logs for errors.


Enjoyed the guide? Share it with other Rails developers looking to secure their applications with Cloudflare Turnstile!


Happy coding! 🎉

Follow Me