Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use GenServer instead of Task for the default strategy process #36

Merged
merged 5 commits into from
Sep 14, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions lib/joken_jwks/default_strategy_template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()},
Expand All @@ -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
Expand All @@ -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),
Expand Down