diff --git a/lib/joken_jwks/default_strategy_template.ex b/lib/joken_jwks/default_strategy_template.ex index 0d42c922..812e1e21 100644 --- a/lib/joken_jwks/default_strategy_template.ex +++ b/lib/joken_jwks/default_strategy_template.ex @@ -20,13 +20,13 @@ defmodule JokenJwks.DefaultStrategyTemplate do This strategy tries to be smart about keys it can USE to verify signatures. For example, if the provider has encryption keys, it will skip those (any key with field "use" with value "enc"). - Also, if the running BEAM instance has no support for a given signature algorithm (possibly not implemented + Also, if the running BEAM instance has no support for a given signature algorithm (possibly not implemented on the given OpenSSL + BEAM + JOSE combination) this implementation will also skip those. Be sure to check your logs as if there are NO signers available it will log a warning telling you that. - For debugging purpouses, calling the function `fetch_signers/2` directly might be helpful. + For debugging purpouses, calling the function `fetch_signers/2` directly might be helpful. ## Usage @@ -110,7 +110,7 @@ defmodule JokenJwks.DefaultStrategyTemplate do defmacro __using__(_opts) do # credo:disable-for-next-line quote do - use Task, restart: :transient + use GenServer, restart: :transient require Logger @@ -194,25 +194,41 @@ defmodule JokenJwks.DefaultStrategyTemplate do |> Keyword.put(:jwks_url, url) |> Keyword.put(:telemetry_prefix, telemetry_prefix) |> Keyword.put(:jws_supported_algs, algs) + |> Keyword.put(:should_start, start?) + |> Keyword.put(:first_fetch_sync, first_fetch_sync) - do_init(start?, first_fetch_sync, opts) + GenServer.start_link(__MODULE__, opts, name: __MODULE__) + end + + # Server (callbacks) + @impl true + def init(opts) do + {should_start, opts} = Keyword.pop!(opts, :should_start) + {first_fetch_sync, opts} = Keyword.pop!(opts, :first_fetch_sync) + do_init(should_start, first_fetch_sync, opts) end defp do_init(should_start, first_fetch_sync, opts) do cond do should_start and first_fetch_sync -> fetch_signers(opts[:jwks_url], opts) - Task.start_link(__MODULE__, :poll, [opts]) + {:ok, opts, {:continue, :start_poll}} should_start -> - {:ok, _} = start_fetch_signers(opts[:jwks_url], opts) - Task.start_link(__MODULE__, :poll, [opts]) + fetch_signers(opts[:jwks_url], opts) + {:ok, opts, {:continue, :start_poll}} true -> - {:ok, spawn_link(fn -> "Normal shutdown" end)} + :ignore end end + @impl true + def handle_continue(:start_poll, state) do + schedule_check_fetch(state) + {:noreply, state} + end + @impl SignerMatchStrategy def match_signer_for_kid(kid, opts) do with {:cache, [{:signers, signers}]} <- {:cache, EtsCache.get_signers()}, @@ -231,16 +247,18 @@ defmodule JokenJwks.DefaultStrategyTemplate do end end + defp schedule_check_fetch(state) do + interval = state[:time_interval] + Process.send_after(self(), :check_fetch, interval) + end + @doc false - def poll(opts) do - interval = opts[:time_interval] - - receive do - after - interval -> - _ = check_fetch(opts) - poll(opts) - end + @impl true + def handle_info(:check_fetch, state) do + check_fetch(state) + schedule_check_fetch(state) + + {:noreply, state} end defp check_fetch(opts) do @@ -253,14 +271,10 @@ defmodule JokenJwks.DefaultStrategyTemplate do # start re-fetching _counter -> Logger.debug("Re-fetching cache is needed and will start.") - start_fetch_signers(opts[:jwks_url], opts) + fetch_signers(opts[:jwks_url], opts) end end - defp start_fetch_signers(url, opts) do - Task.start(fn -> fetch_signers(url, opts) end) - end - @doc "Fetch signers with `JokenJwks.HttpFetcher`" def fetch_signers(url, opts) do with {:ok, keys} <- HttpFetcher.fetch_signers(url, opts),