defmodule IDToken do @moduledoc """ Entry point module for using this package. `verify/2` verifies JWT and returns claims. iex> IDToken.verify(token, module: IDToken.Firebase) {:ok, %{"aud" => "...", ... }} Callback module have to behave as `IDToken.Callback`. """ @type opts :: [ module: module() ] @spec verify(token :: String.t(), opts :: opts()) :: {:ok, map()} | {:error, term()} def verify(token, module: module) do with {:ok, headers = %{"alg" => alg}} <- Joken.peek_header(token), {:ok, cert} <- fetch_cert(module) do key = module.verification_key(cert, headers) signer = Joken.Signer.create(alg, %{"pem" => key}) Joken.verify(token, signer) else :error -> {:error, "invalid token #{token}"} error = {:error, _} -> error end end defp fetch_cert(module) do case IDToken.CertificateStore.get(module) do nil -> fetch_then_store_cert(module) cert -> {:ok, cert} end end defp fetch_then_store_cert(module) do with {:ok, cert} <- module.fetch_certificates() do IDToken.CertificateStore.put(module, cert) {:ok, cert} end end end