defmodule Rivet.Auth.Token do
# Break out the claims of a JWT, without any validation
@doc """
iex> jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJjYWExOmFjYzpleGFtcGxlLmNvbSIsImV4cCI6MTY3MTkyMjMwMCwiZm9yIjp7fSwic3ViIjoiY2FzMTpuYXJmIn0.N6PV_XAGTjymi1CEfVkKRj433S0XBlTxcevL7VAhTRY"
iex> extract(jwt, :all)
{:ok, %{"alg" => "HS256","typ" => "JWT"}, %{aud: "caa1:acc:example.com", exp: 1671922300, for: %{}, sub: "cas1:narf"}}
iex> extract(jwt, :claims)
{:ok, %{aud: "caa1:acc:example.com", exp: 1671922300, for: %{}, sub: "cas1:narf"}}
iex> extract(jwt, :header)
{:ok, %{"alg" => "HS256","typ" => "JWT"}}
iex> extract("asdf", :all)
{:error, "Invalid JWT, cannot extract claims"}
iex> extract("asdf.asdf.asdf", :all)
{:error, "Unable to decode JWT part: asdf"}
"""
def extract(token, part \\ :claims)
def extract(token, :claims) do
case String.split(token, ".", parts: 3) do
[_, claims, _] -> decode_part(claims, true)
_ -> {:error, "Invalid JWT, cannot extract claims"}
end
end
def extract(token, :header) do
case String.split(token, ".", parts: 2) do
[header, _] -> decode_part(header, false)
_ -> {:error, "Invalid JWT, cannot extract claims"}
end
end
def extract(token, :all) do
case String.split(token, ".", parts: 3) do
[header, claims, _] ->
with {:ok, header} <- decode_part(header, false),
{:ok, claims} <- decode_part(claims, true) do
{:ok, header, claims}
end
_ ->
{:error, "Invalid JWT, cannot extract claims"}
end
end
defp decode_part(data, as_atom) do
case {Base.decode64!(data, padding: false) |> Jason.decode(), as_atom} do
{{:ok, claims}, true} -> {:ok, Transmogrify.transmogrify(claims)}
{{:ok, claims}, false} -> {:ok, claims}
{{:error, _msg}, _} -> {:error, "Unable to decode JWT part: #{data}"}
end
end
end