defmodule ElixirAuthGithub do @moduledoc """ ElixirAuthGithub is an Elixir package that handles all your GitHub OAuth needs so you can add "Sign in with GitHub" to any Elixir/Phoenix App. For all setup details, please see: https://github.com/dwyl/elixir-auth-github """ @github_url "https://github.com/login/oauth/" @github_auth_url @github_url <> "access_token?" @httpoison Application.get_env(:elixir_auth_github, :httpoison) || HTTPoison @doc """ `login_url/0` returns a `String` URL to be used as the initial OAuth redirect. Requires `GITHUB_CLIENT_ID` environment variable to be set. See step 2 of setup instructions. """ def login_url do client_id = System.get_env("GITHUB_CLIENT_ID") @github_url <> "authorize?client_id=#{client_id}" end @doc """ Identical to `login_url/0` except with an additional state property appended to the URL. """ def login_url(state) do login_url() <> "&state=#{state}" end @doc """ login_url_with_scope/1 takes a list of GitHub auth scopes to add to the url. Returns `String` URL. """ def login_url_with_scope(scopes) do login_url() <> "&scope=#{Enum.join(scopes, "%20")}" end @doc """ When called with a valid OAuth callback code, `github_auth/1` makes a number of authentication requests to GitHub and returns a tuple with `:ok` and a map with GitHub user details and an access_token. Bad authentication codes will return a tuple with `:error` and an error map. """ def github_auth(code) do %{"client_id" => System.get_env("GITHUB_CLIENT_ID"), "client_secret" => System.get_env("GITHUB_CLIENT_SECRET"), "code" => code} |> URI.encode_query |> (&(@httpoison.post!(@github_auth_url <> &1, ""))).() |> Map.get(:body) |> URI.decode_query |> check_authenticated end defp check_authenticated(%{"access_token" => access_token}) do access_token |> get_user_details end defp check_authenticated(error), do: {:error, error} defp get_user_details(access_token) do @httpoison.get!("https://api.github.com/user", [ {"User-Agent", "elixir-practice"}, {"Authorization", "token #{access_token}"} ]) |> Map.get(:body) |> Poison.decode! |> set_user_details(access_token) end defp set_user_details(%{"login" => _name} = user, access_token) do user = Map.put(user, "access_token", access_token) {:ok, user} end defp set_user_details(error, _token), do: {:error, error} end