aboutsummaryrefslogtreecommitdiff
path: root/lib/recaptcha.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/recaptcha.ex')
-rw-r--r--lib/recaptcha.ex86
1 files changed, 32 insertions, 54 deletions
diff --git a/lib/recaptcha.ex b/lib/recaptcha.ex
index e0656c3..0037480 100644
--- a/lib/recaptcha.ex
+++ b/lib/recaptcha.ex
@@ -1,75 +1,53 @@
defmodule Recaptcha do
- require Elixir.EEx
+ @moduledoc """
+ A module for verifying reCAPTCHA version 2.0 response strings.
- @secret_key_errors ~w(missing-input-secret invalid-input-secret)
-
- EEx.function_from_file :defp, :render_template, "lib/template.html.eex", [:assigns]
-
- @doc """
- Returns a string with reCAPTCHA code
-
- To convert the string to html code, use Phoenix.HTML.Raw/1 method
+ See the [documentation](https://developers.google.com/recaptcha/docs/verify) for more details.
"""
- def display(options \\ []) do
- public_key = options[:public_key] || config.public_key
- render_template(public_key: public_key, options: options)
- end
+ @secret Application.get_env(:recaptcha, :secret)
+ @http_client Application.get_env(:recaptcha, :http_client, Recaptcha.Http)
@doc """
- Verifies reCAPTCHA response string.
+ Verifies a reCAPTCHA response string.
- The function returns :ok or :error, depending on the verification's result
+ ## Options
- :ok is returned when the response code was successfully verified
+ * `: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)
- :error is returned when the response is nil or if the reCAPTCHA server returned
- a missing-input-response or invalid-input-response code
+ ## Example
- The function raises RuntimeError if the server returned a missing-input-secret or invalid-input-secret
- code
+ {:ok, api_response} = Recaptcha.verify("response_string")
"""
- def verify(remote_ip, response, options \\ [])
+ @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(remote_ip, response, options) when is_tuple(remote_ip) do
- verify(:inet_parse.ntoa(remote_ip), response, options)
+ def verify(nil = _response, _) do
+ {:error, [:invalid_input_response]}
end
- def verify(_remote_ip, nil = _response, _options), do: :error
-
- def verify(remote_ip, response, options) do
- case api_response(remote_ip, response, options) do
- %{"success" => true} ->
- :ok
- %{"success" => false, "error-codes" => error_codes} ->
- handle_error_codes(error_codes)
- %{"success" => false} ->
- :error
+ 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 api_response(remote_ip, response, options) do
- private_key = options[:private_key] || config.private_key
- timeout = options[:timeout] || 5000
- body_content = URI.encode_query(%{"remoteip" => to_string(remote_ip),
- "response" => response,
- "secret" => private_key})
- headers = [{"Content-type", "application/x-www-form-urlencoded"}, {"Accept", "application/json"}]
+ defp request_body(response, options) do
+ body_options = Keyword.take(options, [:remote_ip, :secret])
+ application_options = [secret: @secret]
- HTTPoison.post!(config.verify_url, body_content, headers, timeout: timeout).body
- |> Poison.decode!
+ # override application secret with options secret if it exists
+ application_options
+ |> Keyword.merge(body_options)
+ |> Keyword.put(:response, response)
+ |> URI.encode_query
end
- defp config do
- Application.get_env(:recaptcha, :api_config)
+ defp atomise_api_error(error) do
+ String.replace(error, "-", "_")
+ |> String.to_atom
end
-
- defp handle_error_codes(error_codes) do
- if Enum.any?(error_codes, fn(code) -> Enum.member?(@secret_key_errors, code) end) do
- raise RuntimeError,
- message: "reCaptcha API has declined the private key. Please make sure you've set the correct private key"
- else
- :error
- end
- end
-
end