# `Joken.Hooks`
[🔗](https://github.com/joken-elixir/joken/blob/v2.7.0/lib/joken/hooks.ex#L1)

Behaviour for defining hooks into Joken's lifecycle.

Hooks are passed to `Joken` functions or added to `Joken.Config` through the
`Joken.Config.add_hook/2` macro. They can change the execution flow of a token configuration.

There are 2 kinds of hooks: before and after.

Both of them are executed in a reduce_while call and so must always return either:
  - `{:halt, ...}` -> when you want to abort execution (other hooks won't be called)
  - `{:cont, ...}` -> when you want to let other hooks execute

## Before hooks

A before hook receives as the first parameter its options and then a tuple with the input of
the function. For example, the `generate_claims` function receives the token configuration plus a
map of extra claims. Therefore, a `before_generate` hook receives:
  - the hook options or `[]` if none are given;
  - a tuple with two elements where the first is the token configuration and the second is the extra
  claims map;

The return of a before hook is always the input of the next hook. Say you want to add an extra claim
with a hook. You could do so like in this example:

    defmodule EnsureExtraClaimHook do
      use Joken.Hooks

      @impl true
      def before_generate(_hook_options, {token_config, extra_claims}) do
        {:cont, {token_config, Map.put(extra_claims, "must_exist", true)}}
      end
    end

You could also halt execution completely on a before hook. Just use the `:halt` return with an error
tuple:

    defmodule StopTheWorldHook do
      use Joken.Hooks

      @impl true
      def before_generate(_hook_options, _input) do
        {:halt, {:error, :stop_the_world}}
      end
    end

## After hooks

After hooks work similar then before hooks. The difference is that it takes and returns the result of the
operation. So, instead of receiving 2 arguments it takes three:
  - the hook options or `[]` if none are given;
  - the result tuple which might be `{:error, reason}` or a tuple with `:ok` and its parameters;
  - the input to the function call.

Let's see an example with `after_verify`. The verify function takes as argument the token and a signer. So,
an `after_verify` might look like this:

    defmodule CheckVerifyError do
      use Joken.Hooks
      require Logger

      @impl true
      def after_verify(_hook_options, result, input) do
        case result do
          {:error, :invalid_signature} ->
            Logger.error("Check signer!!!")
            {:halt, result}

          {:ok, _claims} ->
            {:cont, result, input}
        end
      end
    end

On this example we have conditional logic for different results.

## `Joken.Config`

When you create a module that has `use Joken.Config` it automatically implements
this behaviour with overridable functions. You can simply override a callback
implementation directly and it will be triggered when using any of the generated
functions. Example:

    defmodule HookToken do
      use Joken.Config

      @impl Joken.Hooks
      def before_generate(_options, input) do
        IO.puts("Before generating claims")
        {:cont, input}
      end
    end

Now if we call `HookToken.generate_claims/1` it will call our callback.

Also in `Joken.Config` there is an imported macro for adding hooks with options. Example:

    defmodule ManyHooks do
      use Joken.Config

      add_hook(JokenJwks, jwks_url: "http://someserver.com/.well-known/certs")
    end

For an implementation reference, please see the source code of `Joken.Hooks.RequiredClaims`

# `generate_input`

```elixir
@type generate_input() :: {Joken.token_config(), extra :: Joken.claims()}
```

# `halt_tuple`

```elixir
@type halt_tuple() :: {:halt, tuple()}
```

# `hook_options`

```elixir
@type hook_options() :: Keyword.t()
```

# `sign_input`

```elixir
@type sign_input() :: {Joken.claims(), Joken.Signer.t()}
```

# `validate_input`

```elixir
@type validate_input() :: {Joken.token_config(), Joken.claims(), context :: map()}
```

# `verify_input`

```elixir
@type verify_input() :: {Joken.bearer_token(), Joken.Signer.t()}
```

# `after_generate`

```elixir
@callback after_generate(hook_options(), Joken.generate_result(), generate_input()) ::
  {:cont, Joken.generate_result(), generate_input()} | halt_tuple()
```

Called after `Joken.generate_claims/3`

# `after_sign`

```elixir
@callback after_sign(
  hook_options(),
  {:ok, Joken.bearer_token()} | {:error, Joken.error_reason()},
  sign_input()
) :: {:cont, Joken.sign_result(), sign_input()} | halt_tuple()
```

Called after `Joken.encode_and_sign/3`

# `after_validate`

```elixir
@callback after_validate(
  hook_options(),
  Joken.validate_result(),
  validate_input()
) :: {:cont, Joken.validate_result(), validate_input()} | halt_tuple()
```

Called after `Joken.validate/4`

# `after_verify`

```elixir
@callback after_verify(
  hook_options(),
  Joken.verify_result(),
  verify_input()
) :: {:cont, Joken.verify_result(), verify_input()} | halt_tuple()
```

Called after `Joken.verify/3`

# `before_generate`

```elixir
@callback before_generate(hook_options(), generate_input()) ::
  {:cont, generate_input()} | halt_tuple()
```

Called before `Joken.generate_claims/3`

# `before_sign`

```elixir
@callback before_sign(hook_options(), sign_input()) ::
  {:cont, sign_input()} | halt_tuple()
```

Called before `Joken.encode_and_sign/3`

# `before_validate`

```elixir
@callback before_validate(hook_options(), validate_input()) ::
  {:cont, validate_input()} | halt_tuple()
```

Called before `Joken.validate/4`

# `before_verify`

```elixir
@callback before_verify(hook_options(), verify_input()) ::
  {:cont, verify_input()} | halt_tuple()
```

Called before `Joken.verify/3`

# `run_after_hook`

# `run_before_hook`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
