# `Charon.TokenFactory.Jwt`
[🔗](https://github.com/weareyipyip/charon/blob/v4.3.0/lib/charon/token_factory/jwt.ex#L1)

JWT's with either symmetric (HMAC) or asymmetric (EDDSA) signatures.
The default, simplest and most performant option is symmetric signatures (MAC),
with the key derived from the Charon base secret.

Asymmetric tokens can be used when it is desirable for an external party
to be able to verify a token's integrity,
in which case distributing symmetric keys can be a hassle and a security risk.

## Keysets

In order to sign and verify JWT's, a keyset is used.
A keyset is a map of key ID's to keys.
A key is a tuple of the signing algorithm and the actual secret(s).
To simplify things and discourage key reuse,
a key can only be used with a single signing algorithm.
The default keyset looks like this, for example:

    %{"default" => {:hmac_sha256, <<0, ...>>}}

Every token that is signed gets a `"kid"` claim in its header, allowing it to
be verified with the specific key and algorithm that it was signed with.
For that reason, no two keys should have the same kid.

### Key cycling

It is possible to transition to a new signing key by adding a new key to the keyset
and setting it as the new signing key using the `:signing_key` config option:

    %{
      "default" => {:hmac_sha256, <<0, ...>>},
      "new!" => {:hmac_sha512, <<1, ...>>}
    }

Older tokens will be verified using the older key, based on their `"kid"` header claim.

> #### Warning: keyset caching {: .warning}
> The resolved keyset is cached in `m::persistent_term`, keyed by the `:signing_key` value.
> If you change the contents of your keyset (e.g. add or remove keys) **without** also
> changing `:signing_key`, the running node will continue using the old cached keyset
> until it is restarted. To force a cache refresh at runtime, change `:signing_key`.

### Tokens without a `"kid"` header claim

Legacy or external tokens may not have a `"kid"` header claim.
Such tokens can still be verified by adding
a `"kid_not_set.<alg>"` (for example "kid_not_set.HS256")
key to the keyset.

## Symmetric signatures

Symmetric signatures are message authentication codes or MACs,
either HMACs based on SHA256, 384 or 512,
or a MAC generated using Poly1305,
which can be used directly without using a HMAC wrapper.

By default, a SHA256-based HMAC is used.

## Asymmetric signatures

Asymmetric signatures are created using EDDSA (Edwards-curve Digital Signature Algorithm)
based on Curve25519 or Curve448.
Use in JWTs is standardized (pending) in [RFC 8073](https://datatracker.ietf.org/doc/rfc8037/).
These algorithms were chosen for performance and implementation ease,
since they offer built-in protection against many side-channel (timing) attacks and are
not susceptible to nonce-reuse (technically, they are, but not on the part of the implementation,
which means they are safe to use in your [PlayStation](https://en.wikipedia.org/wiki/EdDSA#Secure_coding)).
Unless you are paranoid, use Curve25519, which offers about 128 bits of security.
Curve448 offers about 224 bits, but is significantly slower.

In order to use asymmetric signatures, generate a key using `gen_keypair/1`.
Create a publishable JWK using `keypair_to_pub_jwk/1`.

## Config

Additional config is required for this module:

    Charon.Config.from_enum(
      ...,
      optional_modules: %{
        Charon.TokenFactory.Jwt => %{
          get_keyset: fn -> %{"key1" => {:hmac_sha256, "my_key"}} end,
          signing_key: "key1"
        }
      }
    )

The following options are supported:
  - `:get_keyset` (optional, default `default_keyset/1`). The keyset used to sign and verify JWTs. If not specified, a default keyset with a single key called "default" is used, which is derived from Charon's base secret. You should never use the same ID for different keys.
  - `:signing_key` (optional, default "default"). The ID of the key in the keyset that is used to sign new tokens.
  - `:gen_poly1305_nonce` (optional, default `:random`). How to generate Poly1305-signed JWT nonces, can be overridden by a 0-arity function that must return a 96-bits binary. It is of critical importance that the nonce is unique for each invocation. The default random generation provides adequate security for most applications (collision risk becomes significant only after ~2^48 tokens). For extremely high-volume applications, consider using a counter-based approach via this option, for example using [NoNoncense](`e:no_noncense:NoNoncense.html`).

## Examples

Generate and verify tokens with the default configuration:

    iex> {:ok, token} = sign(%{"user_id" => 123}, @charon_config)
    iex> {:ok, payload} = verify(token, @charon_config)
    iex> payload["user_id"]
    123

Generate an asymmetric keypair and create a publishable JWK:

    iex> keypair = Jwt.gen_keypair(:eddsa_ed25519)
    iex> {:eddsa_ed25519, {_pubkey, _privkey}} = keypair
    iex> %{"crv" => "Ed25519", "kty" => "OKP", "x" => <<_::binary>>} = Jwt.keypair_to_pub_jwk(keypair)

Use a custom keyset to rotate keys:

    iex> old_key = :crypto.strong_rand_bytes(32)
    iex> new_key = :crypto.strong_rand_bytes(32)
    iex> keyset = %{"old" => {:hmac_sha256, old_key}, "new" => {:hmac_sha512, new_key}}
    iex> old_config = Charon.TestHelpers.override_opt_mod_conf(@charon_config, Jwt, get_keyset: fn _ -> keyset end, signing_key: "old")
    iex> new_config = Charon.TestHelpers.override_opt_mod_conf(@charon_config, Jwt, get_keyset: fn _ -> keyset end, signing_key: "new")
    iex> {:ok, old_token} = sign(%{"uid" => 1}, old_config)
    iex> {:ok, new_token} = sign(%{"uid" => 2}, new_config)
    iex> {:ok, _} = verify(old_token, new_config)
    iex> {:ok, _} = verify(new_token, new_config)

# `eddsa_alg`

```elixir
@type eddsa_alg() :: :eddsa_ed25519 | :eddsa_ed448
```

# `eddsa_keypair`

```elixir
@type eddsa_keypair() :: {eddsa_alg(), {binary(), binary()}}
```

# `hmac_alg`

```elixir
@type hmac_alg() :: :hmac_sha256 | :hmac_sha384 | :hmac_sha512
```

# `key`

```elixir
@type key() :: symmetric_key() | eddsa_keypair()
```

# `keyset`

```elixir
@type keyset() :: %{required(String.t()) =&gt; key()}
```

# `mac_alg`

```elixir
@type mac_alg() :: :poly1305
```

# `symmetric_key`

```elixir
@type symmetric_key() :: {hmac_alg() | mac_alg(), binary()}
```

# `default_keyset`

```elixir
@spec default_keyset(Charon.Config.t()) :: keyset()
```

Get the default keyset that is used if config option `:get_keyset` is not set explicitly.

# `gen_keypair`

```elixir
@spec gen_keypair(eddsa_alg()) :: eddsa_keypair()
```

Generate a new keypair for an asymmetrically signed JWT.

# `keypair_to_pub_jwk`

```elixir
@spec keypair_to_pub_jwk(eddsa_keypair()) :: map()
```

Convert a keypair generated by `gen_keypair/1` to a publishable JWK
containing only the public key.

---

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