lib/firebase_auth.ex

defmodule FirebaseAuth do
  @moduledoc """
  Documentation for `FirebaseAuth`.
  """
  alias FirebaseAuth.PublicKeyServer

  @doc """
  Verify the accessToken/idToken obtained after logging in with Firebase
  """
  @spec verify_token(binary(), binary()) :: {:ok, map()} | {:error, term()}
  def verify_token(token, project_id) when is_binary(token) do
    with {:ok, %{"kid" => kid}} <- Joken.peek_header(token),
         {:ok, public_key} <- PublicKeyServer.get_key(kid),
         signer <- create_signer(public_key),
         token_config <- gen_token_config(project_id) do
      case Joken.verify_and_validate(token_config, token, signer) do
        {:ok, claims} -> {:ok, claims}
        {:error, _} = error -> error
      end
    end
  end

  defp gen_token_config(project_id) do
    %{}
    |> Joken.Config.add_claim("exp", nil, &(&1 > :os.system_time(:second)))
    |> Joken.Config.add_claim("iat", nil, &(&1 < :os.system_time(:second)))
    |> Joken.Config.add_claim("aud", nil, &(&1 == project_id))
    |> Joken.Config.add_claim("iss", nil, &(&1 == "https://securetoken.google.com/#{project_id}"))
    |> Joken.Config.add_claim("sub", nil, &(String.trim(&1) != ""))
    |> Joken.Config.add_claim("auth_time", nil, &(&1 < :os.system_time(:second)))
  end

  defp create_signer(key) do
    Joken.Signer.create("RS256", %{"pem" => key})
  end
end