# `Pasexto`
[🔗](https://github.com/alexkornitzer/pasexto/blob/v0.1.2/lib/pasexto.ex#L1)

A compliant and complete implementation of [PASETO](https://github.com/paseto-standard/paseto-rfc) and [PASERK](https://github.com/paseto-standard/paserk).

PASETO is everything you love about JOSE (JWT, JWE, JWS) without any of the
[many design deficits that plague the JOSE standards](https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid).

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](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries).

> **Caution:** Neither JWT nor PASETO were designed for
> [stateless session management](http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/).
> 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:

```elixir
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:

```elixir
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:

```elixir
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)
```

# `purpose`

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

# `version`

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

# `build`

```elixir
@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`

```elixir
@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`

```elixir
@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`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
