I am trying to figure out how to use Joken from the limited documentation on Hex. The only other references I can find seem very out of date.
I am able to create JWT’s that are read correctly by other systems like online JWT inspectors.
However, my understanding is running Joken.verify_and_validate() should return :error or something of that nature on expired Tokens.
I can assess the outputted claims map manually myself, but isn’t this the point of the function?
(1) Create claims map & token:
def create_token(user_name, duration_hrs) do
# start with empty map
token_config_map = %{}
# userID:
|> Joken.Config.add_claim("user_name", fn -> user_name end, &(&1 == user_name))
# issuer:
|> Joken.Config.add_claim("iss", fn -> "The Issuer" end, &(&1 == "The Issuer"))
# expiry:
|> Joken.Config.add_claim("exp", fn -> Joken.CurrentTime.OS.current_time() + (duration_hrs * 60 * 60) end, &(&1 < Joken.CurrentTime.OS.current_time() ) )
# generate claims
token_claims = Joken.generate_claims(token_config_map)
# return the unwrapped claims or nil if fails
token_claims = case token_claims do
{:ok, claims}-> claims
_ -> nil
end
#create signer or get if stored
signer = Joken.Signer.create("RS256", %{"pem" => My.Token.private_key})
# make the token
result = Joken.encode_and_sign(token_claims, signer)
case (result) do
{:ok, token, claims} -> token
_-> nil
end
This seems to all work okay. Though I don’t really understand what the point of the verification claims functions are. For example, we have add_claim("iss", fn -> "The Issuer" end, &(&1 == "The Issuer")).
Is this just to have the Joken.encode_and_sign check this is still valid at the time it runs? Why would this be necessary? How would this be tampered with at this stage? Seems pointless. The verification function logic &(&1 == "The Issuer") is not somehow encoded into the JWT, so what is the point of this at all?
In any case this seems to work and the token can be created and read correctly.
(2) Verify & Validate Token
I am not sure what the intended method of this is.
As far as I can tell, Joken.verify_and_validate just checks the encryption to make sure the key is correct and presumably that nothing is tampered with from payload vs. signature. Is that all that is intended? Or is it also supposed to be able to test the claims of the token like expiry? Or should we manually do that?
For example if you do:
def verify_token(token) do
#create or get signer to check
signer = Joken.Signer.create("RS256", %{"pem" => My.Token.public_key})
# Define your token configuration (WHAT IS THE POINT? DOES NOTHING)
config = %{
alg: "HS256", # wrong algorithm, should be RS256
claims: %{
"exp" => %{required: true}, # I have no idea if this is right
"iss" => "NOT THE ISSUER" # just to prove a point
}
}
verify_result = Joken.verify_and_validate(config, token, signer)
verify_result = case (verify_result) do
{:ok, claims_map}-> claims_map
_-> nil
end
IO.puts("CHECK TOKEN CLAIMS: " <> inspect(verify_result))
verify_result #returns claims map if succeeds or nil if not
end
This successfully gives you the claims map back, even for expired tokens or for empty config %{}, or even when we have “NOT THE ISSUER” for issuer or wrong “alg” and required for exp.
Is this correct? Or is it supposed to evaluate this things? Are we supposed to manually evaluate the claims map to make sure it has what we want and the exp is not past? What is the point or meaning of the “config” here?
eg. We can manually go through the claims map that is recovered after:
exp_date = Map.get(claims_map, "exp")
if exp_date < Joken.CurrentTime.OS.current_time() do
#EXPIRED TOKEN
end
Is that what we’re supposed to do here? Feels like that should not be the case.
I also tried in the verification code (2) like this:
token_config_map = %{} #empty
# issuer:
|> Joken.Config.add_claim("iss", fn -> "The Issuer" end, &(&1 == "The Issuer"))
# expiry:
|> Joken.Config.add_claim("exp", fn -> Joken.CurrentTime.OS.current_time() end, &(&1 < Joken.CurrentTime.OS.current_time() ) )
verify_result = Joken.verify_and_validate(token_config_map, token, signer)
But that also does not fail even when the Token is expired. My theory is the “config map” is to supply two sides of data - one side is the claims (to be used on creation) and the other side is the verification functions. But either way it is not verifying that I can tell.
Thanks for any clarification or help.






















