Current section
Files
Jump to
Current section
Files
lib/hmac_auth_ex.ex
defmodule HMACAuth do
@spec sign(%{ secret: binary, method: binary, request_id: binary, path: binary, data: binary }) :: binary
def sign(%{ secret: secret, method: method, request_id: request_id, path: path, data: data } = args) do
timestamp = args[:timestamp] || utc_timestamp()
:crypto.hmac(
:sha,
secret,
[method, request_id, path, data, timestamp] |> Enum.join("-")
) |> Base.encode16(case: :lower)
end
@spec verify(map()) :: integer()
def verify(%{ signature: signature } = args) do
# Welcome, time travellers!
# In case the clock of the server signing the request is ahead by more than the
# time it takes to receive the request, we could see failed verifications signatures
# made around the time the second rolls over. Advance a bit into the future
# (and compensate in the ttl) to prevent this.
drift = args[:drift] || default_drift()
ttl = (args[:ttl] || default_ttl()) + drift
Stream.iterate(utc_timestamp() + drift, &(&1 - 1))
|> Enum.take(ttl)
|> Enum.find(fn (ts) ->
sign(Map.drop(%{ args | timestamp: ts }, [:signature, :ttl])) == signature
end)
end
@spec utc_timestamp() :: integer()
def utc_timestamp do
:os.system_time(:seconds)
end
@spec default_drift() :: integer()
defp default_drift do
Application.get_env(:hmac_auth_ex, :drift, 5)
end
@spec default_ttl() :: integer()
defp default_ttl do
Application.get_env(:hmac_auth_ex, :ttl, 5)
end
end