JokenJwks.DefaultStrategyTemplate (Joken JWKS v1.6.0) View Source

A JokenJwks.SignerMatchStrategy template that has a window of time for refreshing its cache. This is a template and not a concrete implementation. You should use this module in order to use the default strategy.

This implementation is a task that should be supervised. It loops on a time window checking whether it should re-fetch keys or not.

Every time a bad kid is received it writes to an ets table a counter to 1. When the task loops, it polls for the counter value. If it is more than zero it starts re-fetching the cache. Upon successful fetching, it zeros the counter once again. This way we avoid overloading the JWKS server.

It will try to fetch signers when supervision starts it. This can be a sync or async operation depending on the value of first_fetch_sync. It defaults to false.

Resiliency

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 (future ones possible not implemented on the given OpenSSL + BEAM + JOSE set) it will also skip.

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.

Usage

This strategy must be under your apps' supervision tree. It must be explicitly used under a module so that you can have more than one JWKS source.

When using this strategy, there is an init_opts/1 callback that can be overridden. This is called upon supervision start. It should return a keyword list with all the options. This follows the standard practice of allowing a callback for using runtime configuration. It can override all other options as this has higher preference.

Configuration

Other than the init_opts/1 callback you can pass options through Mix.Config and when starting the supervisor. The order of preference in least significant order is:

  • Per environment Mix.Config
  • Supervisor child options
  • init_opts/1 callback

The only mandatory option is jwks_url (binary()) that is, usually, a runtime parameter like a system environment variable. It is recommended to use the init_opts/1 callback.

Other options are:

  • time_interval (integer() - default 60_000 (1 minute)): time interval for polling if it is needed to re-fetch the keys

  • log_level (:none | :debug | :info | :warn | :error - default :debug): the level of log to use for events in the strategy like HTTP errors and so on. It is advised not to turn off logging in production

  • should_start (boolean() - default true): whether to start the supervised polling task. For tests, this should be false

  • first_fetch_sync (boolean() - default false): whether to fetch the first time synchronously or async

  • explicit_alg (String.t()): the JWS algorithm for use with the key. Overrides the one in the JWK

  • http_max_retries_per_fetch (pos_integer() - default 10): passed to Tesla.Middleware.Retry

  • http_delay_per_retry (pos_integer() - default 500): passed to Tesla.Middleware.Retry

Examples

defmodule JokenExample.MyStrategy do
  use JokenJwks.DefaultStrategyTemplate

  def init_opts(opts) do
    url = # fetch url ...
    Keyword.merge(opts, jwks_url: url)
  end
end

defmodule JokenExample.Application do
  @doc false
  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      {MyStrategy, time_interval: 2_000}
    ]

    opts = [strategy: :one_for_one]
    Supervisor.start_link(children, opts)
  end
end

Then on your token configuration module:

defmodule MyToken do
  use Joken.Config

  add_hook(JokenJwks, strategy: MyStrategy)
  # rest of your token config
end