JWT Common use cases
This is just a collection of common examples of working with tokens using Joken's API.
JWT simplest configuration
We will start from the simplest configuration possible:
defmodule Token do
use Joken.Config
end
With this configuration you can:
- Generate a token with
aud
,iss
,exp
,jti
,iat
,nbf
. - Validate a token with those claims.
- Use a default signer configuration.
Custom static iss
claim
defmodule Token do
use Joken.Config
@impl true
def token_config do
default_claims(skip: [:iss])
|> add_claim("iss", fn -> "My issuer" end, &(&1 == "My issuer"))
end
end
Since iss
is a default claim, we can pass that value to default_claims directly:
defmodule Token do
use Joken.Config
@impl true
def token_config do
default_claims(iss: "My custom issuer")
end
end
Custom dynamic aud
claim
In 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.
defmodule Token do
use Joken.Config
@impl true
def token_config do
default_claims(skip: [:aud])
|> add_claim("aud", nil, &(&1 in ["My audience", "Your audience", "Her audience"]))
end
end
# Defined at call site
Token.generate_and_sign(%{"aud" => "My audience"})
Custom validation cross claims
Sometimes 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.
defmodule Token do
use Joken.Config
@impl true
def token_config do
default_claims(skip: [:aud])
|> add_claim("aud", nil, &validate_audience/2)
end
defp validate_audience(value, claims) do
case claims["role"] do
"admin" ->
"http://myserver.com/admin"
"user" ->
"http://myserver.com"
end
end
end
Signer fetched from another server
It 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.
This 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.
We 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:
defmodule Token do
use Joken.Config, default_signer: nil # no signer
# This hook implements a before_verify callback that checks whether it has a signer configuration
# cached. If it does not, it tries to fetch it from the jwks_url.
add_hook(JokenJwks, jwks_url: "https://someurl.com")
@impl true
def token_config do
default_claims(skip: [:aud, :iss])
|> add_claim("roles", nil, &(&1 in ["admin", "user"]))
|> add_claim("iss", nil, &(&1 == "some server iss"))
|> add_claim("aud", nil, &(&1 == "some server aud"))
end
end
# We can call this by
Token.verify_and_validate(jwt)
Generate a token with the user id as subject
Another 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.
defmodule Token do
use Joken.Config
@impl true
def token_config do
default_claims()
|> add_claim("sub", nil, &is_valid_uuid/1)
end
# ... implementaion of UUID validation
end
# We can pass the subject as extra claims
Token.generate_and_sign(%{"sub" => "uuid"})