diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..1477090 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @ueberauth/developers diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..59ced69 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,81 @@ +--- + +name: Continuous Integration + +on: + pull_request: + types: + - opened + - reopened + - synchronize + +jobs: + Test: + runs-on: ubuntu-latest + + container: + image: elixir:1.11-alpine + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + persist-credentials: false + + - name: Install (mix) + run: | + mix local.rebar --force + mix local.hex --force + + - name: Install (deps) + run: mix deps.get + + - name: Run Tests + run: mix test + + Format: + runs-on: ubuntu-latest + + container: + image: elixir:1.11-alpine + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + persist-credentials: false + + - name: Install (mix) + run: | + mix local.rebar --force + mix local.hex --force + mix deps.get + + - name: Run Formatter + run: mix format --check-formatted + + Credo: + runs-on: ubuntu-latest + + container: + image: elixir:1.11-alpine + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + persist-credentials: false + + - name: Install (os) + run: apk add --no-cache gcc g++ git make musl-dev tar zstd + + - name: Install (mix) + run: | + mix local.rebar --force + mix local.hex --force + + - name: Install (deps) + run: mix deps.get + + - name: Run Credo + run: mix credo diff --git a/config/test.exs b/config/test.exs index e69de29..8b13789 100644 --- a/config/test.exs +++ b/config/test.exs @@ -0,0 +1 @@ + diff --git a/lib/ueberauth/strategy/twitter.ex b/lib/ueberauth/strategy/twitter.ex index 0643a04..4ddaa0d 100644 --- a/lib/ueberauth/strategy/twitter.ex +++ b/lib/ueberauth/strategy/twitter.ex @@ -5,21 +5,21 @@ defmodule Ueberauth.Strategy.Twitter do use Ueberauth.Strategy, uid_field: :id_str - alias Ueberauth.Auth.Info alias Ueberauth.Auth.Credentials alias Ueberauth.Auth.Extra - alias Ueberauth.Strategy.Twitter + alias Ueberauth.Auth.Info + alias Ueberauth.Strategy.Twitter.OAuth @doc """ Handles initial request for Twitter authentication. """ def handle_request!(conn) do params = with_state_param([], conn) - token = Twitter.OAuth.request_token!([], redirect_uri: callback_url(conn, params)) + token = OAuth.request_token!([], redirect_uri: callback_url(conn, params)) conn |> put_session(:twitter_token, token) - |> redirect!(Twitter.OAuth.authorize_url!(token)) + |> redirect!(OAuth.authorize_url!(token)) end @doc """ @@ -28,7 +28,7 @@ defmodule Ueberauth.Strategy.Twitter do def handle_callback!(%Plug.Conn{params: %{"oauth_verifier" => oauth_verifier}} = conn) do token = get_session(conn, :twitter_token) - case Twitter.OAuth.access_token(token, oauth_verifier) do + case OAuth.access_token(token, oauth_verifier) do {:ok, access_token} -> fetch_user(conn, access_token) {:error, error} -> set_errors!(conn, [error(error.code, error.reason)]) end @@ -104,7 +104,7 @@ defmodule Ueberauth.Strategy.Twitter do defp fetch_user(conn, token) do params = [{"include_entities", false}, {"skip_status", true}, {"include_email", true}] - case Twitter.OAuth.get("/1.1/account/verify_credentials.json", params, token) do + case OAuth.get("/1.1/account/verify_credentials.json", params, token) do {:ok, %{status_code: 401, body: _, headers: _}} -> set_errors!(conn, [error("token", "unauthorized")]) diff --git a/lib/ueberauth/strategy/twitter/internal.ex b/lib/ueberauth/strategy/twitter/internal.ex index 0d966b7..e39e852 100644 --- a/lib/ueberauth/strategy/twitter/internal.ex +++ b/lib/ueberauth/strategy/twitter/internal.ex @@ -4,18 +4,22 @@ defmodule Ueberauth.Strategy.Twitter.OAuth.Internal do """ def get(url, extraparams, {consumer_key, consumer_secret, _}, token \\ "", token_secret \\ "") do - creds = OAuther.credentials( - consumer_key: consumer_key, - consumer_secret: consumer_secret, - token: token, - token_secret: token_secret - ) + creds = + OAuther.credentials( + consumer_key: consumer_key, + consumer_secret: consumer_secret, + token: token, + token_secret: token_secret + ) + {header, params} = "get" |> OAuther.sign(url, extraparams, creds) - |> OAuther.header + |> OAuther.header() + + response = HTTPoison.get(url, [header, {"Accept", "application/json"}], params: params) - HTTPoison.get(url, [header, {"Accept", "application/json"}], params: params) + response |> decode_body() end @@ -40,12 +44,7 @@ defmodule Ueberauth.Strategy.Twitter.OAuth.Internal do def decode_body(other), do: other def params_decode(resp) do - resp - |> String.split("&", trim: true) - |> Enum.map(&String.split(&1, "=")) - |> Enum.map(&List.to_tuple/1) - |> Enum.into(%{}) - # |> Enum.reduce(%{}, fn({name, val}, acc) -> Map.put_new(acc, name, val) end) + URI.decode_query(resp) end def token(params) do diff --git a/lib/ueberauth/strategy/twitter/oauth.ex b/lib/ueberauth/strategy/twitter/oauth.ex index e231abb..d85f859 100644 --- a/lib/ueberauth/strategy/twitter/oauth.ex +++ b/lib/ueberauth/strategy/twitter/oauth.ex @@ -12,17 +12,19 @@ defmodule Ueberauth.Strategy.Twitter.OAuth do alias Ueberauth.Strategy.Twitter.OAuth.Internal - @defaults [access_token: "/oauth/access_token", - authorize_url: "/oauth/authorize", - request_token: "/oauth/request_token", - site: "https://api.twitter.com"] + @defaults [ + access_token: "/oauth/access_token", + authorize_url: "/oauth/authorize", + request_token: "/oauth/request_token", + site: "https://api.twitter.com" + ] defmodule ApiError do @moduledoc "Raised on OAuth API errors." defexception [:message, :code] - def message(e = %{code: nil}), do: e.message + def message(%{code: nil} = e), do: e.message def message(e) do "#{e.message} (Code #{e.code})" @@ -60,6 +62,7 @@ defmodule Ueberauth.Strategy.Twitter.OAuth do end def get(url, access_token), do: get(url, [], access_token) + def get(url, params, {token, token_secret}) do client() |> to_url(url) @@ -93,6 +96,10 @@ defmodule Ueberauth.Strategy.Twitter.OAuth do {:ok, {token, token_secret}} end + defp decode_response({:ok, %{status_code: 401, body: body}}) do + {:error, "401: #{inspect(body)}"} + end + defp decode_response({:ok, %{body: %{"errors" => [error | _]}}}) do {:error, %ApiError{message: error["message"], code: error["code"]}} end diff --git a/mix.exs b/mix.exs index 7c20df6..6467393 100644 --- a/mix.exs +++ b/mix.exs @@ -8,11 +8,13 @@ defmodule UeberauthTwitter.Mixfile do [ app: :ueberauth_twitter, version: @version, - name: "Überauth Twitter", + name: "Ueberauth Twitter Strategy", + package: package(), elixir: "~> 1.1", build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, - package: package(), + source_url: @source_url, + homepage_url: @source_url, deps: deps(), docs: docs() ] @@ -29,7 +31,8 @@ defmodule UeberauthTwitter.Mixfile do {:ueberauth, "~> 0.7"}, # dev/test dependencies - {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, + {:earmark, ">= 0.0.0", only: :dev}, + {:ex_doc, "~> 0.18", only: :dev}, {:credo, "~> 0.8", only: [:dev, :test]} ] end diff --git a/mix.lock b/mix.lock index 7d55184..935ddea 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,13 @@ %{ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, + "certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"}, "credo": {:hex, :credo, "0.10.2", "03ad3a1eff79a16664ed42fc2975b5e5d0ce243d69318060c626c34720a49512", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "539596b6774069260d5938aa73042a2f5157e1c0215aa35f5a53d83889546d14"}, "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"}, "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, "ex_doc": {:hex, :ex_doc, "0.24.2", "e4c26603830c1a2286dae45f4412a4d1980e1e89dc779fcd0181ed1d5a05c8d9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "e134e1d9e821b8d9e4244687fb2ace58d479b67b282de5158333b0d57c6fb7da"}, - "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "c2790c9f0f7205f4a362512192dee8179097394400e745e4d20bab7226a8eaad"}, + "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"}, "httpoison": {:hex, :httpoison, "1.5.0", "71ae9f304bdf7f00e9cd1823f275c955bdfc68282bc5eb5c85c3a9ade865d68e", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "e9d994aea63fab9e29307920492ab95f87339b56fbc5c8c4b1f65ea20d3ba9a4"}, - "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, @@ -17,11 +17,11 @@ "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, "oauther": {:hex, :oauther, "1.1.1", "7d8b16167bb587ecbcddd3f8792beb9ec3e7b65c1f8ebd86b8dd25318d535752", [:mix], [], "hexpm", "9374f4302045321874cccdc57eb975893643bd69c3b22bf1312dab5f06e5788e"}, - "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, + "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"}, "plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm", "603561dc0fd62f4f2ea9b890f4e20e1a0d388746d6e20557cafb1b16950de88c"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, "ueberauth": {:hex, :ueberauth, "0.7.0", "9c44f41798b5fa27f872561b6f7d2bb0f10f03fdd22b90f454232d7b087f4b75", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2efad9022e949834f16cc52cd935165049d81fa9e925690f91035c2e4b58d905"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } diff --git a/test/strategy/twitter/oauth_test.exs b/test/strategy/twitter/oauth_test.exs index c646e2a..279867c 100644 --- a/test/strategy/twitter/oauth_test.exs +++ b/test/strategy/twitter/oauth_test.exs @@ -4,33 +4,35 @@ defmodule Ueberauth.Strategy.Twitter.OAuthTest do alias Ueberauth.Strategy.Twitter.OAuth setup do - Application.put_env :ueberauth, OAuth, + Application.put_env(:ueberauth, OAuth, consumer_key: "consumer_key", consumer_secret: "consumer_secret" + ) + :ok end test "access_token!/2: raises an appropriate error on auth failure" do assert_raise RuntimeError, ~r/401/i, fn -> - OAuth.access_token! {"badtoken", "badsecret"}, "badverifier" + OAuth.access_token!({"badtoken", "badsecret"}, "badverifier") end end test "access_token!/2 raises an appropriate error on network failure" do assert_raise RuntimeError, ~r/nxdomain/i, fn -> - OAuth.access_token! {"token", "secret"}, "verifier", site: "https://bogusapi.twitter.com" + OAuth.access_token!({"token", "secret"}, "verifier", site: "https://bogusapi.twitter.com") end end test "request_token!/2: raises an appropriate error on auth failure" do assert_raise RuntimeError, ~r/401/i, fn -> - OAuth.request_token! [], redirect_uri: "some/uri" + OAuth.request_token!([], redirect_uri: "some/uri") end end test "request_token!/2: raises an appropriate error on network failure" do assert_raise RuntimeError, ~r/nxdomain/i, fn -> - OAuth.request_token! [], site: "https://bogusapi.twitter.com", redirect_uri: "some/uri" + OAuth.request_token!([], site: "https://bogusapi.twitter.com", redirect_uri: "some/uri") end end end