ProductPromotion
Logo

Elixir

made by https://0x3d.site

GitHub - jsonmaur/phoenix-turnstile: Use Cloudflare Turnstile in Phoenix
Use Cloudflare Turnstile in Phoenix. Contribute to jsonmaur/phoenix-turnstile development by creating an account on GitHub.
Visit Site

GitHub - jsonmaur/phoenix-turnstile: Use Cloudflare Turnstile in Phoenix

GitHub - jsonmaur/phoenix-turnstile: Use Cloudflare Turnstile in Phoenix

Phoenix Turnstile

Phoenix components and helpers for using CAPTCHAs with Cloudflare Turnstile. Before getting started, log into the Cloudflare dashboard and visit the Turnstile tab. Add a new site with your domain name (no need to add localhost if using the default test keys), and take note of your site key and secret key. You'll need these values later.

Installation

def deps do
  [
    {:phoenix_turnstile, "~> 1.0"}
  ]
end

Now add the site key and secret key to your environment variables, and configure them in config/runtime.exs:

config :phoenix_turnstile,
  site_key: System.fetch_env!("TURNSTILE_SITE_KEY"),
  secret_key: System.fetch_env!("TURNSTILE_SECRET_KEY")

You don't need to add a site key or secret key for dev/test environments. This library will use the Turnstile test keys by default.

Getting Started

To use CAPTCHAs in a LiveView app, start out by adding the script component in your root layout:

<head>
  <!-- ... -->

  <Turnstile.script />
</head>

