lib/assent/strategies/vk.ex

defmodule Assent.Strategy.VK do
  @moduledoc """
  VK.com OAuth 2.0 strategy.

  The VK token endpoint does not provide data on email verification, email is
  considered unverified.

  ## Configuration

  - `:user_url_params` - Parameters to send along with the user fetch request,
    optional, defaults to `[]`

  See `Assent.Strategy.OAuth2` for more.

  ## Usage

      config = [
        client_id: "REPLACE_WITH_CLIENT_ID",
        client_secret: "REPLACE_WITH_CLIENT_SECRET"
      ]
  """
  use Assent.Strategy.OAuth2.Base

  alias Assent.{Config, Strategy.OAuth2}

  @profile_fields ["uid", "first_name", "last_name", "photo_200", "screen_name"]
  @url_params     [fields: Enum.join(@profile_fields, ","), v: "5.69", https: "1"]

  @impl true
  def default_config(config) do
    params          = Config.get(config, :user_url_params, [])
    user_url_params = Config.merge(@url_params, params)

    [
      site: "https://api.vk.com",
      authorize_url: "https://oauth.vk.com/authorize",
      token_url: "https://oauth.vk.com/access_token",
      user_url: "/method/users.get",
      authorization_params: [scope: "email"],
      user_url_params: user_url_params,
      auth_method: :client_secret_post
    ]
  end

  @impl true
  def normalize(_config, user) do
    {:ok, %{
      "sub"                => user["id"],
      "given_name"         => user["first_name"],
      "family_name"        => user["last_name"],
      "picture"            => user["photo_200"],
      "email"              => user["email"]
    }}
  end

  @impl true
  def fetch_user(config, token) do
    params =
      config
      |> Config.get(:user_url_params, [])
      |> Config.put(:access_token, token["access_token"])

    config
    |> OAuth2.fetch_user(token, params)
    |> handle_user_response(token)
  end

  defp handle_user_response({:ok, %{"response" => [user]}}, token) do
    user =
      user
      |> Map.put_new("id", token["user_id"])
      |> Map.put_new("email", token["email"])

    {:ok, user}
  end
  defp handle_user_response({:ok, user}, _token),
    do: {:error, %Assent.RequestError{message: "Retrieved invalid response: #{inspect user}"}}
  defp handle_user_response({:error, error}, _token),
    do: {:error, error}
end