1# JWT Common use cases
2
3This is just a collection of common examples of working with tokens using Joken's API.
4
5## JWT simplest configuration
6
7We will start from the simplest configuration possible:
8
9``` elixir
10defmodule Token do
11  use Joken.Config
12end
13```
14
15With this configuration you can:
16
17- Generate a token with `aud`, `iss`, `exp`, `jti`, `iat`, `nbf`.
18- Validate a token with those claims.
19- Use a default signer configuration.
20
21## Custom static `iss` claim
22
23``` elixir
24defmodule Token do
25  use Joken.Config
26
27  @impl true
28  def token_config do
29    default_claims(skip: [:iss])
30    |> add_claim("iss", fn -> "My issuer" end, &(&1 == "My issuer"))
31  end
32end
33```
34
35Since `iss` is a default claim, we can pass that value to default_claims directly:
36
37``` elixir
38defmodule Token do
39  use Joken.Config
40
41  @impl true
42  def token_config do
43    default_claims(iss: "My custom issuer")
44  end
45end
46```
47
48## Custom dynamic `aud` claim
49
50In this scenario we don't want a function for generating the claim value. We will generate it according to some business context. So, we skip static generation BUT we provide the claim validation all the same.
51
52``` elixir
53defmodule Token do
54  use Joken.Config
55
56  @impl true
57  def token_config do
58    default_claims(skip: [:aud])
59    |> add_claim("aud", nil, &(&1 in ["My audience", "Your audience", "Her audience"]))
60  end
61end
62
63# Defined at call site
64Token.generate_and_sign(%{"aud" => "My audience"})
65```
66
67## Custom validation cross claims
68
69Sometimes you need the value of another claim to validate some other claim. For example, you need the value of the role claim to validate the audience. That is fine. The validate function can receive up to 3 arguments: 1) the claim value, 2) &1, all the claims, 3) &1, &2, context.
70
71``` elixir
72defmodule Token do
73  use Joken.Config
74
75  @impl true
76  def token_config do
77    default_claims(skip: [:aud])
78    |> add_claim("aud", nil, &validate_audience/2)
79  end
80
81  defp validate_audience(value, claims) do
82    case claims["role"] do
83      "admin" ->
84        "http://myserver.com/admin"
85
86      "user" ->
87        "http://myserver.com"
88    end
89  end
90end
91```
92
93## Signer fetched from another server
94
95It is common to use OpenID Connect authentication servers to federate login. In this scenario, the signer configuration is usually available in what is called a well known URL.
96
97This URL has a JWKS configuration. This tells the world that tokens generated by these servers can be validated with these keys. Normally, the key id is a claim in the token header.
98
99We can use Joken easily in this scenario. We can abstract all the logic of fetching the signer configuration from the URL in a Hook and configure our claims validation without generation. Let's see an example:
100
101``` elixir
102defmodule Token do
103  use Joken.Config, default_signer: nil # no signer
104
105  # This hook implements a before_verify callback that checks whether it has a signer configuration
106  # cached. If it does not, it tries to fetch it from the jwks_url.
107  add_hook(JokenJwks, jwks_url: "https://someurl.com")
108
109  @impl true
110  def token_config do
111    default_claims(skip: [:aud, :iss])
112    |> add_claim("roles", nil, &(&1 in ["admin", "user"]))
113    |> add_claim("iss", nil, &(&1 == "some server iss"))
114    |> add_claim("aud", nil, &(&1 == "some server aud"))
115  end
116end
117
118# We can call this by
119Token.verify_and_validate(jwt)
120```
121
122## Generate a token with the user id as subject
123
124Another common need is to generate a token with some specific data. We can even validate that data format when we receive a token. As an example, we will validate the claim "sub" as being a valid UUID but will not generate this value in Joken. This is just an example of a dynamic claim value.
125
126``` elixir
127defmodule Token do
128  use Joken.Config
129
130  @impl true
131  def token_config do
132    default_claims()
133    |> add_claim("sub", nil, &is_valid_uuid/1)
134  end
135
136  # ... implementation of UUID validation
137end
138
139# We can pass the subject as extra claims
140Token.generate_and_sign(%{"sub" => "uuid"})
141```
142