Skip to content

Moving from 0.15 to 0.16

Bryan Joseph edited this page Sep 11, 2015 · 3 revisions

Joken 0.16 introduces a new API. The goal of the API is to better follow the spec and make it easier for users and tool makers to use it. Below are some examples of how to move from 0.15 to 0.16

The biggest change is that 0.16 moves to a functional composition styled API. This makes it easier to add claims and validations when needed.

Configuration

In 0.15, there was a Joken.Config module that was needed that had functions for items needed by the Joken module such as secret_key, algorithm, encode, decode, claim, and validate_claim which was added to a config block.

  defmodule My.Config.Module do
    @behaviour Joken.Config

    def secret_key(), do: Application.get_env(:app, :secret_key) 

    def algorithm(), do: :HS256

    def encode(map), do: Poison.encode!(map)

    def decode(binary), do: Poison.decode!(binary)

    def claim(:exp, payload) do
      Joken.Helpers.get_current_time() + 300
    end

    def claim(_, _), do: nil

    def validate_claim(:exp, payload, options) do
      Joken.Helpers.validate_time_claim(payload, "exp", "Token expired", fn(expires_at, now) -> expires_at > now end)
    end

    def validate_claim(_, _, _), do: :ok
  end

And then finally you could encode and decode tokens

{:ok, token} = Joken.encode(%{username: "johndoe"})
{:ok, decoded_payload} = Joken.decode(jwt)

There were several problems with this approach:

  1. It forced users to define functions for adding and validating claims that are predefined in the spec. These are not necessary and JWT spec allows users to use any claims they want.
  2. This assumed the secret_key is constant and a binary. It is possible for neither of those to be true.
  3. Going back to claims, this setup loses context in some situations where it is needed to accurately provide claims.
  4. encode and decode are the wrong terms for the operations that are happening

0.16 fixes these issues and has several advantages as well:

  1. A functional composition styled api means that you can apply certain claims or properties on the token when needed or when the context is available. It also means you can add your own custom functions within the pipeline.
  2. You are no longer forced to worry about claims that you are not using
  3. encode is now sign and decode is now verify

Configuration ahead of time is no longer needed. To achieve the same kind of functionality you would do the following

import Joken

#signing
compact_token = token(%{username: "johndoe"})
|> with_json_module(Poison) #Poison is now the default
|> with_exp(some_time)
|> sign(hs256(Application.get_env(:app, :secret)))
|> get_compact

#verifying
my_claims = compact_token
|> token
|> with_validation("exp", &(&1 > current_time))
|> with_validation("username", &(&1 == "johndoe"))
|> verify(hs256(Application.get_env(:app, :secret)))
|> get_claims

The token/0 and token/1 functions return a Joken.Token struct. This struct is the first parameter in all of the following calls and the return value of them all as well. Only get_compact/1, get_claims/1 and verify!/3 return something other than a Joken.Token struct

Going back to the above example, if you only cared about the username claim, and wanted to use Poison as default, you can boil down the code to the following:

import Joken

#signing
compact_token = token(%{username: "johndoe"})
|> sign(hs256(Application.get_env(:app, :secret)))
|> get_compact

#verifying
my_claims = compact_token
|> token
|> with_validation("username", &(&1 == "johndoe"))
|> verify(hs256(Application.get_env(:app, :secret)))
|> get_claims

Using the same example again, if you did not want to add any claims initially and instead add them later you can do so

import Joken

#signing
compact_token = token
|> with_claim("username", "johndoe")
|> sign(hs256(Application.get_env(:app, :secret)))
|> get_compact

#verifying
my_claims = compact_token
|> token
|> with_validation("username", &(&1 == "johndoe"))
|> verify(hs256(Application.get_env(:app, :secret)))
|> get_claims

Algorithms

Joken now accepts the following algorithms. There are convenience functions for each one. We used the hs256/1 function earlier:

  • ES256
  • ES384
  • ES512
  • HS256
  • HS384
  • HS512
  • PS256
  • PS384
  • PS512
  • RS256
  • RS384
  • RS512

The convenience functions create a Joken.Signer struct that contain a property called jws that represents the JSON Web Signature and jwk which represent the JSON Web Key.

Joken.Signer can be applied to the Joken.Token using the with_signer/2 function or as a parameter to any of the sign or verify functions

With the previous example, using the with_signer function instead would look like this:

#signing
compact_token = token
|> with_claim("username", "johndoe")
|> with_signer(hs256(Application.get_env(:app, :secret))
|> sign
|> get_compact

#verifying
my_claims = compact_token
|> token
|> with_validation("username", &(&1 == "johndoe"))
|> with_signer(hs256(Application.get_env(:app, :secret))
|> verify
|> get_claims

Joken.Plug

Joken now includes a plug to be used in your web applications. This deprecates the plugJWT library and make it so the library and plug are always in synced. Information on the plug can be obtained from the docs or in the README.md file

Clone this wiki locally