diff --git a/lib/goth/token.ex b/lib/goth/token.ex index 74fbb23..356d7ec 100644 --- a/lib/goth/token.ex +++ b/lib/goth/token.ex @@ -53,6 +53,9 @@ defmodule Goth.Token do defstruct [:token, :type, :scope, :sub, :expires, :account] + # time in seconds before token will be auto renewed + @timeout 120 + @doc """ Get a `%Goth.Token{}` for a particular `scope`. `scope` can be a single scope or multiple scopes joined by a space. See [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/googlescopes) for all available scopes. @@ -137,13 +140,13 @@ defmodule Goth.Token do def queue_for_refresh(%__MODULE__{} = token) do diff = token.expires - :os.system_time(:seconds) - if diff < 10 do + if diff < @timeout do # just do it immediately Task.async(fn -> __MODULE__.refresh!(token) end) else - :timer.apply_after((diff - 10) * 1000, __MODULE__, :refresh!, [token]) + :timer.apply_after((diff - @timeout) * 1000, __MODULE__, :refresh!, [token]) end end diff --git a/lib/goth/token_store.ex b/lib/goth/token_store.ex index 8351777..0a1cdd0 100644 --- a/lib/goth/token_store.ex +++ b/lib/goth/token_store.ex @@ -10,11 +10,14 @@ defmodule Goth.TokenStore do use GenServer alias Goth.Token + @ets_table :goth_token_store + def start_link do GenServer.start_link(__MODULE__, %{}, name: __MODULE__) end def init(state) do + :ets.new(@ets_table, [:named_table, :set, {:read_concurrency, true}]) {:ok, state} end @@ -58,12 +61,22 @@ defmodule Goth.TokenStore do def find(scope, sub) when is_binary(scope), do: find({:default, scope}, sub) def find({account, scope}, sub) do - GenServer.call(__MODULE__, {:find, {account, scope, sub}}) + case :ets.lookup(@ets_table, {account, scope, sub}) do + [{{^account, ^scope, ^sub}, token}] -> + case {:ok, token} |> filter_expired(:os.system_time(:seconds)) do + :error -> GenServer.call(__MODULE__, {:find, {account, scope, sub}}) + response -> response + end + + _ -> + GenServer.call(__MODULE__, {:find, {account, scope, sub}}) + end end # when we store a token, we should refresh it later def handle_call({:store, {account, scope, sub}, token}, _from, state) do # this is a race condition when inserting an expired (or about to expire) token... + :ets.insert(@ets_table, {{account, scope, sub}, token}) pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, {account, scope, sub}, token)} end