Compare commits

...

11 Commits

Author SHA1 Message Date
5c042bc36c remove github garbage 2024-07-29 18:22:29 +02:00
d341f77b52 add nix flake with devshell 2024-07-29 18:22:24 +02:00
matt-brale-xyz
5a5535a4a7
Merge pull request #4 from Brale-xyz/matt/bra-8627
chore: rename from oauth2_token_manager to oauth2_token_agent to avoid conflict
2024-03-25 11:25:27 -05:00
Matt Franczak
e8e12ca521 chore: rename from oauth2_token_manager to oauth2_token_agent to avoid conflict 2024-03-25 10:59:15 -05:00
matt-brale-xyz
e21ad2d797
Merge pull request #3 from Brale-xyz/matt/bra-8625
docs: add source URL for project
2024-03-25 10:25:59 -05:00
Matt Franczak
87b74ab15d docs: add source URL for project 2024-03-25 10:21:50 -05:00
matt-brale-xyz
83e538c928
Merge pull request #2 from Brale-xyz/license
Add Apache 2.0 License
2023-12-15 15:18:45 -06:00
matt-brale-xyz
28a812c4f2
Merge pull request #1 from Brale-xyz/release-prep
build: update mix.exs config to build the package
2023-12-14 11:51:12 -06:00
Matt Franczak
e50e65d032 build: add license to package metadata 2023-12-13 12:28:00 -06:00
Matt Franczak
abb2d88f31 ci: add pull request validation workflow 2023-12-13 12:22:35 -06:00
matt-brale-xyz
f8f9c36961
Add Apache 2.0 License 2023-12-13 10:03:52 -06:00
11 changed files with 263 additions and 30 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

5
.gitignore vendored
View File

@ -20,10 +20,13 @@ erl_crash.dump
*.ez
# Ignore package tarball (built via "mix hex.build").
oauth2_token_manager-*.tar
oauth2_token_agent-*.tar
# Temporary files, for example, from tests.
/tmp/
# Elixir VSCode extension data
/.elixir_ls/
.direnv
.state

