lib/verifier.ex

defmodule FirebaseJwt.Verifier do
  @moduledoc """
  a module that verifies JWT from Firebase

  Specificaiton:
  https://firebase.google.com/docs/auth/admin/verify-id-tokens?hl=ja
  """
  alias FirebaseJwt.PublicKeyStore
  alias JOSE.{JWK, JWT}

  def verify(token) do
    case Application.get_env(:firebase_jwt, :simulator_mode) do
      true -> verify_without_signing(token)
      _ -> verify_with_signing(token)
    end
  end

  defp verify_without_signing(token) do
    with {:ok, payload} <- token |> String.split(".") |> Enum.at(1) |> Base.decode64(padding: false),
         {:ok, json} <- Jason.decode(payload) do
      {:ok, json}
    else
      _ -> {:error, :invalid_token}
    end
  end

  defp verify_with_signing(token) do
    case(pem(token)) do
      nil ->
        {:error, :unknown_public_key}

      pem ->
        with {true, %JWT{fields: claims}, _} <- JWT.verify_strict(jwk(pem), ["RS256"], token) do
          {:ok, claims}
        else
          _ -> {:error, :signature_error}
        end
    end
  end

  defp jwk(pem) do
    JWK.from_pem(pem)
  end

  defp firebase_public_key_id(token) do
    %{fields: %{"kid" => kid}} = JWT.peek_protected(token)
    kid
  end

  defp pem(token) do
    token
    |> firebase_public_key_id()
    |> PublicKeyStore.get_public_key()
  end
end