Current section

Files

Jump to
paseto lib paseto.ex
Raw

lib/paseto.ex

defmodule Paseto do
@moduledoc """
Main entry point for consumers. Will parse the provided payload and return a version
(currently only v1 and v2 exist) struct.
"""
alias Paseto.{Token, V1, V2}
@doc """
Handles parsing a paseto token
## Examples
iex> Paseto.parse("v1.local.dGVzdA==")
{:ok, %Paseto.V1{version: "v1", purpose: "local", payload: "test", footer: nil}}
"""
@spec parse(String.t()) :: {:ok, %Paseto.V1{}} | {:ok, %Paseto.V2{}} | {:error, String.t()}
def parse(token) do
case parse_token(token) do
{:ok, %{version: "v1"} = parsed_token} ->
{:ok, V1.from_token(parsed_token)}
{:ok, %{version: "v2"} = parsed_token} ->
{:ok, V2.from_token(parsed_token)}
{:ok, %{version: _}} ->
{:error, "Invalid token version. Only versions 1 & 2 are supported"}
{:error, _} = err ->
err
end
end
# TODO(ian): Add docstring and check out the typespec w/ exception
@spec parse!(String.t()) :: %Paseto.V1{} | %Paseto.V2{}
def parse!(payload) do
case parse(payload) do
{:ok, token} -> token
{:error, error_message} -> raise error_message
end
end
# TODO(ian): Add a docstring here explaining pieces.
@spec parse_token(String.t()) :: {:ok, %Token{}} | {:error, String.t()}
def parse_token(payload) do
case String.split(payload, ".") do
[version, purpose, payload, footer] ->
case Base.decode64(payload) do
{:ok, payload} ->
{:ok,
%Token{
version: version,
purpose: purpose,
payload: payload,
footer: footer
}}
:error ->
{:error, "Invalid (non-base64 encoded) payload in token."}
end
[version, purpose, payload] ->
case Base.decode64(payload) do
{:ok, payload} ->
{:ok,
%Token{
version: version,
purpose: purpose,
payload: payload,
footer: nil
}}
:error ->
{:error, "Invalid (non-base64 encoded) payload in token."}
end
_ ->
{:error, "Invalid token encountered during token parsing"}
end
end
end