ExCrypto v0.10.0 ExCrypto.Token

Use ExCrypto.Token to create unforgeable HMAC tokens that expire after a TTL.

Tokens created with this module have the following properties:

  • unforgeable
  • expire after a given TTL
  • may contain useful information in the payload (e.g. user_id or permissions)
  • safe to use in HTTP headers or URLs (encoded with Base.url_encode64/1)

Basic usage

Often it's convenient to include a JSON Object as the payload. That way the data in the payload is available after the token is verified like this:

iex> payload = %{"user_id" => 12345}
iex> encoded_payload = Poison.encode!(payload)
iex> {:ok, secret} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, token} = ExCrypto.Token.create(encoded_payload, secret)
iex> ttl = (15 * 60)  # 15 minute TTL (in seconds)
iex> {:ok, verified_payload} = ExCrypto.Token.verify(token, secret, ttl)
iex> decoded_verified_payload = Poison.decode!(verified_payload)
iex> assert(decoded_verified_payload == payload)
iex> Map.get(decoded_verified_payload, "user_id")
12345

Notes

  • the payload is not encrypted, only base64 encoded, do not include secrets in the payload
  • do not create a new secret each time, it must be stored and kept secret
  • do not include the secret in the payload
  • store the secret in the config for your app if using one global secret
  • store the secret on a given record (e.g. user record) if using a unique secret for each user

Link to this section Summary

Functions

Generate a signed token that carries of timestamp of when it was signed

Like create/3 but raises an exception on error

Check if a given binary has the correct structure to be a token

Update the signature on an existing token

Verify a token. Ensure the signature is no older than the ttl

Like verify/4 but raises an exception on error

Link to this section Types

Link to this type

option()
option() ::
  {:divider, String.t()}
  | {:date_time,
     {{integer(), integer(), integer()}, {integer(), integer(), integer()}}}

Link to this type

options()
options() :: [option()]

Link to this type

payload()
payload() :: binary()

Link to this type

token()
token() :: binary()

Link to this section Functions

Link to this function

create(payload, secret, opts \\ [])
create(payload(), binary(), options()) :: {:ok, token()} | {:error, any()}

Generate a signed token that carries of timestamp of when it was signed.

Examples

iex> payload = "my binary payload"
iex> {:ok, secret} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, token} = ExCrypto.Token.create(payload, secret)
iex> ExCrypto.Token.is_token?(token)
true
Link to this function

create!(payload, secret, opts \\ [])
create!(payload(), binary(), options()) :: binary() | no_return()

Like create/3 but raises an exception on error.

Link to this function

is_token?(token)
is_token?(binary()) :: true | false

Check if a given binary has the correct structure to be a token.

This does not mean it is a valid token, only that it has all the parts of a token.

Examples

iex> payload = "my binary payload"
iex> {:ok, secret} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, token} = ExCrypto.Token.create(payload, secret)
iex> ExCrypto.Token.is_token?(token)
true
Link to this function

update(token, secret, ttl, opts \\ [])

Update the signature on an existing token.

This is useful if you want to have a token that expires quickly, but only if it is not being used.

For example, if you use these tokens in a cookie for authentication in a web app, you can update the token each time the user makes a request, and send the updated cookie in the response.

This way a user can be logged out after N minutes of inactivity without requiring the user to supply credentials every N minutes.

This is also useful if the payload is expensive to create in the first place.

Another important benefit is that since the token is rotated with each request stealing a token becomes much less valuable. It's not impossible, but because the token changes with each request old tokens are only good until their TTL expires.

Examples

iex> payload = "my binary payload"
iex> {:ok, secret} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, token} = ExCrypto.Token.create(payload, secret)
iex> ExCrypto.Token.is_token?(token)
true
iex> ttl = (15 * 60)  # 15 minute TTL (in seconds)
iex> {:ok, {update_token, update_payload}} = ExCrypto.Token.update(token, secret, ttl)
iex> update_payload == payload
true
iex> {:ok, verified_payload} = ExCrypto.Token.verify(update_token, secret, ttl)
iex> verified_payload == payload
true
Link to this function

verify(token, secret, ttl, opts \\ [])
verify(token(), binary(), integer(), options()) ::
  {:ok, payload()} | {:error, any()}

Verify a token. Ensure the signature is no older than the ttl.

Examples

iex> payload = "my binary payload"
iex> {:ok, secret} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, token} = ExCrypto.Token.create(payload, secret)
iex> ExCrypto.Token.is_token?(token)
true
iex> ttl = (15 * 60)  # 15 minute TTL (in seconds)
iex> {:ok, verified_payload} = ExCrypto.Token.verify(token, secret, ttl)
iex> verified_payload == payload
true
Link to this function

verify!(token, secret, ttl, opts \\ [])
verify!(token(), binary(), integer(), options()) :: binary() | no_return()

Like verify/4 but raises an exception on error.