diff --git a/.credo.exs b/.credo.exs deleted file mode 100644 index f62ee1e..0000000 --- a/.credo.exs +++ /dev/null @@ -1,129 +0,0 @@ -# This file contains the configuration for Credo and you are probably reading -# this after creating it with `mix credo.gen.config`. -# -# If you find anything wrong or unclear in this file, please report an -# issue on GitHub: https://github.com/rrrene/credo/issues -# -%{ - # - # You can have as many configs as you like in the `configs:` field. - configs: [ - %{ - # - # Run any config using `mix credo -C `. If no config name is given - # "default" is used. - name: "default", - # - # These are the files included in the analysis: - files: %{ - # - # You can give explicit globs or simply directories. - # In the latter case `**/*.{ex,exs}` will be used. - included: ["lib/", "src/", "web/", "apps/"], - excluded: [~r"/_build/", ~r"/deps/"] - }, - # - # If you create your own checks, you must specify the source files for - # them here, so they can be loaded by Credo before running the analysis. - requires: [], - # - # Credo automatically checks for updates, like e.g. Hex does. - # You can disable this behaviour below: - check_for_updates: true, - # - # If you want to enforce a style guide and need a more traditional linting - # experience, you can change `strict` to `true` below: - strict: false, - # - # If you want to use uncolored output by default, you can change `color` - # to `false` below: - color: true, - # - # You can customize the parameters of any check by adding a second element - # to the tuple. - # - # To disable a check put `false` as second element: - # - # {Credo.Check.Design.DuplicatedCode, false} - # - checks: [ - {Credo.Check.Consistency.ExceptionNames}, - {Credo.Check.Consistency.LineEndings}, - {Credo.Check.Consistency.MultiAliasImportRequireUse}, - {Credo.Check.Consistency.ParameterPatternMatching}, - {Credo.Check.Consistency.SpaceAroundOperators}, - {Credo.Check.Consistency.SpaceInParentheses}, - {Credo.Check.Consistency.TabsOrSpaces}, - - # For some checks, like AliasUsage, you can only customize the priority - # Priority values are: `low, normal, high, higher` - {Credo.Check.Design.AliasUsage, priority: :low}, - - # For others you can set parameters - - # If you don't want the `setup` and `test` macro calls in ExUnit tests - # or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just - # set the `excluded_macros` parameter to `[:schema, :setup, :test]`. - {Credo.Check.Design.DuplicatedCode, excluded_macros: []}, - - # You can also customize the exit_status of each check. - # If you don't want TODO comments to cause `mix credo` to fail, just - # set this value to 0 (zero). - {Credo.Check.Design.TagTODO, exit_status: 2}, - {Credo.Check.Design.TagFIXME}, - - {Credo.Check.Readability.FunctionNames}, - {Credo.Check.Readability.LargeNumbers}, - {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80}, - {Credo.Check.Readability.ModuleAttributeNames}, - {Credo.Check.Readability.ModuleDoc}, - {Credo.Check.Readability.ModuleNames}, - {Credo.Check.Readability.NoParenthesesWhenZeroArity}, - {Credo.Check.Readability.ParenthesesInCondition}, - {Credo.Check.Readability.PredicateFunctionNames}, - {Credo.Check.Readability.PreferImplicitTry}, - {Credo.Check.Readability.RedundantBlankLines}, - {Credo.Check.Readability.Specs, false}, - {Credo.Check.Readability.StringSigils}, - {Credo.Check.Readability.TrailingBlankLine}, - {Credo.Check.Readability.TrailingWhiteSpace}, - {Credo.Check.Readability.VariableNames}, - {Credo.Check.Refactor.DoubleBooleanNegation}, - - # {Credo.Check.Refactor.CaseTrivialMatches}, # deprecated in 0.4.0 - {Credo.Check.Refactor.ABCSize}, - {Credo.Check.Refactor.CondStatements}, - {Credo.Check.Refactor.CyclomaticComplexity}, - {Credo.Check.Refactor.FunctionArity}, - {Credo.Check.Refactor.MatchInCondition}, - {Credo.Check.Refactor.NegatedConditionsInUnless}, - {Credo.Check.Refactor.NegatedConditionsWithElse}, - {Credo.Check.Refactor.Nesting}, - {Credo.Check.Refactor.PipeChainStart}, - {Credo.Check.Refactor.UnlessWithElse}, - {Credo.Check.Refactor.VariableRebinding}, - - {Credo.Check.Warning.BoolOperationOnSameValues}, - {Credo.Check.Warning.IExPry}, - {Credo.Check.Warning.IoInspect}, - {Credo.Check.Warning.NameRedeclarationByAssignment}, - {Credo.Check.Warning.NameRedeclarationByCase}, - {Credo.Check.Warning.NameRedeclarationByDef}, - {Credo.Check.Warning.NameRedeclarationByFn}, - {Credo.Check.Warning.OperationOnSameValues}, - {Credo.Check.Warning.OperationWithConstantResult}, - {Credo.Check.Warning.UnusedEnumOperation}, - {Credo.Check.Warning.UnusedFileOperation}, - {Credo.Check.Warning.UnusedKeywordOperation}, - {Credo.Check.Warning.UnusedListOperation}, - {Credo.Check.Warning.UnusedPathOperation}, - {Credo.Check.Warning.UnusedRegexOperation}, - {Credo.Check.Warning.UnusedStringOperation}, - {Credo.Check.Warning.UnusedTupleOperation}, - - # Custom checks can be created using `mix credo.gen.check`. - # - ] - } - ] -} 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/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..2cafaeb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +patreon: sobolevn 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/.tool-versions b/.tool-versions new file mode 100644 index 0000000..d47ed19 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +erlang 23.2.3 +elixir 1.11 diff --git a/.travis.yml b/.travis.yml index 0d4beef..da39a37 100755 --- a/.travis.yml +++ b/.travis.yml @@ -3,18 +3,19 @@ language: elixir sudo: false elixir: - - 1.2 - - 1.3 + - 1.6 + - 1.5 - 1.4 otp_release: - - 17.4 - - 18.1 + - 20.3 + - 19.3 env: MIX_ENV=test script: - - mix coveralls.travis && mix credo --strict + - mix credo --strict + - mix coveralls.travis notifications: email: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 64a1672..483b089 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## v0.4.0 + +* Fixes VK API version + +## v0.3.1 + +* Fixes docs generation + +## v0.3.0 + +* Updates requirements versions +* Fixes failing tests + +## v0.2.4 + +* Fixes `state` issue, see https://github.com/sobolevn/ueberauth_vk/issues/21 + +## v0.2.3 + +* Adds `state` parameter + ## v0.2.2 * Fixed `ex_doc` version diff --git a/README.md b/README.md index bdbeb86..bd92579 100755 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Requirements -We support `elixir` versions `1.2` and bellow. +We support `elixir` versions `1.4` and above. ## Installation @@ -17,7 +17,7 @@ We support `elixir` versions `1.2` and bellow. ```elixir def deps do # installation via hex: - [{:ueberauth_vk, "~> 0.2"}] + [{:ueberauth_vk, "~> 0.3"}] # if you want to use github: # [{:ueberauth_vk, github: "sobolevn/ueberauth_vk"}] end @@ -73,11 +73,11 @@ We support `elixir` versions `1.2` and bellow. For an example implementation see the [Überauth Example](https://github.com/ueberauth/ueberauth_example) application. -## Calling +## Customizing -Depending on the configured url you can initial the request through: `/auth/vk` +You can customize [multiple fields](https://vk.com/dev/auth_sites), such as `default_scope`, `default_display`, `default_state`, `profile_fields`, and `uid_field`. -Or with options: `/auth/vk?scope=friends,video,offline` +### Scope By default the requested scope is `"public_profile"`. Scope can be configured either explicitly as a `scope` query value on the request path or in your configuration: @@ -88,6 +88,8 @@ config :ueberauth, Ueberauth, ] ``` +### Profile Fields + You can also provide custom fields for user profile: ```elixir @@ -99,6 +101,33 @@ config :ueberauth, Ueberauth, See [VK API Method Reference > User](https://vk.com/dev/users.get) for full list of fields. +### State + +You can also set the custom field called [`state`](https://github.com/sobolevn/ueberauth_vk/pull/20). It is used to prevent "man in the middle" attacks. + +```elixir +config :ueberauth, Ueberauth, + providers: [ + vk: {Ueberauth.Strategy.VK, [default_state: "secret-state-value"]} + ] +``` + +This state will be passed to you in the callback as `/auth/vk?state=` and will be available in the success struct. + +### UID Field + +You can use alternate fields to identify users. For example, you can use `email`. + +```elixir +config :ueberauth, Ueberauth, + providers: [ + vk: {Ueberauth.Strategy.VK, [ + default_scope: "email", + uid_field: :email + ]} + ] +``` + ## License diff --git a/config/config.exs b/config/config.exs index 9124414..2290ea9 100755 --- a/config/config.exs +++ b/config/config.exs @@ -17,4 +17,4 @@ use Mix.Config # import_config "#{Mix.env}.exs" # import_config "#{Mix.env}.exs" -if Mix.env == :test, do: import_config "test.exs" +if Mix.env() == :test, do: import_config("test.exs") diff --git a/config/test.exs b/config/test.exs index 8380957..dd29e6e 100644 --- a/config/test.exs +++ b/config/test.exs @@ -2,10 +2,15 @@ use Mix.Config config :ueberauth, Ueberauth, providers: [ - vk: { Ueberauth.Strategy.VK, [] }, + vk: {Ueberauth.Strategy.VK, []} ] config :ueberauth, Ueberauth.Strategy.VK.OAuth, client_id: "appid", client_secret: "secret", redirect_uri: "/callback" + +config :exvcr, + vcr_cassette_library_dir: "test/fixtures/vcr_cassettes" + +config :plug, :validate_header_keys_during_test, true diff --git a/lib/ueberauth/strategy/vk.ex b/lib/ueberauth/strategy/vk.ex index 6f88fc0..6df53d8 100755 --- a/lib/ueberauth/strategy/vk.ex +++ b/lib/ueberauth/strategy/vk.ex @@ -1,17 +1,78 @@ defmodule Ueberauth.Strategy.VK do @moduledoc """ VK Strategy for Überauth. + + ### Setup + + Create an VK application for you to use. + + Register a new application at: [VK devs](https://vk.com/dev) and get the `client_id` and `client_secret`. + + Include the provider in your configuration for Ueberauth + + config :ueberauth, Ueberauth, + providers: [ + vk: { Ueberauth.Strategy.VK, [] } + ] + + Then include the configuration for github. + + config :ueberauth, Ueberauth.Strategy.VK.OAuth, + client_id: System.get_env("VK_CLIENT_ID"), + client_secret: System.get_env("VK_CLIENT_SECRET"), + + If you haven't already, create a pipeline and setup routes for your callback handler + + pipeline :auth do + Ueberauth.plug "/auth" + end + scope "/auth" do + pipe_through [:browser, :auth] + get "/:provider/callback", AuthController, :callback + end + + Create an endpoint for the callback where you will handle the `Ueberauth.Auth` struct + + defmodule MyApp.AuthController do + use MyApp.Web, :controller + + def callback_phase(%{ assigns: %{ ueberauth_failure: fails } } = conn, _params) do + # do things with the failure + end + def callback_phase(%{ assigns: %{ ueberauth_auth: auth } } = conn, params) do + # do things with the auth + end + end + + You can edit the behaviour of the Strategy by including some options when you register your provider. + + You can customize [multiple fields](https://vk.com/dev/auth_sites), such as `default_scope`, `default_display`, `default_state`, `profile_fields`, `uid_field` + + config :ueberauth, Ueberauth, + providers: [ + vk: { Ueberauth.Strategy.VK, [ + default_scope: "email,friends,video,offline", + default_display: "popup", + default_state: "secret-state-value", + uid_field: :email + ] } + ] + + Default is empty ("") which "Grants read-only access to public information (includes public user profile info, public repository info, and gists)" + """ - use Ueberauth.Strategy, default_scope: "", - default_display: "page", - profile_fields: "", - uid_field: :id, - allowed_request_params: [ - :display, - :scope - ], - api_version: "5.122" + use Ueberauth.Strategy, + default_scope: "", + default_display: "page", + default_state: "", + profile_fields: "", + uid_field: :id, + allowed_request_params: [ + :display, + :scope, + :state + ] alias OAuth2.{Response, Error, Client} alias Ueberauth.Auth.{Info, Credentials, Extra} @@ -31,10 +92,12 @@ defmodule Ueberauth.Strategy.VK do |> maybe_replace_param(conn, "auth_type", :auth_type) |> maybe_replace_param(conn, "scope", :default_scope) |> maybe_replace_param(conn, "display", :default_display) + # |> maybe_replace_param(conn, "state", :default_state) |> Enum.filter(fn {k, _} -> 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)) - |> OAuth.authorize_url! + |> with_state_param(conn) + |> OAuth.authorize_url!() redirect!(conn, authorize_url) @@ -43,7 +106,8 @@ defmodule Ueberauth.Strategy.VK do @doc """ Handles the callback from VK. """ - def handle_callback!(%Plug.Conn{params: %{"code" => code}} = conn) do + def handle_callback!(%Plug.Conn{params: %{"code" => _}} = conn) do + {code, state} = parse_params(conn) opts = [redirect_uri: callback_url(conn)] client = OAuth.get_token!([code: code], opts) token = client.token @@ -53,7 +117,7 @@ defmodule Ueberauth.Strategy.VK do desc = token.other_params["error_description"] set_errors!(conn, [error(err, desc)]) else - fetch_user(conn, client) + fetch_user(conn, client, state) end end @@ -69,7 +133,7 @@ defmodule Ueberauth.Strategy.VK do other_params = Map.put(token.other_params, "user_id", verified_token["user_id"]) token = Map.put(token, :other_params, other_params) put_private(conn, :vk_token, token) - fetch_user(conn, %{client | token: token}) + fetch_user(conn, %{client | token: token}, nil) else set_errors!(conn, [error("token", "Token verification failed")]) end @@ -104,9 +168,12 @@ defmodule Ueberauth.Strategy.VK do """ def credentials(conn) do token = conn.private.vk_token - scopes = String.split( - token.other_params["scope"] || "", "," - ) + + scopes = + String.split( + token.other_params["scope"] || "", + "," + ) %Credentials{ expires: token.expires_at == nil, @@ -151,6 +218,14 @@ defmodule Ueberauth.Strategy.VK do } end + defp parse_params(%Plug.Conn{params: %{"code" => code, "state" => state}}) do + {code, state} + end + + defp parse_params(%Plug.Conn{params: %{"code" => code}}) do + {code, nil} + end + defp fetch_name(user), do: user["first_name"] <> " " <> user["last_name"] defp fetch_image(user) do @@ -158,7 +233,7 @@ defmodule Ueberauth.Strategy.VK do user |> Enum.filter(fn {k, _v} -> String.starts_with?(k, "photo_") end) |> Enum.sort_by(fn {"photo_" <> size, _v} -> Integer.parse(size) end) - |> List.last + |> List.last() case user_photo do nil -> nil @@ -166,13 +241,18 @@ defmodule Ueberauth.Strategy.VK do end end - defp fetch_user(conn, client) do - conn = put_private(conn, :vk_token, client.token) + defp fetch_user(conn, client, state) do + conn = + conn + |> put_private(:vk_token, client.token) + |> put_private(:vk_state, state) + path = user_query(conn) case Client.get(client, path) do {:ok, %Response{status_code: 401, body: _body}} -> set_errors!(conn, [error("token", "unauthorized")]) + {:ok, %Response{status_code: status_code, body: user}} when status_code in 200..399 -> users = user["response"] @@ -195,8 +275,9 @@ defmodule Ueberauth.Strategy.VK do |> Map.merge(query_params(conn, :profile)) |> Map.merge(query_params(conn, :user_id)) |> Map.merge(query_params(conn, :access_token)) - |> Map.merge(query_params(conn, :api_version)) - |> URI.encode_query + |> Map.merge(query_params(conn, :version)) + |> URI.encode_query() + "https://api.vk.com/method/users.get?#{query}" end @@ -206,25 +287,26 @@ defmodule Ueberauth.Strategy.VK do fields -> %{"fields" => fields} end end + defp query_params(conn, :locale) do case option(conn, :locale) do nil -> %{} locale -> %{"lang" => locale} end end - defp query_params(conn, :api_version) do - case option(conn, :api_version) do - nil -> %{} - api_version -> %{"v" => api_version} - end - end + defp query_params(conn, :user_id) do %{"user_ids" => conn.private.vk_token.other_params["user_id"]} end + defp query_params(conn, :access_token) do %{"access_token" => conn.private.vk_token.access_token} end + defp query_params(_conn, :version) do + %{"v" => "5.124"} + end + defp option(conn, key) do default = Keyword.get(default_options(), key) @@ -232,6 +314,7 @@ defmodule Ueberauth.Strategy.VK do |> options |> Keyword.get(key, default) end + defp option(nil, conn, key), do: option(conn, key) defp option(value, _conn, _key), do: value @@ -248,7 +331,7 @@ defmodule Ueberauth.Strategy.VK do params = %{ "token" => token.access_token, "access_token" => config[:client_service_key], - "v" => option(conn, :api_version) + "v" => "5.124" } case OAuth2.Client.get(client, "/secure.checkToken", [], params: params) do {:ok, %OAuth2.Response{ diff --git a/lib/ueberauth/strategy/vk/oauth.ex b/lib/ueberauth/strategy/vk/oauth.ex index 460e625..9ad1e99 100755 --- a/lib/ueberauth/strategy/vk/oauth.ex +++ b/lib/ueberauth/strategy/vk/oauth.ex @@ -17,7 +17,7 @@ defmodule Ueberauth.Strategy.VK.OAuth do strategy: __MODULE__, site: "https://api.vk.com/method", authorize_url: "https://oauth.vk.com/authorize", - token_url: "https://oauth.vk.com/access_token", + token_url: "https://oauth.vk.com/access_token" ] @doc """ diff --git a/mix.exs b/mix.exs index 9ecc29c..2cdbad1 100755 --- a/mix.exs +++ b/mix.exs @@ -1,8 +1,8 @@ defmodule UeberauthVK.Mixfile do use Mix.Project - @version "0.2.6" - @url "https://github.com/sobolevn/ueberauth_vk" + @version "0.4.0" + @url "https://github.com/ueberauth/ueberauth_vk" def project do [ @@ -10,11 +10,9 @@ defmodule UeberauthVK.Mixfile do version: @version, name: "Ueberauth VK Strategy", package: package(), - elixir: "~> 1.2", - - build_embedded: Mix.env == :prod, - start_permanent: Mix.env == :prod, - + elixir: "~> 1.11", + build_embedded: Mix.env() == :prod, + start_permanent: Mix.env() == :prod, source_url: @url, homepage_url: @url, description: description(), @@ -27,44 +25,38 @@ defmodule UeberauthVK.Mixfile do # Test coverage: test_coverage: [tool: ExCoveralls], preferred_cli_env: [ - "coveralls": :test, + coveralls: :test, "coveralls.detail": :test, "coveralls.post": :test, - "coveralls.html": :test, - ], + "coveralls.html": :test + ] ] end def application do - [applications: [:logger, :oauth2, :ueberauth]] + [extra_applications: [:logger]] end defp deps do - [ - # Auth: - {:ueberauth, "~> 0.2"}, - {:oauth2, "~> 0.8.0"}, + [ + # Auth: + {:oauth2, "~> 1.0 or ~> 2.0"}, + {:ueberauth, "~> 0.10.0"}, - # Tests: - {:exvcr, "~> 0.8.4", only: :test}, - {:excoveralls, "~> 0.6", only: :test}, - {:poison, "~> 3.0", only: :test}, # is needed for tests + # Tests: + {:exvcr, "~> 0.10", only: :test}, + {:excoveralls, ">= 0.0.0", only: :test}, - # Docs: - {:ex_doc, "~> 0.14", only: :dev}, + # Docs: + {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, - # Lint: - {:credo, "~> 0.6", only: [:dev, :test]}, - ] + # Lint: + {:credo, "~> 0.8", only: [:dev, :test]} + ] end defp docs do - # Docs - [source_ref: "v#{@version}", - main: "README", - canonical: "http://hexdocs.pm/ueberauth_vk", - source_url: @url, - extras: ["README.md"]] + [extras: ["README.md"], main: "readme"] end defp description do @@ -72,9 +64,11 @@ defmodule UeberauthVK.Mixfile do end defp package do - [files: ["lib", "mix.exs", "README.md", "LICENSE.md"], - maintainers: ["Sobolev Nikita"], - licenses: ["MIT"], - links: %{"GitHub": @url}] + [ + files: ["lib", "mix.exs", "README.md", "LICENSE.md"], + maintainers: ["Sobolev Nikita"], + licenses: ["MIT"], + links: %{GitHub: @url} + ] end end diff --git a/mix.lock b/mix.lock old mode 100755 new mode 100644 index 1ef49f6..07f220f --- a/mix.lock +++ b/mix.lock @@ -1,24 +1,32 @@ -%{"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []}, - "certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []}, - "credo": {:hex, :credo, "0.6.0", "44a82f82b94eeb4ba6092c89b8a6730ca1a3291c7940739d5acc8806d25ac991", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]}, - "dogma": {:hex, :dogma, "0.1.7", "927f76a89a809db96e0983b922fc899f601352690aefa123529b8aa0c45123b2", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, optional: false]}]}, - "earmark": {:hex, :earmark, "1.1.1", "433136b7f2e99cde88b745b3a0cfc3fbc81fe58b918a09b40fce7f00db4d8187", [:mix], []}, - "ex_doc": {:hex, :ex_doc, "0.15.0", "e73333785eef3488cf9144a6e847d3d647e67d02bd6fdac500687854dd5c599f", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]}, - "exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [:mix], []}, - "excoveralls": {:hex, :excoveralls, "0.6.1", "9e946b6db84dba592f47632157ecd135a46384b98a430fd16007dc910c70348b", [:mix], [{:exjsx, "~> 3.0", [hex: :exjsx, optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, optional: false]}]}, - "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, - "exvcr": {:hex, :exvcr, "0.8.6", "96ab19fcb8b0031c7c703193c0cab061a2c7ba93c29733832d96c249ae2935e4", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, optional: false]}, {:exjsx, "~> 3.2", [hex: :exjsx, optional: false]}, {:httpoison, "~> 0.8", [hex: :httpoison, optional: true]}, {:httpotion, "~> 3.0", [hex: :httpotion, optional: true]}, {:ibrowse, "~> 4.2.2", [hex: :ibrowse, optional: true]}, {:meck, "~> 0.8.3", [hex: :meck, optional: false]}]}, - "hackney": {:hex, :hackney, "1.6.5", "8c025ee397ac94a184b0743c73b33b96465e85f90a02e210e86df6cbafaa5065", [: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.2", "a211a8e87403a043c41218e64df250d321f236ac57f786c6a0ccf3e9e817c819", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]}, - "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, - "jsx": {:hex, :jsx, "2.8.1", "1453b4eb3615acb3e2cd0a105d27e6761e2ed2e501ac0b390f5bbec497669846", [:mix, :rebar3], []}, - "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:make, :rebar], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "mimetype_parser": {:hex, :mimetype_parser, "0.1.2", "221d2d3f727e89d80de5e1610fc2ce444514aa56f873da1b8fc9c033143e5d6a", [:mix], []}, - "oauth2": {:hex, :oauth2, "0.8.3", "080ea0bf2f7060ddb66204ed756b8363d8c34f20fc5684183a16f19fc5d3ad97", [:mix], [{:hackney, "~> 1.6", [hex: :hackney, optional: false]}]}, - "plug": {:hex, :plug, "1.1.0", "69078a481549b81e879e169fbe099a509baf8d6a793db757f39f86258f91db07", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}]}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [: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.2.0", "9160c601b468c6692462a56be7ad8890d1a2d84519d7d9c4541da5b07d6495e1", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]}} +%{ + "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, + "credo": {:hex, :credo, "0.9.2", "841d316612f568beb22ba310d816353dddf31c2d94aa488ae5a27bb53760d0bf", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8394e6ad3f53b74a75013f79b8104c935add0247ec5dfcde7f444ffe509000c0"}, + "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, + "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, + "exactor": {:hex, :exactor, "2.2.4", "5efb4ddeb2c48d9a1d7c9b465a6fffdd82300eb9618ece5d34c3334d5d7245b1", [:mix], [], "hexpm", "1222419f706e01bfa1095aec9acf6421367dcfab798a6f67c54cf784733cd6b5"}, + "excoveralls": {:hex, :excoveralls, "0.12.2", "a513defac45c59e310ac42fcf2b8ae96f1f85746410f30b1ff2b710a4b6cd44b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "151c476331d49b45601ffc45f43cb3a8beb396b02a34e3777fea0ad34ae57d89"}, + "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm", "32e95820a97cffea67830e91514a2ad53b888850442d6d395f53a1ac60c82e07"}, + "exvcr": {:hex, :exvcr, "0.11.1", "a5e5f57a67538e032e16cfea6cfb1232314fb146e3ceedf1cde4a11f12fb7a58", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.1", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "984a4d52d9e01d5f0e28d45718565a41dffab3ac18e029ae45d42f16a2a58a1d"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [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.4.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", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "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"}, + "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm", "fc3499fed7a726995aa659143a248534adc754ebd16ccd437cd93b649a95091f"}, + "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"}, + "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, + "oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, + "tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"}, + "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, +} diff --git a/test/fixtures/cassettes/httpoison_get.json b/test/fixtures/vcr_cassettes/httpoison_get.json similarity index 66% rename from test/fixtures/cassettes/httpoison_get.json rename to test/fixtures/vcr_cassettes/httpoison_get.json index b35a4b2..2cdf428 100644 --- a/test/fixtures/cassettes/httpoison_get.json +++ b/test/fixtures/vcr_cassettes/httpoison_get.json @@ -1,4 +1,33 @@ [ + { + "request": { + "body": "client_id=appid&client_secret=secret&code=code_abc&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.example.com%2Fauth%2Fvk%2Fcallback&state=abc", + "headers": { + "Accept": "application/x-www-form-urlencoded", + "Content-Type": "application/x-www-form-urlencoded" + }, + "method": "post", + "options": [], + "request_body": "", + "url": "https://oauth.vk.com/access_token" + }, + "response": { + "body": "{\"access_token\": \"x\", \"user_ids\": 210700286, \"fields\": \"photo_50,city,verified\"}", + "headers": { + "Server": "Apache", + "Date": "Sun, 03 Jul 2016 19:28:36 GMT", + "Content-Type": "application/json; charset=utf-8", + "Content-Length": "69", + "Connection": "keep-alive", + "X-Powered-By": "PHP/3.2508", + "Set-Cookie": "remixlang=0; expires=Mon, 26 Jun 2017 04:09:35 GMT; path=/; domain=.vk.com", + "Pragma": "no-cache", + "Cache-control": "no-store" + }, + "status_code": 200, + "type": "ok" + } + }, { "request": { "body": "client_id=appid&client_secret=secret&code=code_abc&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fwww.example.com%2Fauth%2Fvk%2Fcallback", diff --git a/test/fixtures/vk_response.html b/test/fixtures/vk_response.html index 1f9cecb..8afb121 100644 --- a/test/fixtures/vk_response.html +++ b/test/fixtures/vk_response.html @@ -1 +1 @@ -You are being redirected. +You are being redirected. diff --git a/test/test_helper.exs b/test/test_helper.exs index af68290..8237743 100755 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -5,16 +5,16 @@ defmodule SpecRouter do require Ueberauth use Plug.Router - plug :fetch_query_params + plug(:fetch_query_params) - plug Ueberauth, base_path: "/auth" + plug(Ueberauth, base_path: "/auth") - plug :match - plug :dispatch + plug(:match) + plug(:dispatch) - get "/auth/vk", do: send_resp(conn, 200, "vk request") + get("/auth/vk", do: send_resp(conn, 200, "vk request")) - get "/auth/vk/callback", do: send_resp(conn, 200, "vk callback") + get("/auth/vk/callback", do: send_resp(conn, 200, "vk callback")) end ExUnit.start() diff --git a/test/ueberauth/strategy/vk_test.exs b/test/ueberauth/strategy/vk_test.exs index 12a4273..475e6d8 100644 --- a/test/ueberauth/strategy/vk_test.exs +++ b/test/ueberauth/strategy/vk_test.exs @@ -17,31 +17,30 @@ defmodule Ueberauth.Strategy.VKTest do # Setups: setup_all do - # Set the custom location for the cassettes: - ExVCR.Config.cassette_library_dir("test/fixtures/cassettes") - + # Creating token: token = %{other_params: %{"email" => @test_email}} # Read the fixture with the user information: {:ok, json} = "test/fixtures/vk.json" - |> Path.expand - |> File.read + |> Path.expand() + |> File.read() user_info = Poison.decode!(json) {:ok, response} = "test/fixtures/vk_response.html" - |> Path.expand - |> File.read + |> Path.expand() + |> File.read() response = String.replace(response, "\n", "") - {:ok, %{ - user_info: user_info, - token: token, - response: response, - }} + {:ok, + %{ + user_info: user_info, + token: token, + response: response + }} end # Tests: @@ -56,7 +55,7 @@ defmodule Ueberauth.Strategy.VKTest do end test "default callback phase" do - query = %{code: "code_abc"} |> URI.encode_query + query = %{code: "code_abc"} |> URI.encode_query() use_cassette "httpoison_get" do conn = @@ -70,7 +69,26 @@ defmodule Ueberauth.Strategy.VKTest do assert auth.provider == :vk assert auth.strategy == Ueberauth.Strategy.VK - assert auth.uid == 210700286 + assert auth.uid == 210_700_286 + end + end + + test "callback phase with state" do + query = %{code: "code_abc", state: "abc"} |> URI.encode_query() + + use_cassette "httpoison_get" do + conn = + :get + |> conn("/auth/vk/callback?#{query}") + |> SpecRouter.call(@router) + + assert conn.resp_body == "vk callback" + + auth = conn.assigns.ueberauth_auth + + assert auth.provider == :vk + assert auth.strategy == Ueberauth.Strategy.VK + assert auth.uid == 210_700_286 end end @@ -81,22 +99,22 @@ defmodule Ueberauth.Strategy.VKTest do conn = %Plug.Conn{ private: %{ vk_user: user_info, - vk_token: token, + vk_token: token } } assert info(conn) == %Info{ - email: @test_email, - name: "Lindsey Stirling", - first_name: "Lindsey", - last_name: "Stirling", - nickname: nil, - location: 5331, - description: "some info", - image: "100.jpg", - urls: %{ - vk: "https://vk.com/id210700286" - } - } + email: @test_email, + name: "Lindsey Stirling", + first_name: "Lindsey", + last_name: "Stirling", + nickname: nil, + location: 5331, + description: "some info", + image: "100.jpg", + urls: %{ + vk: "https://vk.com/id210700286" + } + } end end