lib/signing_keys_manager.ex

defmodule ExMicrosoftBot.SigningKeysManager do
  use ExMicrosoftBot.RefreshableAgent

  import ExMicrosoftBot.Client, only: [opts: 0, deserialize_response: 2]

  # Public API

  @doc """
  Get the token that can be used to authorize calls to Microsoft Bot Framework
  """
  def get_keys() do
    get_state()
  end

  def force_refresh_keys() do
    force_refresh_state()
  end

  # Refreshable Agent Callbacks

  def get_refreshed_state(_args, _old_state) do
    get_microsoft_bot_keys()
  end

  def time_to_refresh_after_in_seconds(_state) do
    # 5 Days to expire the signing keys
    # Round to_seconds to return in scientific notation
    5
    |> Timex.Duration.from_days()
    |> Timex.Duration.to_seconds()
    |> Kernel.round()
  end

  # Private

  defp get_microsoft_bot_keys() do
    with {:ok, %{"jwks_uri" => uri}} <- get_json_from_wellknown_key_uri(),
         {:ok, %{"keys" => keys}} <- get_json_from_uri(uri) do
      {:ok, Enum.map(keys, &JOSE.JWK.from_map/1)}
    else
      {:error, _, _} = error -> error
    end
  end

  defp get_json_from_wellknown_key_uri() do
    :ex_microsoftbot
    |> Application.get_env(:openid_valid_keys_url)
    |> get_json_from_uri()
  end

  defp get_json_from_uri(uri) do
    uri
    |> HTTPoison.get([], opts())
    |> deserialize_response(&Poison.decode!(&1, as: %{}))
  end
end