From eb612b3dffc94eb413c7d3a65c041de9fca3f077 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Mon, 8 Aug 2022 22:30:26 -0700 Subject: [PATCH 1/4] UPDATE: use GenServer instead of Task for the default strategy process --- lib/joken_jwks/default_strategy_template.ex | 67 ++++++++++++++------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/lib/joken_jwks/default_strategy_template.ex b/lib/joken_jwks/default_strategy_template.ex index c9040cdc..5c160d29 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 @@ -75,6 +75,11 @@ defmodule JokenJwks.DefaultStrategyTemplate do - `http_delay_per_retry` (`pos_integer()` - default `500`): passed to `Tesla.Middleware.Retry` + - `name`: name of the GenServer. Default is __MODULE__. + It can be any acceptable values in [Name registration section](https://hexdocs.pm/elixir/1.13.4/GenServer.html#module-name-registration){:target=_blank} of the GenServer HexDoc + + - `ets_name`: name of the `:ets` table for `EtsCache`. Default is __MODULE__. Has to be atom. + ### Examples defmodule JokenExample.MyStrategy do @@ -114,7 +119,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 @@ -193,6 +198,9 @@ defmodule JokenJwks.DefaultStrategyTemplate do [_, _, {:jws, {:alg, algs}}] = JOSE.JWA.supports() + name = if is_nil(opts[:name]), do: __MODULE__, else: opts[:name] + ets_name = if is_nil(opts[:ets_name]), do: __MODULE__, else: opts[:ets_name] + opts = opts |> Keyword.put(:time_interval, time_interval) @@ -200,25 +208,42 @@ 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) + |> Keyword.put(:ets_name, ets_name) + + GenServer.start_link(__MODULE__, opts, name: name) + end - do_init(start?, first_fetch_sync, opts) + # Server (callbacks) + @impl true + def init(opts) do + {should_start, opts} = opts |> Keyword.pop!(:should_start) + {first_fetch_sync, opts} = opts |> Keyword.pop!(: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()}, @@ -237,16 +262,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 @@ -259,14 +286,10 @@ defmodule JokenJwks.DefaultStrategyTemplate do # start re-fetching _counter -> JokenJwks.log(:debug, opts[:log_level], "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 log_level = opts[:log_level] From e2b474d8ef733b8b4f279166206d0a2940e6e935 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Mon, 8 Aug 2022 23:20:59 -0700 Subject: [PATCH 2/4] REMOVE: ets_name (this is going to get complicated) --- lib/joken_jwks/default_strategy_template.ex | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/joken_jwks/default_strategy_template.ex b/lib/joken_jwks/default_strategy_template.ex index 5c160d29..e57f616a 100644 --- a/lib/joken_jwks/default_strategy_template.ex +++ b/lib/joken_jwks/default_strategy_template.ex @@ -78,8 +78,6 @@ defmodule JokenJwks.DefaultStrategyTemplate do - `name`: name of the GenServer. Default is __MODULE__. It can be any acceptable values in [Name registration section](https://hexdocs.pm/elixir/1.13.4/GenServer.html#module-name-registration){:target=_blank} of the GenServer HexDoc - - `ets_name`: name of the `:ets` table for `EtsCache`. Default is __MODULE__. Has to be atom. - ### Examples defmodule JokenExample.MyStrategy do @@ -199,7 +197,6 @@ defmodule JokenJwks.DefaultStrategyTemplate do [_, _, {:jws, {:alg, algs}}] = JOSE.JWA.supports() name = if is_nil(opts[:name]), do: __MODULE__, else: opts[:name] - ets_name = if is_nil(opts[:ets_name]), do: __MODULE__, else: opts[:ets_name] opts = opts @@ -210,7 +207,6 @@ defmodule JokenJwks.DefaultStrategyTemplate do |> Keyword.put(:jws_supported_algs, algs) |> Keyword.put(:should_start, start?) |> Keyword.put(:first_fetch_sync, first_fetch_sync) - |> Keyword.put(:ets_name, ets_name) GenServer.start_link(__MODULE__, opts, name: name) end From b81420142f9d5626f0d2f41f8cb3924389d797a4 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Mon, 8 Aug 2022 23:26:45 -0700 Subject: [PATCH 3/4] REMOVE: name option as EtsCache is not ready. So this is in line with setting up only one module for one strategy, and not useable for dynamic spinning up of strategies --- lib/joken_jwks/default_strategy_template.ex | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/joken_jwks/default_strategy_template.ex b/lib/joken_jwks/default_strategy_template.ex index e57f616a..c61c5a6b 100644 --- a/lib/joken_jwks/default_strategy_template.ex +++ b/lib/joken_jwks/default_strategy_template.ex @@ -75,9 +75,6 @@ defmodule JokenJwks.DefaultStrategyTemplate do - `http_delay_per_retry` (`pos_integer()` - default `500`): passed to `Tesla.Middleware.Retry` - - `name`: name of the GenServer. Default is __MODULE__. - It can be any acceptable values in [Name registration section](https://hexdocs.pm/elixir/1.13.4/GenServer.html#module-name-registration){:target=_blank} of the GenServer HexDoc - ### Examples defmodule JokenExample.MyStrategy do @@ -196,8 +193,6 @@ defmodule JokenJwks.DefaultStrategyTemplate do [_, _, {:jws, {:alg, algs}}] = JOSE.JWA.supports() - name = if is_nil(opts[:name]), do: __MODULE__, else: opts[:name] - opts = opts |> Keyword.put(:time_interval, time_interval) @@ -208,7 +203,7 @@ defmodule JokenJwks.DefaultStrategyTemplate do |> Keyword.put(:should_start, start?) |> Keyword.put(:first_fetch_sync, first_fetch_sync) - GenServer.start_link(__MODULE__, opts, name: name) + GenServer.start_link(__MODULE__, opts, name: __MODULE__) end # Server (callbacks) From 75f0952d99affbfe17579c28fcfa0e7b0e801a92 Mon Sep 17 00:00:00 2001 From: Seungjin Kim Date: Wed, 21 Sep 2022 08:25:00 -0700 Subject: [PATCH 4/4] Update lib/joken_jwks/default_strategy_template.ex Co-authored-by: Victor Oliveira Nascimento <376386+victorolinasc@users.noreply.github.com> --- lib/joken_jwks/default_strategy_template.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/joken_jwks/default_strategy_template.ex b/lib/joken_jwks/default_strategy_template.ex index c61c5a6b..a047427a 100644 --- a/lib/joken_jwks/default_strategy_template.ex +++ b/lib/joken_jwks/default_strategy_template.ex @@ -209,8 +209,8 @@ defmodule JokenJwks.DefaultStrategyTemplate do # Server (callbacks) @impl true def init(opts) do - {should_start, opts} = opts |> Keyword.pop!(:should_start) - {first_fetch_sync, opts} = opts |> Keyword.pop!(:first_fetch_sync) + {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