Skip to main content

lib/config.ex

defmodule Amur.Config do
  @moduledoc """
  Configuration resolver for OAuth providers.
  """

  @built_ins %{
    github: Amur.Providers.GitHub,
    google: Amur.Providers.Google,
    hackclub: Amur.Providers.HackClub
  }

  def resolve(provider) when is_binary(provider) do
    provider
    |> String.to_existing_atom()
    |> resolve()
  rescue
    ArgumentError -> {:error, :unknown_provider}
  end

  def resolve(provider) when is_atom(provider) do
    configured_providers = Application.get_env(:amur, :providers, [])

    case Keyword.fetch(configured_providers, provider) do
      {:ok, module} when is_atom(module) ->
        build_config(module, provider)

      {:ok, _credentials} ->
        case Map.fetch(@built_ins, provider) do
          {:ok, module} -> build_config(module, provider)
          :error -> {:error, :unknown_provider}
        end

      :error ->
        {:error, :unknown_provider}
    end
  end

  defp build_config(module, provider) do
    configured_providers = Application.get_env(:amur, :providers, [])
    base_url = Application.get_env(:amur, :base_url, "")
    credentials = Keyword.get(configured_providers, provider, [])

    {scopes, credentials} = Keyword.pop(credentials, :scopes)

    config =
      module.base_config()
      |> Keyword.merge(credentials)
      |> Keyword.put(:strategy, module.strategy())
      |> Keyword.put_new(:redirect_uri, "#{base_url}/auth/#{provider}/callback")
      |> merge_scopes(scopes)

    {:ok, {module, config}}
  end

  defp merge_scopes(config, nil), do: config

  defp merge_scopes(config, scopes) do
    Keyword.update(config, :authorization_params, [scope: scopes], fn params ->
      Keyword.put(params, :scope, scopes)
    end)
  end
end