This package works with the oauth2
package to manage the automatic renewal of
tokens before they expire
If available in Hex, the package can be installed
by adding oauth2_token_agent
to your list of dependencies in mix.exs
:
def deps do
[
{:oauth2_token_agent, "~> 0.1.0"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/oauth2_token_agent.
Create an OAuth2.Client
instance to use to get the initial token and pass it to
OAuth2TokenAgent.TokenAgent.start_link/1
along with the inline
OAuth2TokenAgent.TokenRefreshStrategy
and the name for the Agent
.
The client can be configured as described at https://github.com/ueberauth/oauth2#configure-a-http-client.
client = Client.new([
strategy: OAuth2.Strategy.ClientCredentials,
client_id: "example_client_id",
client_secret: "example_client_secret",
site: "https://example.com/"
])
{:ok, agent} = OAuth2TokenAgent.TokenAgent.start_link(
name: MyModule.TokenAgent,
initial_client: client,
inline_refresh_strategy: %OAuth2TokenAgent.TokenRefreshStrategy{seconds_before_expires: 30}
)
The current version of the client can be retrieved using
OAuth2TokenAgent.TokenAgent.get_client/1
with the agent's PID or name.
This client will automatically use the access token as a Bearer token in
the Authorization header when calling its request methods. The access token
itself can be retrieved from the client struct if it is desirable to use separately
configured clients and OAuth2TokenAgent.TokenAgent.get_access_token/1
is
provided for convenience when doing so.
current_client = OAuth2TokenAgent.TokenAgent.get_client(agent)
current_client = OAuth2TokenAgent.TokenAgent.get_client(MyModule.TokenAgent)
access_token = OAuth2TokenAgent.TokenAgent.get_access_token(MyModule.TokenAgent)
The token can be refreshed by calling OAuth2TokenAgent.TokenAgent.refresh_tokens/1
.
If a refresh token is available, it will be exchanged for a new set of tokens.
If no refresh token is available or the attempt to use the refresh results in an error,
the original client will be used again to attempt to obtain a new set of tokens.
:ok = OAuth2TokenAgent.TokenAgent.refresh(MyModule.TokenAgent)
This can be used to handle complex token refresh logic, but it will generally be preferable to configure a strategy.
Inline token refresh strategies are used to determine when to obtain a new token before returning the current client state or token value. The supported conditions are show in the below example.
%TokenRefreshStrategy{
seconds_before_expires: 30, # refresh the token if it will expire in the next N seconds
every_seconds: 300 # refresh the token if it has been at least N seconds since the last refresh
}
If the Authorization Server does not provide an expiration time for the token, the
expiration time conditions will not trigger a refresh, so :every_seconds
should
be used.
The inline refreshes only occur as part of request for data from the agent; this
saves unnecessary renewal requests in low-volume systems but tokens may be allowed
to expire if unused. If refresh tokens need to be kept active in a system where the
time between requests exceeds the token duration and the initial client cannot be
reused, using OAuth2TokenAgent.TokenAgent.refresh
may be necessary.
The inline refreshes occur during message processing in the agent, which is not concurrent per agent. This guarantees that redundant refresh calls will not be made, which is particularly important when using single-use refresh tokens, but also adds the latency of the refresh to the processing of the message that triggers it.
The same strategy can have properties configured for multiple conditions and the token will be refreshed if any of them are met.
If no value is specified when starting the agent, the behavior defaults to
%TokenRefreshStrategy{seconds_before_expires: 30, every_seconds: 300}
.
Providing a name for the agent allows it to be referred to in code without passing around the PID, which is useful for reusing the same token whenever providing credentials for the same principal, cutting down on the number of calls that need to be made to the Authorization Server. A named instance can be added to a supervision tree's children using a tuple like the below example. This will launch the agent under the supervision tree and restart it if it crashes.
children = [
{TokenAgent, name: MyModule.TokenAgent, initial_client: client}
]