OmniCaptcha

Hex.pm VersionDocumentationpipeline
statuscoverage report

The package is fork of the hcaptcha package that has been modified to be adaptable.

Important Notice

The repo works for me but is not tested that all configuration options or callbacks given by captcha providers are processed correctly. Feel free to open PRs to resolve issues that you may experience.

Installation

  1. Add omni_captcha to your mix.exs dependencies
  defp deps do
    [
      {:omni_captcha, "~> 0.0.4"},
    ]
  end
  1. List :omni_captcha as an application dependency
  def application do
    [ extra_applications: [:omni_captcha] ]
  end
  1. Run mix do deps.get, compile

Config

Specify the adapter to have a default adapter to be used across the project.

The following adapters currently available in the project include:

By default the public and private keys are loaded via the OMNI_CAPTCHA_PUBLIC_KEY and OMNI_CAPTCHA_PRIVATE_KEY environment variables.

  config :omni_captcha,
    adapter: OmniCaptcha.Adapter.Turnstile,
    site_key: System.fetch_env!("OMNI_CAPTCHA_SITE_KEY"),
    secret_key: System.fetch_env!("OMNI_CAPTCHA_SECRET_KEY")

JSON Decoding

By default omni_captcha will use Jason to decode JSON responses, this can be changed as such:

  config :omni_captcha, :json_library, Poison

Load the Script

Render the omni_captcha script in the head block.

<head>
  <OmniCaptcha.Template.script />
</head>
Option Action Default
adapter Sets the default adapter to use for captcha provider sandbox
script_opts Configurable script options for the selected script ""

Render the Widget

Render the embed inside the form.

<%= form_for ... %>
  <OmniCaptcha.Template.captcha />
<% end %>
Option Action Default
adapter Sets the default adapter to use for captcha provider sandbox
site_key Site key to render with the Captcha widget Site key from the config file

Verify API

OmniCaptcha provides the verify/2 method. Below is an example using a Phoenix controller action:

Turnstile

  def create(conn, params) do
    case OmniCaptcha.verify(params["cf-turnstile-response"]) do
      {:ok, response} -> do_something
      {:error, errors} -> handle_error
    end
  end

reCAPTCHA

  def create(conn, params) do
    case OmniCaptcha.verify(params["g-recaptcha-response"]) do
      {:ok, response} -> do_something
      {:error, errors} -> handle_error
    end
  end

hCaptcha

  def create(conn, params) do
    case OmniCaptcha.verify(params["h-captcha-response"]) do
      {:ok, response} -> do_something
      {:error, errors} -> handle_error
    end
  end

verify method sends a POST request to the omni_captcha API and returns 2 possible values:

{:ok, %OmniCaptcha.Response{challenge_ts: timestamp, hostname: host}} -> The captcha is valid, see the documentation for more details.

{:error, errors} -> errors contains atomised versions of the errors returned by the API, See the error documentation for more details. Errors caused by timeouts in HTTPoison or Jason encoding are also returned as atoms. If the turnstile request succeeds but the challenge is failed, a :challenge_failed error is returned.

(turnstile)[https://developers.cloudflare.com/turnstile/get-started/server-side-validation/#error-codes] (reCAPTCHA)[https://developers.google.com/recaptcha/docs/verify#error-code-reference] (hCaptcha)[https://docs.hcaptcha.com/configuration#error-codes]

verify method also accepts a keyword list as the third parameter with the following options:

Option Action Default
adapter Sets the default adapter to use for captcha provider sandbox
http_client Sets the default HTTP adapter in the package OmniCaptcha.Http
secret Private key to send as a parameter of the API request Private key from the config file
timeout Time to wait before timeout 5000 (ms)
remote_ip Optional. The user's IP address, used by omni_captcha no default

Running multiple captchas

This package allows for using more than one captcha at once if necessary.

Custom modules are also supported if the validation API responds in a similar format.

conn
|> assign(adapter: OmniCaptcha.Adapter.HCaptcha)
|> assign(site_key: "asdf1234")
<head>
  <OmniCaptcha.Template.script adapter={@adapter} />
</head>
<%= form_for ... %>
  <OmniCaptcha.Template.captcha adapter={@adapter} site_key={@site_key} />
<% end %>
OmniCaptcha.verify(
  params["captcha-response"],
  adapter: OmniCaptcha.Adapter.HCaptcha,
  secret: "4321fdsa"
)

Testing

To recieve only valid responses from the live Turnstile API use the following config.

config :omni_captcha,
  secret_key: "1x0000000000000000000000000000000cAA"

To recieve only invalid responses from the live Turnstile API use the following config.

config :omni_captcha,
  secret_key: "2x0000000000000000000000000000000AA"

Setting up tests without network access can be done also. When configured as such a positive or negative result can be generated locally.

config :omni_captcha,
  http_client: OmniCaptcha.Http.MockClient,
  secret_key: "test_secret_key"


  {:ok, _details} = OmniCaptcha.verify("valid_response")

  {:error, _details} = OmniCaptcha.verify("invalid_response")

License

MIT License.