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