defmodule Auth0.Common.Management.TokenManager do
defmodule Store do
@registry :tokens_registry
@type key :: String.t()
@type token :: String.t()
@type expiration :: integer
@spec init() :: :ok
def init() do
_ = :ets.new(@registry, [:set, :protected, :named_table])
:ok
end
@spec get(key) :: {token, expiration} | nil
def get(key) do
case with_retry(fn -> :ets.lookup(@registry, key) end) do
[] -> nil
[{_key, value}] -> value
end
end
@spec put(key, {token, expiration}) :: :ok
def put(key, value) do
_ = with_retry(fn -> :ets.insert(@registry, {key, value}) end)
:ok
end
defp with_retry(func) do
try do
func.()
rescue
_ ->
_ = init()
func.()
end
end
end
alias Auth0.Config
alias Auth0.Authentication
alias Auth0.Entity.Token
alias Auth0.Authentication.Token.ClientCredentials
@type config :: Config.t()
@spec get_token(config) :: String.t() | nil
def get_token(%Config{token_cache_disabled: true} = config) do
case config |> request_token() do
%Token{access_token: access_token} -> access_token
_ -> nil
end
end
def get_token(%Config{clear_token_cache: clear_token_cache} = config) do
client_id = config |> Config.get_client_id()
_ = if clear_token_cache, do: client_id |> Store.put(nil)
token =
case client_id |> Store.get() do
{token, expiration_time_second} ->
if expired(system_time_second(), expiration_time_second) do
nil
else
token
end
_ ->
nil
end
if token |> is_nil do
case config |> request_token() do
%Token{access_token: access_token, expires_in: expires_in} ->
client_id |> Store.put({access_token, system_time_second() + expires_in})
access_token
_ ->
nil
end
else
token
end
end
defp system_time_second(), do: System.system_time(:second)
defp expired(system_time_second, expiration_time_second) do
system_time_second - 5 > expiration_time_second
end
defp request_token(config) do
%ClientCredentials.Params{
audience: config |> get_audience(),
client_id: config |> Config.get_client_id(),
client_secret: config |> Config.get_client_secret()
}
|> Authentication.token_by_client_credentials(config)
|> case do
{:ok, %Token{access_token: access_token} = token, _}
when access_token |> is_binary ->
token
_ ->
nil
end
end
defp get_audience(%Config{} = config), do: "https://#{Config.get_domain(config)}/api/v2/"
end