Next, install the hook in app.js or wherever your live socket is being defined (make sure you're setting NODE_PATH in your esbuild config and including the deps folder):

import { TurnstileHook } from "phoenix_turnstile"

const liveSocket = new LiveSocket("/live", Socket, {
  /* ... */
  hooks: {
    Turnstile: TurnstileHook
  }
})

Now you can use the Turnstile widget component in any of your forms. For example:

<.form for={@form} phx-submit="submit">
  <Turnstile.widget theme="light" />

  <button type="submit">Submit</button>
</.form>

To customize the widget, pass any of the render parameters specificed here (without the data- prefix).

Multiple Widgets

If you want to have multiple widgets on the same page, pass a unique ID to Turnstile.widget/1, Turnstile.refresh/1, and Turnstile.remove/1.

Verification

The widget by itself won't actually complete the verification. It works by generating a token which gets injected into your form as a hidden input named cf-turnstile-response. The token needs to be sent to the Cloudflare API for final verification before continuing with the form submission. This should be done in your submit event using Turnstile.verify/2:

def handle_event("submit", values, socket) do
  case Turnstile.verify(values) do
    {:ok, _} ->
      # Verification passed!

      {:noreply, socket}

    {:error, _} ->
      socket =
        socket
        |> put_flash(:error, "Please try submitting again")
        |> Turnstile.refresh()

      {:noreply, socket}
  end
end

To be extra sure the user is not a robot, you also have the option of passing their IP address to the verification API. This step is optional. To get the user's IP address in LiveView, add :peer_data to the connect info for your socket in endpoint.ex:

socket "/live", Phoenix.LiveView.Socket,
  websocket: [
    connect_info: [:peer_data, ...]
  ]

and pass it as the second argument to Turnstile.verify/2:

def mount(_params, session, socket) do
  remote_ip = get_connect_info(socket, :peer_data).address
  {:ok, assign(socket, :remote_ip, remote_ip)}
end

def handle_event("submit", values, socket) do
  case Turnstile.verify(values, socket.assigns.remote_ip) do
    # ...
  end
end

Events

The Turnstile widget supports the following events:

  • :success - When the challenge was successfully completed
  • :error - When there was an error (like a network error or the challenge failed)
  • :expired - When the challenge token expires and was not automatically reset
  • :beforeInteractive - Before the challenge enters interactive mode
  • :afterInteractive - After the challenge has left interactive mode
  • :unsupported - When a given client/browser is not supported by Turnstile
  • :timeout - When the challenge expires (after 5 minutes)

These can be useful for doing things like disabling the submit button until the challenge successfully completes, or refreshing the widget if it fails. To handle an event, add it to the events attribute and create a Turnstile event handler in the LiveView:

<Turnstile.widget events={[:success]} />
handle_event("turnstile:success", _params, socket) do
  # ...

  {:noreply, socket}
end

Without LiveView

Turnstile.script/1 and Turnstile.widget/1 both rely on client hooks, and should work in non-LiveView pages as long as app.js is opening a live socket (which it should by default). Simply call Turnstile.verify/2 in the controller:

def create(conn, params) do
  case Turnstile.verify(params, conn.remote_ip) do
    {:ok, _} ->
      # Verification passed!

      redirect(conn, to: ~p"/success")

    {:error, _} ->
      conn
      |> put_flash(:error, "Please try submitting again")
      |> redirect(to: ~p"/new")
  end
end

If your page doesn't open a live socket or your're not using HEEx, you can still run Turnstile verifications by building your own client-side widget following the documentation and using Turnstile.site_key/0 to get your site key in the template:

def new(conn, _params) do
  conn
  |> assign(:site_key, Turnstile.site_key())
  |> render("new.html")
end
<form action="/create" method="POST">
  <!-- ... -->

  <div class="cf-turnstile" data-sitekey="<%= @site_key %>"></div>
  <button type="submit">Submit</button>
</form>

Content Security Policies

If your site uses a content security policy, you'll need to add https://challenges.cloudflare.com to your script-src and frame-src directives. You can also add attributes to the script component such as nonce, and they will be passed through to the script tag:

<head>
  <!-- ... -->

  <Turnstile.script nonce={@script_src_nonce} />
</head>

Writing Tests

When testing forms that use Turnstile verification, you may or may not want to call the live API.

Although we use the test keys by default, you should consider using mocks during testing. An excellent library to consider is mox. Phoenix Turnstile exposes Turnstile.Behavior which makes writing tests much easier.

To start using Mox with Phoenix Turnstile, add this to your test/test_helper.ex:

Mox.defmock(TurnstileMock, for: Turnstile.Behaviour)

Then in your config/test.exs:

config :phoenix_turnstile, adapter: TurnstileMock

To make sure you're using TurnstileMock during testing, use the adapter from the config rather than using Turnstile directly:

@turnstile Application.compile_env(:phoenix_turnstile, :adapter, Turnstile)

def handle_event("submit", values, socket) do
  case @turnstile.verify(values) do
    {:ok, _} ->
      # Verification passed!

      {:noreply, socket}

    {:error, _} ->
      socket =
        socket
        |> put_flash(:error, "Please try submitting again")
        |> @turnstile.refresh()

      {:noreply, socket}
  end
end

Now you can easily mock or stub any Turnstile function in your tests and they won't make any real API calls:

import Mox

setup do
  stub(TurnstileMock, :refresh, fn socket -> socket end)
  stub(TurnstileMock, :verify, fn _values, _remoteip -> {:ok, %{}} end)
end

Articles
to learn more about the elixir concepts.

Resources
which are currently available to browse on.

mail [email protected] to add your project or resources here 🔥.

FAQ's
to know more about the topic.

mail [email protected] to add your project or resources here 🔥.

Queries
or most google FAQ's about Elixir.

mail [email protected] to add more queries here 🔍.

More Sites
to check out once you're finished browsing here.

0x3d
https://www.0x3d.site/
0x3d is designed for aggregating information.
NodeJS
https://nodejs.0x3d.site/
NodeJS Online Directory
Cross Platform
https://cross-platform.0x3d.site/
Cross Platform Online Directory
Open Source
https://open-source.0x3d.site/
Open Source Online Directory
Analytics
https://analytics.0x3d.site/
Analytics Online Directory
JavaScript
https://javascript.0x3d.site/
JavaScript Online Directory
GoLang
https://golang.0x3d.site/
GoLang Online Directory
Python
https://python.0x3d.site/
Python Online Directory
Swift
https://swift.0x3d.site/
Swift Online Directory
Rust
https://rust.0x3d.site/
Rust Online Directory
Scala
https://scala.0x3d.site/
Scala Online Directory
Ruby
https://ruby.0x3d.site/
Ruby Online Directory
Clojure
https://clojure.0x3d.site/
Clojure Online Directory
Elixir
https://elixir.0x3d.site/
Elixir Online Directory
Elm
https://elm.0x3d.site/
Elm Online Directory
Lua
https://lua.0x3d.site/
Lua Online Directory
C Programming
https://c-programming.0x3d.site/
C Programming Online Directory
C++ Programming
https://cpp-programming.0x3d.site/
C++ Programming Online Directory
R Programming
https://r-programming.0x3d.site/
R Programming Online Directory
Perl
https://perl.0x3d.site/
Perl Online Directory
Java
https://java.0x3d.site/
Java Online Directory
Kotlin
https://kotlin.0x3d.site/
Kotlin Online Directory
PHP
https://php.0x3d.site/
PHP Online Directory
React JS
https://react.0x3d.site/
React JS Online Directory
Angular
https://angular.0x3d.site/
Angular JS Online Directory