Exact.TokenStore behaviour (exact_online v0.1.0)

Copy Markdown View Source

Behaviour for persisting the OAuth2 token pair.

Because Exact Online rotates the refresh token on every refresh, the token has to be stored somewhere that outlives the request. Exact.TokenStore.ETS is the built-in in-memory implementation; implement this behaviour to keep tokens in a database or a secrets manager instead.

lock/2 exists so that concurrent requests cannot refresh the same grant twice. Exact Online rejects a refresh that uses an already rotated refresh token, so a second concurrent refresh would break the grant. Implementations must run fun with the lock for key held, and may serialize more coarsely than per key. It is optional and defaults to running fun unsynchronized.

defmodule MyApp.ExactTokens do
  @behaviour Exact.TokenStore

  @impl true
  def fetch(user_id) do
    case MyApp.Repo.get(MyApp.ExactToken, user_id) do
      nil -> :error
      record -> {:ok, MyApp.ExactToken.to_token(record)}
    end
  end

  @impl true
  def put(user_id, token) do
    MyApp.ExactToken.upsert!(user_id, token)
    :ok
  end
end

Summary

Callbacks

Returns the stored token for key, or :error when there is none.

Runs fun while holding a lock for key.

Persists token under key.

Types

key()

@type key() :: term()

Callbacks

fetch(key)

@callback fetch(key()) :: {:ok, Exact.Token.t()} | :error

Returns the stored token for key, or :error when there is none.

lock(key, function)

(optional)
@callback lock(key(), (-> result)) :: result when result: term()

Runs fun while holding a lock for key.

put(key, t)

@callback put(key(), Exact.Token.t()) :: :ok | {:error, term()}

Persists token under key.