Current section

Files

Jump to
yajwt lib jwt.ex
Raw

lib/jwt.ex

defmodule JWT do
@moduledoc """
Encode claims for transmission as a JSON object that is used as the payload of a JSON Web
Signature (JWS) structure, enabling the claims to be integrity protected with a Message
Authentication Code (MAC), to be later verified
see http://tools.ietf.org/html/rfc7519
"""
alias JWT.Jws
@default_algorithm "HS256"
@default_header %{typ: "JWT"}
# JOSE header types from: https://tools.ietf.org/html/rfc7515
@header_jose_keys [:alg, :jku, :jwk, :kid, :x5u, :x5c, :x5t, :"x5t#S256", :typ, :cty, :crit]
@doc """
Return a JSON Web Token (JWT), a string representing a set of claims as a JSON object that is
encoded in a JWS
## Example
iex> claims = %{iss: "joe", exp: 1300819380, "http://example.com/is_root": true}
...> key = "gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C"
...> JWT.sign(claims, key: key)
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiZXhwIjoxMzAwODE5MzgwfQ.Ktfu3EdLz0SpuTIMpMoRZMtZsCATWJHeDEBGrsZE6LI"
see http://tools.ietf.org/html/rfc7519#section-7.1
"""
@spec sign(map, Keyword.t | map) :: binary
def sign(claims, options) when is_map(claims) do
header = unify_header(options)
jws_message(header, Poison.encode!(claims), options[:key])
end
defp jws_message(%{alg: "none"} = header, payload, _key) do
Jws.unsecured_message(header, payload)
end
defp jws_message(header, payload, key) do
Jws.sign(header, payload, key)
end
@doc """
Given an options map, return a map of header options
## Example
iex> JWT.unify_header(alg: "RS256", key: "key")
%{typ: "JWT", alg: "RS256"}
Filters out unsupported claims options and ignores any encryption keys
"""
@spec unify_header(Keyword.t | map) :: map
def unify_header(options) when is_list(options) do
options |> Map.new |> unify_header
end
def unify_header(options) when is_map(options) do
jose_registered_headers = Map.take(options, @header_jose_keys)
@default_header
|> Map.merge(jose_registered_headers)
|> Map.merge(%{alg: algorithm(options)})
end
@doc """
Return a tuple {:ok, claims (map)} if the JWT signature is verified,
or {:error, "invalid"} otherwise
## Example
iex> jwt ="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiam9lIiwiaHR0cDovL2V4YW1wbGUuY29tL2lzX3Jvb3QiOnRydWUsImRhdGV0aW1lIjoxMzAwODE5MzgwfQ.8CbXtOJ51MfPLlNTDpMMBHExFZGmqIC2c_hjuY0Dp24"
...> key = "gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C"
...> JWT.verify(jwt, %{key: key})
{:ok, %{"name" => "joe", "datetime" => 1300819380, "http://example.com/is_root" => true}}
see http://tools.ietf.org/html/rfc7519#section-7.2
"""
@spec verify(binary, binary | map | Keyword.t) :: {:ok, map} | {:error, binary}
def verify(jwt, options) do
with {:ok, jws} <- Jws.verify(jwt, algorithm(options), unify_key(options)),
claims <- extract_payload!(jws),
:ok <- JWT.Claim.verify(claims, options)
do
{:ok, claims}
else
{:error, reason} -> {:error, reason}
end
end
@spec verify!(binary, binary | map | Keyword.t) :: map | no_return
def verify!(jwt, options) do
jws = Jws.verify!(jwt, algorithm(options), unify_key(options))
claims = extract_payload!(jws)
with :ok <- JWT.Claim.verify(claims, options) do
claims
else
{:error, rejected_claims} ->
raise JWT.ClaimValidationError, type: rejected_claims
end
end
defp algorithm(options) do
Map.get(options, :alg, @default_algorithm)
end
defp unify_key(binary) when is_binary(binary), do: binary
defp unify_key(options), do: options[:key]
defp extract_payload!(jws) do
[_, encoded_payload, _] = String.split(jws, ".")
JWT.Coding.decode!(encoded_payload)
end
end