diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..09f3be6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Order is important. The last matching pattern takes the most precedence. +# Default owners for everything in the repo. +* @ueberauth/developers diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d57516b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: Continuous Integration + +on: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: + - 'master' +jobs: + Test: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v1 + + - name: Set up Elixir + uses: erlef/setup-elixir@v1 + with: + elixir-version: '1.11' + otp-version: '22.3' + + - name: Install Dependencies + run: | + mix local.rebar --force + mix local.hex --force + mix deps.get + - name: Run Tests + run: mix test + + Linting: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v1 + + - name: Set up Elixir + uses: erlef/setup-elixir@v1 + with: + elixir-version: '1.11' + otp-version: '22.3' + + - name: Install Dependencies + run: | + mix local.rebar --force + mix local.hex --force + mix deps.get + - name: Run Formatter + run: mix format --check-formatted + + - name: Run Credo + run: mix credo diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..61d5739 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Hexpm Release + +on: + release: + types: [published] + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Elixir + uses: erlef/setup-elixir@v1 + with: + elixir-version: '1.11' + otp-version: '22.3' + - name: Restore dependencies cache + uses: actions/cache@v2 + with: + path: deps + key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} + restore-keys: ${{ runner.os }}-mix- + - name: Install dependencies + run: | + mix local.rebar --force + mix local.hex --force + mix deps.get + - name: Run Hex Publish + run: mix hex.publish --yes + env: + HEX_API_KEY: ${{ secrets.HEX_API_KEY }} diff --git a/.gitignore b/.gitignore index 9607671..7ee0d01 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,26 @@ -/_build -/deps +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). *.ez + +# Ignore package tarball (built via "mix hex.build"). +ueberauth_facebook-*.tar + +# Temporary files for e.g. tests. +/tmp diff --git a/.travis.yml b/.travis.yml index eb03ffa..ae69db4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,19 @@ language: elixir +elixir: + - 1.6 + - 1.5 + - 1.4 + - 1.3 +otp_release: + - 20.2 + - 19.3 + - 18.3 matrix: - include: - - otp_release: 18.3 - elixir: 1.3.2 - - otp_release: 19.0 - elixir: 1.3.2 -sudo: false + exclude: + - elixir: 1.6 + otp_release: 18.3 + - elixir: 1.3 + otp_release: 20.2 script: - mix test + - mix credo diff --git a/CHANGELOG.md b/CHANGELOG.md index c505f56..40dc272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,83 @@ -# v 0.6.0 +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## v0.10.0 - 2021-09-23 + +* Allows errors from the provider to be thrown back to the user. + +## v0.9.0 - 2021-09-23 + +* Require at least Ueberauth 0.7 due to changed builtin CSRF protection + +## v0.8.2 - 2021-07-15 + +* Allow OAuth options to be configured from the connection +* Set default profile image to large + +## v0.8.1 - 2020-03-02 + +* Handle errors from fetching the access token with an invalid/expired code +* Support OAuth2 version 2.0 library +* Set OAuth2 JSON library based on Ueberauth's JSON libray + +## v0.8.0 - 2019-03-09 + +* Add option to specify scheme for user avatar URL +* Stop sending empty params to the Facebook auth dialog + +## v0.7.0 - 2017-07-18 + +* Support `:appsecret_proof` parameter +* Set the required `:profile_fields` to get the email back + +## v0.6.0 - 2016-12-27 * Support `:display` parameter * Use OAuth2 0.8 -# v 0.5.0 +## v0.5.0 - 2016-09-21 * Pin OAuth2 to 0.6 to avoid errors -# v 0.4 +## v0.4.0 - 2016-07-19 * Allow `:state` param to be configured -# v 0.3.2 +## v0.3.2 - 2016-02-15 * Fix support for auth_type -# v 0.3.1 +## v0.3.1 - 2016-02-10 * Add support for missing Locale parameter -# v 0.3.0 +## v0.3.0 - 2016-02-04 * Add support for auth_type * Support for additional request variables -# v 0.2.1 +## v0.2.1 - 2015-12-11 * Add missing `profile_fields` parameter -# v 0.2.0 +## v0.2.0 - 2015-11-28 * Release to follow the Ueberauth 0.2.0 release -# v 0.1.4 +## v0.1.4 - 2015-11-18 * Added oauth2 and ueberauth to applications list -# v 0.1.1 +## v0.1.1 - 2015-11-16 * Fixed character encoding errors in Hex package -# v 0.1.0 +## v0.1.0 - 2015-11-16 * Initial strategy diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3669a7e..c94ad84 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Contributing to Ueberauth Facebook +# Contributing ## Pull Requests Welcome 1. Fork ueberauth_facebook diff --git a/README.md b/README.md index cf4fcd3..81db272 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Überauth Facebook -[![Build Status][travis-img]][travis] [![Hex Version][hex-img]][hex] [![License][license-img]][license] -[travis-img]: https://travis-ci.org/ueberauth/ueberauth_facebook.png?branch=master -[travis]: https://travis-ci.org/ueberauth/ueberauth_facebook -[hex-img]: https://img.shields.io/hexpm/v/ueberauth_facebook.svg -[hex]: https://hex.pm/packages/ueberauth_facebook -[license-img]: http://img.shields.io/badge/license-MIT-brightgreen.svg -[license]: http://opensource.org/licenses/MIT +[![Build Status](https://travis-ci.org/ueberauth/ueberauth_facebook.svg?branch=master)](https://travis-ci.org/ueberauth/ueberauth_facebook) +[![Module Version](https://img.shields.io/hexpm/v/ueberauth_facebook.svg)](https://hex.pm/packages/ueberauth_facebook) +[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/ueberauth_facebook/) +[![Total Download](https://img.shields.io/hexpm/dt/ueberauth_facebook.svg)](https://hex.pm/packages/ueberauth_facebook) +[![License](https://img.shields.io/hexpm/l/ueberauth_facebook.svg)](https://github.com/ueberauth/ueberauth_facebook/blob/master/LICENSE) +[![Last Updated](https://img.shields.io/github/last-commit/ueberauth/ueberauth_facebook.svg)](https://github.com/ueberauth/ueberauth_facebook/commits/master) > Facebook OAuth2 strategy for Überauth. @@ -16,57 +15,61 @@ 1. Add `:ueberauth_facebook` to your list of dependencies in `mix.exs`: - ```elixir - def deps do - [{:ueberauth_facebook, "~> 0.6"}] - end - ``` + ```elixir + def deps do + [ + {:ueberauth_facebook, "~> 0.8"} + ] + end + ``` 1. Add the strategy to your applications: - ```elixir - def application do - [applications: [:ueberauth_facebook]] - end - ``` + ```elixir + def application do + [ + applications: [:ueberauth_facebook] + ] + end + ``` 1. Add Facebook to your Überauth configuration: - ```elixir - config :ueberauth, Ueberauth, - providers: [ - facebook: {Ueberauth.Strategy.Facebook, []} - ] - ``` + ```elixir + config :ueberauth, Ueberauth, + providers: [ + facebook: {Ueberauth.Strategy.Facebook, []} + ] + ``` -1. Update your provider configuration: +1. Update your provider configuration: - ```elixir - config :ueberauth, Ueberauth.Strategy.Facebook.OAuth, - client_id: System.get_env("FACEBOOK_CLIENT_ID"), - client_secret: System.get_env("FACEBOOK_CLIENT_SECRET") - ``` + ```elixir + config :ueberauth, Ueberauth.Strategy.Facebook.OAuth, + client_id: System.get_env("FACEBOOK_CLIENT_ID"), + client_secret: System.get_env("FACEBOOK_CLIENT_SECRET") + ``` -1. Include the Überauth plug in your controller: +1. Include the Überauth plug in your controller: - ```elixir - defmodule MyApp.AuthController do - use MyApp.Web, :controller - plug Ueberauth - ... - end - ``` + ```elixir + defmodule MyApp.AuthController do + use MyApp.Web, :controller + plug Ueberauth + ... + end + ``` -1. Create the request and callback routes if you haven't already: +1. Create the request and callback routes if you haven't already: - ```elixir - scope "/auth", MyApp do - pipe_through :browser + ```elixir + scope "/auth", MyApp do + pipe_through :browser - get "/:provider", AuthController, :request - get "/:provider/callback", AuthController, :callback - end - ``` + get "/:provider", AuthController, :request + get "/:provider/callback", AuthController, :callback + end + ``` 1. Your controller needs to implement callbacks to deal with `Ueberauth.Auth` and `Ueberauth.Failure` responses. @@ -78,7 +81,7 @@ Depending on the configured URL you can initialize the request through: /auth/facebook -Or with options: +Or with options (`auth_type`, `scope`, `locale`, `display`): /auth/facebook?scope=email,public_profile @@ -117,8 +120,8 @@ config :ueberauth, Ueberauth, See [Graph API Reference > User](https://developers.facebook.com/docs/graph-api/reference/user) for full list of fields. +## Copyright and License -## License - -Please see [LICENSE](https://github.com/ueberauth/ueberauth_facebook/blob/master/LICENSE) for licensing details. +Copyright (c) 2015 Sean Callan +Released under the MIT License, which can be found in the repository in [LICENSE](./LICENSE). diff --git a/lib/ueberauth/strategy/facebook.ex b/lib/ueberauth/strategy/facebook.ex index ac83dc8..77644bc 100644 --- a/lib/ueberauth/strategy/facebook.ex +++ b/lib/ueberauth/strategy/facebook.ex @@ -3,17 +3,16 @@ defmodule Ueberauth.Strategy.Facebook do Facebook Strategy for Überauth. """ - use Ueberauth.Strategy, default_scope: "email", - profile_fields: "", - uid_field: :id, - allowed_request_params: [ - :auth_type, - :scope, - :locale, - :state, - :display - ] - + use Ueberauth.Strategy, + default_scope: "email,public_profile", + profile_fields: "id,email,gender,link,locale,name,timezone,updated_time,verified", + uid_field: :id, + allowed_request_params: [ + :auth_type, + :scope, + :locale, + :display + ] alias Ueberauth.Auth.Info alias Ueberauth.Auth.Credentials @@ -23,37 +22,50 @@ defmodule Ueberauth.Strategy.Facebook do Handles initial request for Facebook authentication. """ def handle_request!(conn) do - allowed_params = conn - |> option(:allowed_request_params) - |> Enum.map(&to_string/1) + allowed_params = + conn + |> option(:allowed_request_params) + |> Enum.map(&to_string/1) - authorize_url = conn.params + opts = oauth_client_options_from_conn(conn) + + params = + conn.params |> maybe_replace_param(conn, "auth_type", :auth_type) |> maybe_replace_param(conn, "scope", :default_scope) - |> maybe_replace_param(conn, "state", :state) |> maybe_replace_param(conn, "display", :display) - |> Enum.filter(fn {k,_v} -> Enum.member?(allowed_params, k) end) - |> Enum.map(fn {k,v} -> {String.to_existing_atom(k), v} end) + |> Enum.filter(fn {k, _v} -> Enum.member?(allowed_params, k) end) + |> Enum.map(fn {k, v} -> {String.to_existing_atom(k), v} end) |> Keyword.put(:redirect_uri, callback_url(conn)) - |> Ueberauth.Strategy.Facebook.OAuth.authorize_url! + |> with_state_param(conn) - redirect!(conn, authorize_url) + redirect!(conn, Ueberauth.Strategy.Facebook.OAuth.authorize_url!(params, opts)) end @doc """ Handles the callback from Facebook. """ def handle_callback!(%Plug.Conn{params: %{"code" => code}} = conn) do - opts = [redirect_uri: callback_url(conn)] - client = Ueberauth.Strategy.Facebook.OAuth.get_token!([code: code], opts) - token = client.token - - if token.access_token == nil do - err = token.other_params["error"] - desc = token.other_params["error_description"] - set_errors!(conn, [error(err, desc)]) - else - fetch_user(conn, client) + opts = oauth_client_options_from_conn(conn) + + config = + :ueberauth + |> Application.get_env(Ueberauth.Strategy.Facebook.OAuth, []) + |> Keyword.merge(opts) + + try do + client = Ueberauth.Strategy.Facebook.OAuth.get_token!([code: code], opts) + token = client.token + + if token.access_token == nil do + err = token.other_params["error"] + desc = token.other_params["error_description"] + set_errors!(conn, [error(err, desc)]) + else + fetch_user(conn, client, config) + end + rescue + e -> set_errors!(conn, [error("get_token_error", e)]) end end @@ -146,33 +158,35 @@ defmodule Ueberauth.Strategy.Facebook do end defp fetch_image(uid) do - "http://graph.facebook.com/#{uid}/picture?type=large" + "https://graph.facebook.com/#{uid}/picture?type=large" end - defp fetch_user(conn, client) do + defp fetch_user(conn, client, config) do conn = put_private(conn, :facebook_token, client.token) - query = user_query(conn, client.token) + query = user_query(conn, client.token, config) path = "/me?#{query}" + case OAuth2.Client.get(client, path) do {:ok, %OAuth2.Response{status_code: 401, body: _body}} -> set_errors!(conn, [error("token", "unauthorized")]) + {:ok, %OAuth2.Response{status_code: status_code, body: user}} - when status_code in 200..399 -> + when status_code in 200..399 -> put_private(conn, :facebook_user, user) + {:error, %OAuth2.Error{reason: reason}} -> set_errors!(conn, [error("OAuth2", reason)]) end end - defp user_query(conn, token) do - %{"appsecret_proof" => appsecret_proof(token)} + defp user_query(conn, token, config) do + %{"appsecret_proof" => appsecret_proof(token, config)} |> Map.merge(query_params(conn, :locale)) |> Map.merge(query_params(conn, :profile)) - |> URI.encode_query + |> URI.encode_query() end - defp appsecret_proof(token) do - config = Application.get_env(:ueberauth, Ueberauth.Strategy.Facebook.OAuth) + defp appsecret_proof(token, config) do client_secret = Keyword.get(config, :client_secret) token.access_token @@ -180,13 +194,20 @@ defmodule Ueberauth.Strategy.Facebook do |> Base.encode16(case: :lower) end + @compile {:no_warn_undefined, {:crypto, :mac, 4}} + @compile {:no_warn_undefined, {:crypto, :hmac, 3}} defp hmac(data, type, key) do - :crypto.hmac(type, key, data) + if function_exported?(:crypto, :mac, 4) do + :crypto.mac(:hmac, type, key, data) + else + :crypto.hmac(type, key, data) + end end defp query_params(conn, :profile) do %{"fields" => option(conn, :profile_fields)} end + defp query_params(conn, :locale) do case option(conn, :locale) do nil -> %{} @@ -201,11 +222,12 @@ defmodule Ueberauth.Strategy.Facebook do |> options |> Keyword.get(key, default) end + defp option(nil, conn, key), do: option(conn, key) defp option(value, _conn, _key), do: value defp maybe_replace_param(params, conn, name, config_key) do - if params[name] do + if params[name] || is_nil(option(params[name], conn, config_key)) do params else Map.put( @@ -216,7 +238,7 @@ defmodule Ueberauth.Strategy.Facebook do end end - def check_access_token(conn, client, token) do + defp check_access_token(conn, client, token) do app_id = client.client_id app_secret = client.client_secret query = URI.encode_query(%{ @@ -231,7 +253,17 @@ defmodule Ueberauth.Strategy.Facebook do body: %{"data" => %{"is_valid" => true, "app_id" => ^app_id}} }} -> true _ -> false + end + end + + defp oauth_client_options_from_conn(conn) do + base_options = [redirect_uri: callback_url(conn)] + request_options = conn.private[:ueberauth_request_options].options + case {request_options[:client_id], request_options[:client_secret]} do + {nil, _} -> base_options + {_, nil} -> base_options + {id, secret} -> [client_id: id, client_secret: secret] ++ base_options end end end diff --git a/lib/ueberauth/strategy/facebook/oauth.ex b/lib/ueberauth/strategy/facebook/oauth.ex index 4da7087..f914414 100644 --- a/lib/ueberauth/strategy/facebook/oauth.ex +++ b/lib/ueberauth/strategy/facebook/oauth.ex @@ -2,11 +2,12 @@ defmodule Ueberauth.Strategy.Facebook.OAuth do @moduledoc """ OAuth2 for Facebook. - Add `client_id` and `client_secret` to your configuration: + Add `:client_id` and `:client_secret` to your configuration: + + config :ueberauth, Ueberauth.Strategy.Facebook.OAuth, + client_id: System.get_env("FACEBOOK_APP_ID"), + client_secret: System.get_env("FACEBOOK_APP_SECRET") - config :ueberauth, Ueberauth.Strategy.Facebook.OAuth, - client_id: System.get_env("FACEBOOK_APP_ID"), - client_secret: System.get_env("FACEBOOK_APP_SECRET") """ use OAuth2.Strategy @@ -26,14 +27,18 @@ defmodule Ueberauth.Strategy.Facebook.OAuth do of Ueberauth. """ def client(opts \\ []) do - config = Application.get_env(:ueberauth, Ueberauth.Strategy.Facebook.OAuth) + config = Application.get_env(:ueberauth, Ueberauth.Strategy.Facebook.OAuth, []) opts = @defaults |> Keyword.merge(config) |> Keyword.merge(opts) - OAuth2.Client.new(opts) + json_library = Ueberauth.json_library() + + opts + |> OAuth2.Client.new() + |> OAuth2.Client.put_serializer("application/json", json_library) end @doc """ diff --git a/mix.exs b/mix.exs index 1357e4a..5c66bdd 100644 --- a/mix.exs +++ b/mix.exs @@ -1,51 +1,64 @@ -defmodule UeberauthFacebook.Mixfile do +defmodule Ueberauth.Facebook.Mixfile do use Mix.Project - @version "0.6.0" - @url "https://github.com/ueberauth/ueberauth_facebook" + @source_url "https://github.com/ueberauth/ueberauth_facebook" + @version "0.10.0" def project do - [app: :ueberauth_facebook, - version: @version, - name: "Ueberauth Facebook Strategy", - package: package(), - elixir: "~> 1.3", - build_embedded: Mix.env == :prod, - start_permanent: Mix.env == :prod, - source_url: @url, - homepage_url: @url, - description: description(), - deps: deps(), - docs: docs()] + [ + app: :ueberauth_facebook, + version: @version, + name: "Überauth Facebook", + elixir: "~> 1.3", + build_embedded: Mix.env() == :prod, + start_permanent: Mix.env() == :prod, + package: package(), + deps: deps(), + docs: docs() + ] end def application do - [applications: [:logger, :oauth2, :ueberauth]] + [ + applications: [:logger, :oauth2, :ueberauth] + ] end defp deps do [ - {:ueberauth, "~> 0.4"}, - {:oauth2, "~> 0.8.0"}, - - {:ex_doc, "~> 0.2", only: :dev}, - {:earmark, ">= 0.0.0", only: :dev}, - {:credo, "~> 0.5", only: [:dev, :test]}, + {:ueberauth, "~> 0.7"}, + {:oauth2, "~> 1.0 or ~> 2.0"}, + {:credo, "~> 0.8.10", only: [:dev, :test]}, + {:ex_doc, ">= 0.24.2", only: :dev, runtime: false} ] end defp docs do - [extras: ["README.md", "CONTRIBUTING.md"]] - end - - defp description do - "An Uberauth strategy for Facebook authentication." + [ + extras: [ + "CHANGELOG.md", + "CONTRIBUTING.md", + {:LICENSE, [title: "License"]}, + "README.md" + ], + main: "readme", + source_url: @source_url, + source_ref: "v#{@version}", + homepage_url: @source_url, + formatters: ["html"] + ] end defp package do - [files: ["lib", "mix.exs", "README.md", "LICENSE"], + [ + description: "An Überauth strategy for Facebook authentication.", + files: ["lib", "mix.exs", "README.md", "CHANGELOG.md", "CONTRIBUTING.md", "LICENSE"], maintainers: ["Sean Callan"], licenses: ["MIT"], - links: %{"GitHub": @url}] + links: %{ + Changelog: "https://hexdocs.pm/ueberauth_facebook/changelog.html", + GitHub: @source_url + } + ] end end diff --git a/mix.lock b/mix.lock index ca35554..d4f9c17 100644 --- a/mix.lock +++ b/mix.lock @@ -1,19 +1,25 @@ -%{"bunt": {:hex, :bunt, "0.1.6", "5d95a6882f73f3b9969fdfd1953798046664e6f77ec4e486e6fafc7caad97c6f", [:mix], []}, - "certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []}, - "credo": {:hex, :credo, "0.5.3", "0c405b36e7651245a8ed63c09e2d52c2e2b89b6d02b1570c4d611e0fcbecf4a2", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]}, - "dogma": {:hex, :dogma, "0.0.11", "1396bfe10a962b5c5b0468eba6eb3be85d693d55f2d698fda29660a059eb2180", [:mix], [{:poison, "~> 1.0", [hex: :poison, optional: false]}]}, - "earmark": {:hex, :earmark, "0.1.19", "ffec54f520a11b711532c23d8a52b75a74c09697062d10613fa2dbdf8a9db36e", [:mix], []}, - "ex_doc": {:hex, :ex_doc, "0.10.0", "f49c237250b829df986486b38f043e6f8e19d19b41101987f7214543f75947ec", [:mix], [{:earmark, "~> 0.1.17 or ~> 0.2", [hex: :earmark, optional: true]}]}, - "hackney": {:hex, :hackney, "1.6.3", "d489d7ca2d4323e307bedc4bfe684323a7bf773ecfd77938f3ee8074e488e140", [:mix, :rebar3], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, - "httpoison": {:hex, :httpoison, "0.9.0", "68187a2daddfabbe7ca8f7d75ef227f89f0e1507f7eecb67e4536b3c516faddb", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]}, - "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "mimetype_parser": {:hex, :mimetype_parser, "0.1.2", "221d2d3f727e89d80de5e1610fc2ce444514aa56f873da1b8fc9c033143e5d6a", [:mix], []}, - "oauth2": {:hex, :oauth2, "0.8.2", "3f3c9fef0ceb5ba80b6ed097ec98acc0d13f5869dce0ebbbbc521caa56077c12", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, optional: false]}]}, - "plug": {:hex, :plug, "1.2.0", "496bef96634a49d7803ab2671482f0c5ce9ce0b7b9bc25bc0ae8e09859dd2004", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, - "poison": {:hex, :poison, "1.5.2", "560bdfb7449e3ddd23a096929fb9fc2122f709bcc758b2d5d5a5c7d0ea848910", [:mix], []}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []}, - "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5", "2e73e068cd6393526f9fa6d399353d7c9477d6886ba005f323b592d389fb47be", [:make], []}, - "ueberauth": {:hex, :ueberauth, "0.4.0", "bc72d5e5a7bdcbfcf28a756e34630816edabc926303bdce7e171f7ac7ffa4f91", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}} +%{ + "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"}, + "credo": {:hex, :credo, "0.8.10", "261862bb7363247762e1063713bb85df2bbd84af8d8610d1272cd9c1943bba63", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm", "f3fe29f8de6be431c7736a933a3897fe9aa45533b1d1358b3691ac8ec4371e8f"}, + "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"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, + "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"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mime": {:hex, :mime, "2.0.1", "0de4c81303fe07806ebc2494d5321ce8fb4df106e34dd5f9d787b637ebadc256", [:mix], [], "hexpm", "7a86b920d2aedce5fb6280ac8261ac1a739ae6c1a1ad38f5eadf910063008942"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, + "oauth2": {:hex, :oauth2, "1.0.1", "2a116de7863d4b5a8a265d77e62b17a6f54e0c01a5bbbf02210783cb861e49ac", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "0417bd37101ac27cdfe33003266c15da31cf9d59f1778994624a77febd0d8e07"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, + "plug": {:hex, :plug, "1.12.1", "645678c800601d8d9f27ad1aebba1fdb9ce5b2623ddb961a074da0b96c35187d", [:mix], [{:mime, "~> 1.0 or ~> 2.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.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d57e799a777bc20494b784966dc5fbda91eb4a09f571f76545b72a634ce0d30b"}, + "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"}, + "telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"}, + "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"}, +}