Pasexto (pasexto v0.1.1)

Copy Markdown View Source

A compliant and complete implementation of PASETO and PASERK.

PASETO is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.

PASETO (Platform-Agnostic SEcurity TOkens) is a specification and reference implementation for secure stateless tokens.

Paseto is pronounced paw-set-oh (pɔːsɛtəʊ).

Key Differences between Paseto and JWT

Unlike JSON Web Tokens (JWT), which gives developers more than enough rope with which to hang themselves, PASETO only allows secure operations. JWT gives you "algorithm agility", PASETO gives you "versioned protocols". It's incredibly unlikely that you'll be able to use Paseto in an insecure way.

Caution: Neither JWT nor PASETO were designed for stateless session management. Paseto is suitable for tamper-proof cookies, but cannot prevent replay attacks by itself.

PASETO

PASETO expects a version and a purpose in order to build and parse tokens. The version is a string that represents the current version of the protocol. The purpose is a short string describing the purpose of the token, where the accepted values are:

  • local: shared-key authenticated encryption

  • public: public-key digital signatures; not encrypted

Tokens can be built and parsed like so:

iex> key = Pasexto.Key.new(:v4, :local)
iex> claims = %{"hello" => "world"}
iex> footer = <<>>
iex> {:ok, paseto, claims} = Pasexto.build(:v4, :local, key, claims, footer)
iex> {:ok, ^claims, ^footer} = Pasexto.parse(:v4, :local, key, paseto)

iex> key = Pasexto.Key.new(:v4, :public)
iex> claims = %{"hello" => "world"}
iex> footer = <<>>
iex> {:ok, paseto, claims} = Pasexto.build(:v4, :public, key, claims, footer)
iex> {:ok, ^claims, ^footer} = Pasexto.parse(:v4, :public, key, paseto)

Keyring

This library also supports keyrings making it easier to manage a group of keys. It can be used like so:

iex> key = Pasexto.Key.new(:v4, :local)
iex> keyring = Pasexto.Keyring.new(:v4, :local) |> Pasexto.Keyring.put("key1", key)
iex> claims = %{"hello" => "world"}
iex> footer = %{"kid" => "key1"}
iex> {:ok, paseto, claims} = Pasexto.build(:v4, :local, keyring, claims, footer)
iex> {:ok, ^claims, ^footer} = Pasexto.parse(:v4, :local, keyring, paseto)

PASERK

PASERK is an extension to PASETO that provides key-wrapping and serialization. It can be used like so:

iex> key = Pasexto.Key.new(:v4, :local)
iex> {:ok, kid} = Pasexto.Paserk.build(:k4, :lid, key)
iex> keyring = Pasexto.Keyring.new(:v4, :local) |> Pasexto.Keyring.put(kid, key)
iex> claims = %{"hello" => "world"}
iex> footer = %{"kid" => kid}
iex> {:ok, paseto, claims} = Pasexto.build(:v4, :local, keyring, claims, footer)
iex> {:ok, ^claims, ^footer} = Pasexto.parse(:v4, :local, keyring, paseto)

Summary

Functions

Builds a PASETO token for the given version and purpose.

Parses a PASETO token for the given version and purpose.

Peek at the footer of a token.

Types

purpose()

@type purpose() :: :local | :public

version()

@type version() :: :v1 | :v2 | :v3 | :v4

Functions

build(version, purpose, key, claims, footer, opts \\ [])

@spec build(
  version(),
  purpose(),
  Pasexto.Key.t() | Pasexto.Keyring.t(),
  map(),
  binary() | map(),
  keyword()
) :: {:ok, binary(), map()} | {:error, term()}

Builds a PASETO token for the given version and purpose.

The key must be a Key.t() or Keyring.t() and it must match the given version and purpose. When a keyring is provided the footer must be a map and must contain a kid for a key within the keyring.

Options

  • defaults - Default claims to merge into the provided claims, it expects a keyword list where the value can be a static value or a function that takes no arguments, defaults to nil. If nil then the following claims are applied:

      [
        exp: fn -> DateTime.utc_now(:second) |> DateTime.shift(hour: 1) |> DateTime.to_iso8601() end,
        iat: fn -> DateTime.utc_now(:second) |> DateTime.to_iso8601() end,
        nbf: fn -> DateTime.utc_now(:second) |> DateTime.to_iso8601() end,
        jti: fn -> :crypto.strong_rand_bytes(16) |> Base.hex_encode32(case: :lower, padding: false) end
      ]
  • deprecated - Allows the usage of deprecated versions, defaults to false.

  • implicits - A map or binary of assertions to bind to the token without being included, defaults to nil.

  • rules - A collection of rules to verify the claims within a token, defaults to nil. If nil then the following rules are applied:

      [
        &Pasexto.Rules.validate_required(&1),
        &Pasexto.Rules.validate_exp(&1),
        &Pasexto.Rules.validate_iat(&1),
        &Pasexto.Rules.validate_nbf(&1)
      ]

parse(version, purpose, key, token, opts \\ [])

@spec parse(
  version(),
  purpose(),
  Pasexto.Key.t() | Pasexto.Keyring.t(),
  binary(),
  keyword()
) ::
  {:ok, map(), binary() | map()} | {:error, term()}

Parses a PASETO token for the given version and purpose.

Upon success it returns the claims and footer contained within the token.

The key must be a Key.t() or Keyring.t() and it must match the given version and purpose. When a keyring is provided the footer is checked to see if it contains a valid kid used to lookup the key within the keyring.

Options

  • deprecated - Allows the usage of deprecated versions, defaults to false.

  • implicits - A map or binary of assertions expected to have been bound to the token, defaults to nil.

  • max_depth - The maximum level of nesting allowed within a JSON footer object, defaults to 1.

  • max_keys - The maximum number of keys allowed within a JSON footer object, defaults to 10.

  • rules - A collection of rules to verify the claims within a token, defaults to nil. If nil then the following rules are applied:

      [
        &Pasexto.Rules.validate_required(&1),
        &Pasexto.Rules.validate_exp(&1),
        &Pasexto.Rules.validate_iat(&1),
        &Pasexto.Rules.validate_nbf(&1)
      ]

peek(version, purpose, token, opts \\ [])

@spec peek(version(), purpose(), binary(), keyword()) ::
  {:ok, binary() | map()} | {:error, term()}

Peek at the footer of a token.

Options

  • deprecated - Allows the usage of deprecated versions, defaults to false.

  • max_depth - The maximum level of nesting allowed within a JSON footer object, defaults to 1.

  • max_keys - The maximum number of keys allowed within a JSON footer object, defaults to 10.