Skip to content

Instantly share code, notes, and snippets.

@tr00gle
Created August 23, 2019 23:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tr00gle/362ee1778f08515b56b8b8de99d6e1af to your computer and use it in GitHub Desktop.
Save tr00gle/362ee1778f08515b56b8b8de99d6e1af to your computer and use it in GitHub Desktop.
auth gen server
defmodule TransactionalMessagingService.Providers.Cheetah.Auth do
@moduledoc """
GenServer to handle all Cheetah auth token related functionality
All requests to Cheetah require using a valid auth token in the request
header, the tokens also expire after a certain time (currently 8 hours).
Setup a GenServer to keep a active token in its state and manage retrieval
of a new token when needed.
This is accomplished by storing the token value and the calculated expiration
time in the GenServer state, when a token is retrieved if it is valid it is
given right away, otherwise if it has expired a new token is retrieved and
placed into the state.
"""
use GenServer
require Logger
alias TransactionalMessagingService.DataDog
#############
# Client API - these methods run in the calling elixir process
#############
def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@timeout 8000
def get_token(shop) do
GenServer.call(__MODULE__, {:get_token, shop}, @timeout)
end
###################
# Server Callbacks - these run in a separate elixir process than the above
# client api
###################
def init(_) do
{:ok, nil, {:continue, :init}}
end
@token_creds # removed
def handle_continue(:init, nil) do
{:noreply,
%{
"www" => retrieve_new_token(@token_creds["www"]),
"uk" => retrieve_new_token(@token_creds["uk"]),
"au" => retrieve_new_token(@token_creds["au"]),
"ca" => retrieve_new_token(@token_creds["ca"])
}}
end
def handle_call({:get_token, shop}, _from, token_store) do
case token_store[shop] do
%{token: token, expiration: expiration} ->
if expiration < :os.system_time(:seconds) do
# fetch a new one and update the token_store
updated_shop_state = %{token: new_token} = retrieve_new_token(@token_creds[shop])
{:reply, new_token, Map.merge(token_store, %{shop => updated_shop_state})}
else
# reply with the token
{:reply, token, token_store}
end
nil ->
updated_shop_state = %{token: new_token} = retrieve_new_token(@token_creds[shop])
{:reply, new_token, Map.merge(token_store, %{shop => updated_shop_state})}
end
end
@endpoint # removed
defp retrieve_new_token({username, password}) do
case HTTPoison.post(
@endpoint,
"username=#{username}&" <>
"password=#{password}&" <>
"grant_type=password",
[{"Content-Type", "application/x-www-form-urlencoded"}]
) do
# Cheetah might return a error without a error status code,
# this causes the Poison.decode!() below to error out because
# it tries to parse the response as JSON when it is a HTML
# error page
{:ok, %HTTPoison.Response{body: response}} ->
response
|> Poison.decode!()
|> extract_new_token_info()
{:error, error} ->
DataDog.increment_metric("cheetah.auth_token_fetch_failure")
Logger.error("Error getting auth token: #{inspect(error)}")
nil
end
end
defp extract_new_token_info(%{"access_token" => token, "expires_in" => expiration}) do
DataDog.increment_metric("cheetah.auth_token_fetch_success")
%{token: token, expiration: :os.system_time(:seconds) + (expiration - 300)}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment