lib/prima_auth0_ex/local_token.ex

defmodule PrimaAuth0Ex.LocalToken do
  @moduledoc """
  Utilities to help working with `prima_auth0_ex` on a local environment, without the need to integrate with Auth0.
  """

  @signer Joken.Signer.create("HS256", "any-secret")

  @doc """
  Forge a token with the desired payload.
  Custom claims can be passed as keyword options.

  The signature will be generated locally, hence it will **not** be valid.
  For this reason, this function should be used for local development only.
  """
  @spec forge(String.t(), Keyword.t()) :: String.t()
  def forge(audience, extra_claims \\ []) do
    claims = merge_claims(default_claims(audience), extra_claims)

    Joken.generate_and_sign!(
      %{},
      claims,
      @signer
    )
  end

  @doc """
  Generate a UNIX timestamp shifted by the given time.
  Can be used to quickly generate timestamps for `iat` and `exp` claims.

  `options` must be compliant to `Timex.shift_options()`.
  E.g. `PrimaAuth0Ex.LocalToken.time_from_now(hours: -1)` will return the UNIX timestamp for one hour ago.
  """
  @spec time_from_now(Timex.shift_options()) :: non_neg_integer()
  def time_from_now(options) do
    Timex.now()
    |> Timex.shift(options)
    |> Timex.to_unix()
  end

  defp merge_claims(base_claims, extra_claims) do
    extra_claims =
      extra_claims
      |> Enum.map(fn {key, value} -> {Atom.to_string(key), value} end)
      |> Enum.into(%{})

    Map.merge(base_claims, extra_claims)
  end

  defp default_claims(audience) do
    %{"aud" => audience, "iat" => time_from_now(seconds: -1), "exp" => time_from_now(hours: 24), "iss" => issuer()}
  end

  defp issuer, do: Application.fetch_env!(:prima_auth0_ex, :server)[:issuer]
end