Packages

Elixir client for Medplum's FHIR API with resource and workflow helpers.

Current section

Files

Jump to
medplum_elixir lib medplum token_cache.ex
Raw

lib/medplum/token_cache.ex

defmodule Medplum.TokenCache do
@moduledoc """
Internal ETS-backed cache for OAuth tokens shared by Medplum clients.
"""
alias Medplum.Client
@table :medplum_token_cache
@spec get(Client.t()) :: {:ok, String.t()} | :miss
def get(%Client{cache_tokens: false}), do: :miss
def get(%Client{} = client) do
ensure_table()
case :ets.lookup(@table, cache_key(client)) do
[{_, token, expires_at}] ->
if fresh?(expires_at, client.token_refresh_skew), do: {:ok, token}, else: :miss
[] ->
:miss
end
end
@spec put(Client.t(), String.t(), integer() | nil) :: :ok
def put(%Client{cache_tokens: false}, _token, _expires_at), do: :ok
def put(%Client{} = client, token, expires_at) do
ensure_table()
true = :ets.insert(@table, {cache_key(client), token, expires_at})
:ok
end
defp cache_key(%Client{} = client) do
{client.base_url, client.client_id, :erlang.phash2(client.client_secret), client.fhir_version}
end
defp fresh?(nil, _skew), do: true
defp fresh?(expires_at, skew) do
System.system_time(:second) + skew < expires_at
end
defp ensure_table do
case :ets.whereis(@table) do
:undefined ->
try do
:ets.new(@table, [:named_table, :public, :set, read_concurrency: true])
rescue
ArgumentError -> :ok
end
_table ->
:ok
end
:ok
end
end