diff --git a/README.md b/README.md index 0a8f45e..81ceccc 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,16 @@ config :elixir_auth_google, google_client_id: , google_scope: "profile", google_redirect_uri: , +``` + +- Create a new endpoint matching the `google_redirect_uri`. +On this endpoint you can exchange the google code for the user's token and +then get the user profile: + +```eixir + def index(conn, %{"code" => code}) do + token = ElixirAuthGoogle.get_token(code) + profile = ElixirAuthGoogle.get_user_profile(token["access_token"]) + render(conn, "index.html", profile: profile) + en ``` \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index 0773cad..6c7f388 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,7 +1,5 @@ use Mix.Config if Mix.env() == :test do - # define configuration specific to test environment - # It can be useful when testing/mocking - nil + config :elixir_auth_google, httpoison: ElixirAuthGoogle.HTTPoison.InMemory end diff --git a/lib/elixir_auth_google.ex b/lib/elixir_auth_google.ex index 942fa98..fdfcefb 100644 --- a/lib/elixir_auth_google.ex +++ b/lib/elixir_auth_google.ex @@ -2,13 +2,46 @@ defmodule ElixirAuthGoogle do @moduledoc """ Minimalist Google OAuth Authentication for Elixir Apps """ - @google_url "https://accounts.google.com/o/oauth2/v2/auth?response_type=code" + @httpoison Application.get_env(:elixir_auth_google, :httpoison) || HTTPoison + @google_auth_url "https://accounts.google.com/o/oauth2/v2/auth?response_type=code" + @google_token_url "https://oauth2.googleapis.com/token" + @google_user_profile "https://www.googleapis.com/oauth2/v3/userinfo" def generate_oauth_url do client_id = Application.get_env(:elixir_auth_google, :google_client_id) scope = Application.get_env(:elixir_auth_google, :google_scope ) || "profile" redirect_uri = Application.get_env(:elixir_auth_google, :google_redirect_uri) - "#{@google_url}&client_id=#{client_id}&scope=#{scope}&redirect_uri=#{redirect_uri}" + "#{@google_auth_url}&client_id=#{client_id}&scope=#{scope}&redirect_uri=#{redirect_uri}" end + + def get_token(code) do + body = Poison.encode!( + %{ client_id: Application.get_env(:elixir_auth_google, :google_client_id), + client_secret: Application.get_env(:elixir_auth_google, :google_client_secret), + redirect_uri: Application.get_env(:elixir_auth_google, :google_redirect_uri), + grant_type: "authorization_code", + code: code + }) + + @httpoison.post(@google_token_url, body) + |> parse_body_response() + end + + def get_user_profile(token) do + "#{@google_user_profile}?access_token=#{token}" + |> @httpoison.get() + |> parse_body_response() + end + + defp parse_body_response({:error, err}), do: {:error, err} + defp parse_body_response({:ok, response}) do + body = Map.get(response, :body) + if body == nil do + {:error, :no_body} + else + Poison.decode(body) + end + end + end diff --git a/lib/httpoison/in_memory.ex b/lib/httpoison/in_memory.ex new file mode 100644 index 0000000..fa6fe1e --- /dev/null +++ b/lib/httpoison/in_memory.ex @@ -0,0 +1,9 @@ +defmodule ElixirAuthGoogle.HTTPoison.InMemory do + def get("https://www.googleapis.com/oauth2/v3/userinfo?access_token=wrong_token") do + {:error, :bad_request} + end + + def get(_url), do: {:ok, %{body: Poison.encode!(%{name: "dwyl"})}} + + def post(_url, _body), do: {:ok, %{body: Poison.encode!(%{access_token: "token1"})}} +end diff --git a/mix.exs b/mix.exs index 7a60d69..4b0519e 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule ElixirAuthGoogle.MixProject do [ app: :elixir_auth_google, version: @version, - elixir: "~> 1.8", + elixir: "~> 1.9", start_permanent: Mix.env() == :prod, deps: deps(), description: @description, @@ -26,7 +26,8 @@ defmodule ElixirAuthGoogle.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:httpoison, "~> 1.6"} + {:httpoison, "~> 1.6"}, + {:poison, "~> 4.0"} ] end diff --git a/mix.lock b/mix.lock index 5255a9d..f4b33ac 100644 --- a/mix.lock +++ b/mix.lock @@ -6,6 +6,7 @@ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, } diff --git a/test/elixir_auth_google_test.exs b/test/elixir_auth_google_test.exs index cf82eaa..bee5357 100644 --- a/test/elixir_auth_google_test.exs +++ b/test/elixir_auth_google_test.exs @@ -6,4 +6,16 @@ defmodule ElixirAuthGoogleTest do Application.put_env(:elixir_auth_google, :google_client_id, 123) assert ElixirAuthGoogle.generate_oauth_url() =~ "https://accounts.google.com/o/oauth2/v2/auth?response_type=code" end + + test "get Google token" do + assert ElixirAuthGoogle.get_token("ok_code") == {:ok, %{"access_token" => "token1"}} + end + + test "get user profile" do + assert ElixirAuthGoogle.get_user_profile("123") == {:ok, %{"name" => "dwyl"}} + end + + test "return error with incorrect token" do + assert ElixirAuthGoogle.get_user_profile("wrong_token") == {:error, :bad_request } + end end