176
LICENSE Normal file
View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -1,4 +1,4 @@
# OAuth2TokenManager
# OAuth2TokenAgent
This package works with the `oauth2` package to manage the automatic renewal of
tokens before they expire
@ -6,25 +6,25 @@ tokens before they expire
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `oauth2_token_manager` to your list of dependencies in `mix.exs`:
by adding `oauth2_token_agent` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:oauth2_token_manager, "~> 0.1.0"}
{:oauth2_token_agent, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/oauth2_token_manager>.
be found at <https://hexdocs.pm/oauth2_token_agent>.
## Usage
Create an `OAuth2.Client` instance to use to get the initial token and pass it to
`OAuth2TokenMananger.TokenAgent.start_link/1` along with the inline
`OAuth2TokenManager.TokenRefreshStrategy` and the name for the `Agent`.
`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.
@ -37,34 +37,34 @@ client = Client.new([
site: "https://example.com/"
])
{:ok, agent} = OAuth2TokenManager.TokenAgent.start_link(
{:ok, agent} = OAuth2TokenAgent.TokenAgent.start_link(
name: MyModule.TokenAgent,
initial_client: client,
inline_refresh_strategy: %OAuth2TokenManager.TokenRefreshStrategy{seconds_before_expires: 30}
inline_refresh_strategy: %OAuth2TokenAgent.TokenRefreshStrategy{seconds_before_expires: 30}
)
```
The current version of the client can be retrieved using
`OAuth2TokenManager.TokenAgent.get_client/1` with the agent's PID or name.
`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 `OAuth2TokenManager.TokenAgent.get_access_token/1` is
configured clients and `OAuth2TokenAgent.TokenAgent.get_access_token/1` is
provided for convenience when doing so.
```Elixir
current_client = OAuth2TokenManager.TokenAgent.get_client(agent)
current_client = OAuth2TokenManager.TokenAgent.get_client(MyModule.TokenAgent)
access_token = OAuth2TokenManager.TokenAgent.get_access_token(MyModule.TokenAgent)
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 `OAuth2TokenManager.TokenAgent.refresh_tokens/1`.
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.
```Elixir
:ok = OAuth2TokenManager.TokenAgent.refresh(MyModule.TokenAgent)
:ok = OAuth2TokenAgent.TokenAgent.refresh(MyModule.TokenAgent)
```
This can be used to handle complex token refresh logic, but it will generally be
@ -91,7 +91,7 @@ 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 `OAuth2TokenManager.TokenAgent.refresh` may be necessary.
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

26
flake.lock generated Normal file
View File

@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1722062969,
"narHash": "sha256-QOS0ykELUmPbrrUGmegAUlpmUFznDQeR4q7rFhl8eQg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b73c2221a46c13557b1b3be9c2070cc42cf01eb3",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

26
flake.nix Normal file
View File

@ -0,0 +1,26 @@
{
inputs = { nixpkgs.url = "nixpkgs/nixos-unstable"; };
outputs = { self, nixpkgs }: {
devShell.x86_64-linux = let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
beam = pkgs.beam.packages.erlang_27;
elixir = beam.elixir_1_17;
elixir-ls = (beam.elixir-ls.override { inherit elixir; });
in pkgs.mkShell {
buildInputs = [ elixir elixir-ls ];
shellHook = ''
# this allows mix to work on the local directory
mkdir -p .state/mix .state/hex
export MIX_HOME=$PWD/.state/mix
export HEX_HOME=$PWD/.state/hex
export PATH=$MIX_HOME/bin:$MIX_HOME/escripts:$HEX_HOME/bin:$PATH
# TODO: not sure how to make hex available without installing it afterwards.
mix local.hex --if-missing --force
export LANG=en_US.UTF-8
export ERL_AFLAGS="-kernel shell_history enabled -kernel shell_history_path '\"$PWD/.state\"' -kernel shell_history_file_bytes 1024000"
'';
};
};
}

View File

@ -1,4 +1,4 @@
defmodule OAuth2TokenManager.TokenAgent do
defmodule OAuth2TokenAgent.TokenAgent do
@moduledoc """
Defines the Agent used to manage the token and the struct it uses to store its state
"""
@ -7,7 +7,7 @@ defmodule OAuth2TokenManager.TokenAgent do
use TypedStruct
alias __MODULE__
alias OAuth2TokenManager.TokenRefreshStrategy
alias OAuth2TokenAgent.TokenRefreshStrategy
alias OAuth2.{AccessToken, Client, Error, Response}
require Logger

View File

@ -1,4 +1,4 @@
defmodule OAuth2TokenManager.TokenRefreshStrategy do
defmodule OAuth2TokenAgent.TokenRefreshStrategy do
@moduledoc """
Module defining a struct for representing strategies for refreshing tokens and
the functions for applying them

11
mix.exs
View File

@ -1,14 +1,15 @@
defmodule OAuth2TokenManager.MixProject do
defmodule OAuth2TokenAgent.MixProject do
use Mix.Project
def project do
[
app: :oauth2_token_manager,
app: :oauth2_token_agent,
version: "0.1.0",
elixir: "~> 1.15",
description: description(),
package: package(),
deps: deps()
deps: deps(),
source_url: "https://github.com/Brale-xyz/oauth2-token-agent"
]
end
@ -38,8 +39,8 @@ defmodule OAuth2TokenManager.MixProject do
defp package do
[
licenses: [],
links: %{"GitHub" => "https://github.com/Brale-xyz/oauth2-token-manager"}
licenses: ["Apache-2.0"],
links: %{"GitHub" => "https://github.com/Brale-xyz/oauth2-token-agent"}
]
end
end

View File

@ -1,4 +1,4 @@
defmodule OAuth2TokenManager.TokenAgentTest do
defmodule OAuth2TokenAgent.TokenAgentTest do
@moduledoc """
Tests for TokenAgent, which is used to track token state
"""
@ -7,7 +7,7 @@ defmodule OAuth2TokenManager.TokenAgentTest do
import Mock
alias OAuth2.{AccessToken, Client}
alias OAuth2TokenManager.{TokenAgent, TokenRefreshStrategy}
alias OAuth2TokenAgent.{TokenAgent, TokenRefreshStrategy}
defp test_client do
Client.new(

View File

@ -1,11 +1,11 @@
defmodule OAuth2TokenManager.TokenRefreshStrategyTest do
defmodule OAuth2TokenAgent.TokenRefreshStrategyTest do
@moduledoc """
Tests the refresh strategies configured with TokenRefreshStrategy
"""
use ExUnit.Case
alias OAuth2TokenManager.TokenRefreshStrategy
alias OAuth2TokenAgent.TokenRefreshStrategy
defp seconds_from_now(seconds) do
DateTime.utc_now() |> DateTime.add(seconds, :second, Calendar.UTCOnlyTimeZoneDatabase)