lib/firebase_auth/public_key_server.ex

defmodule FirebaseAuth.PublicKeyServer do
  use GenServer
  require Logger

  @interval 60 * 60

  @impl true
  def init(:ok) do
    updated_at = 0
    keys = %{}
    {:ok, %{updated_at: updated_at, keys: keys}}
  end

  @impl true
  def handle_call({:get_key, kid}, _from, %{updated_at: updated_at, keys: keys} = state) do
    if now() < @interval + updated_at do
      key = Map.fetch(keys, kid)

      {:reply, key, state}
    else
      Logger.info(fn -> "Refetching keys..." end)

      with {:ok, new_keys} <- FirebaseAuth.Client.get() do
        key = Map.fetch(new_keys, kid)
        new_state = %{updated_at: now(), keys: new_keys}
        {:reply, key, new_state}
      else
        {:error, error} ->
          Logger.warn(fn -> "Fetching  key ids error: #{error}" end)
          key = Map.fetch(keys, kid)

          {:reply, key, state}
      end
    end
  end

  @impl true
  def handle_info(_, state) do
    {:noreply, state}
  end

  # Client API
  def start_link(_opts) do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  @spec get_key(binary()) :: {:ok, binary()} | :error
  def get_key(kid) do
    GenServer.call(__MODULE__, {:get_key, kid})
  end

  defp now do
    :os.system_time(:second)
  end
end