aboutsummaryrefslogtreecommitdiff
path: root/lib/recaptcha.ex
blob: 0037480c7d0c57f7d85ab6111a1b271ff9e26282 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
defmodule Recaptcha do
  @moduledoc """
    A module for verifying reCAPTCHA version 2.0 response strings.

    See the [documentation](https://developers.google.com/recaptcha/docs/verify) for more details.
  """
  @secret Application.get_env(:recaptcha, :secret)
  @http_client Application.get_env(:recaptcha, :http_client, Recaptcha.Http)

  @doc """
  Verifies a reCAPTCHA response string.

  ## Options

    * `:timeout` - the timeout for the request (defaults to 5000ms)
    * `:secret`  - the secret key used by recaptcha (defaults to the secret provided in application config)
    * `:remote_ip` - the IP address of the user (optional and not set by default)

  ## Example

    {:ok, api_response} = Recaptcha.verify("response_string")
  """
  @spec verify(String.t, [timeout: integer, secret: String.t, remote_ip: String.t]) :: {:ok, Recaptcha.Response.t} | {:error, [atom]}
  def verify(response, options \\ [])

  def verify(nil = _response, _) do
    {:error, [:invalid_input_response]}
  end

  def verify(response, options) do
    case @http_client.request_verification(request_body(response, options), Keyword.take(options, [:timeout])) do
      {:error, errors} -> {:error, errors}
      {:ok, %{"success" => false, "error-codes" => errors}} -> {:error, Enum.map(errors, fn(error) -> atomise_api_error(error) end) }
      {:ok, %{"success" => true, "challenge_ts" => timestamp, "hostname" => host}} -> {:ok, %Recaptcha.Response{challenge_ts: timestamp, hostname: host}}
    end
  end

  defp request_body(response, options) do
    body_options = Keyword.take(options, [:remote_ip, :secret])
    application_options = [secret: @secret]

    # override application secret with options secret if it exists
    application_options
    |> Keyword.merge(body_options)
    |> Keyword.put(:response, response)
    |> URI.encode_query
  end

  defp atomise_api_error(error) do
    String.replace(error, "-", "_")
    |> String.to_atom
  end
end