From a88753f2c7a0fb07ad9caf32cb04009f335570c7 Mon Sep 17 00:00:00 2001
From: Annibelle Boling
Date: Sun, 1 Mar 2026 02:06:34 -0700
Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=9D=20Update=20documentation?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 120 +++++++-----
docs/OAuth2.html | 2 +-
docs/OAuth2/AccessToken.html | 2 +-
docs/OAuth2/Authenticator.html | 2 +-
docs/OAuth2/Client.html | 2 +-
docs/OAuth2/Error.html | 2 +-
docs/OAuth2/FilteredAttributes.html | 2 +-
.../FilteredAttributes/ClassMethods.html | 2 +-
docs/OAuth2/Response.html | 2 +-
docs/OAuth2/Strategy.html | 2 +-
docs/OAuth2/Strategy/Assertion.html | 2 +-
docs/OAuth2/Strategy/AuthCode.html | 2 +-
docs/OAuth2/Strategy/Base.html | 2 +-
docs/OAuth2/Strategy/ClientCredentials.html | 2 +-
docs/OAuth2/Strategy/Implicit.html | 2 +-
docs/OAuth2/Strategy/Password.html | 2 +-
docs/OAuth2/Version.html | 2 +-
docs/_index.html | 2 +-
docs/file.CHANGELOG.html | 2 +-
docs/file.CITATION.html | 2 +-
docs/file.CODE_OF_CONDUCT.html | 2 +-
docs/file.CONTRIBUTING.html | 2 +-
docs/file.FUNDING.html | 2 +-
docs/file.IRP.html | 2 +-
docs/file.LICENSE.html | 2 +-
docs/file.OIDC.html | 2 +-
docs/file.README.html | 176 +++++++++++-------
docs/file.REEK.html | 2 +-
docs/file.RUBOCOP.html | 2 +-
docs/file.SECURITY.html | 2 +-
docs/file.THREAT_MODEL.html | 2 +-
docs/file.access_token.html | 2 +-
docs/file.authenticator.html | 2 +-
docs/file.client.html | 2 +-
docs/file.error.html | 2 +-
docs/file.filtered_attributes.html | 2 +-
docs/file.oauth2.html | 2 +-
docs/file.response.html | 2 +-
docs/file.strategy.html | 2 +-
docs/file.version.html | 2 +-
docs/index.html | 176 +++++++++++-------
docs/top-level-namespace.html | 2 +-
42 files changed, 324 insertions(+), 226 deletions(-)
diff --git a/README.md b/README.md
index bc17fe79..b77014dd 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,6 @@
# 🔐 OAuth 2.0 Authorization Framework
-⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
-
[![Version][👽versioni]][👽version] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: MIT][📄license-img]][📄license-ref] [![Downloads Rank][👽dl-ranki]][👽dl-rank] [![Open Source Helpers][👽oss-helpi]][👽oss-help] [![CodeCov Test Coverage][🏀codecovi]][🏀codecov] [![Coveralls Test Coverage][🏀coveralls-img]][🏀coveralls] [![QLTY Test Coverage][🏀qlty-covi]][🏀qlty-cov] [![QLTY Maintainability][🏀qlty-mnti]][🏀qlty-mnt] [![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf] [![CI Runtime Dependencies @ HEAD][🚎12-crh-wfi]][🚎12-crh-wf] [![CI Current][🚎11-c-wfi]][🚎11-c-wf] [![CI JRuby][🚎10-j-wfi]][🚎10-j-wf] [![Deps Locked][🚎13-🔒️-wfi]][🚎13-🔒️-wf] [![Deps Unlocked][🚎14-🔓️-wfi]][🚎14-🔓️-wf] [![CI Supported][🚎6-s-wfi]][🚎6-s-wf] [![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf] [![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf] [![CI Ancient][🚎1-an-wfi]][🚎1-an-wf] [![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf] [![CI Style][🚎5-st-wfi]][🚎5-st-wf] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Apache SkyWalking Eyes License Compatibility Check][🚎15-🪪-wfi]][🚎15-🪪-wf]
`if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord].
@@ -31,10 +29,10 @@ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-ta
## 🌻 Synopsis
OAuth 2.0 is the industry-standard protocol for authorization.
-OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
- desktop applications, mobile phones, and living room devices.
This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications.
+⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
+
### Quick Examples
@@ -53,13 +51,14 @@ curl --request POST \
NOTE: In the ruby version below, certain params are passed to the `get_token` call, instead of the client creation.
```ruby
-OAuth2::Client.new(
+client = OAuth2::Client.new(
"REDMOND_CLIENT_ID", # client_id
"REDMOND_CLIENT_SECRET", # client_secret
auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
site: "https://login.microsoftonline.com/REDMOND_REDACTED",
-). # The base path for token_url when it is relative
+)
+client.
client_credentials. # There are many other types to choose from!
get_token(resource: "REDMOND_RESOURCE_UUID")
```
@@ -322,32 +321,30 @@ See [SECURITY.md][🔐security] and [IRP.md][🔐irp].
## ⚙️ Configuration
-You can turn on additional warnings.
+Global settings for the library:
```ruby
OAuth2.configure do |config|
- # Turn on a warning like:
- # OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key
config.silence_extra_tokens_warning = false # default: true
- # Set to true if you want to also show warnings about no tokens
- config.silence_no_tokens_warning = false # default: true,
+ config.silence_no_tokens_warning = false # default: true
end
```
-The "extra tokens" problem comes from ambiguity in the spec about which token is the right token.
-Some OAuth 2.0 standards legitimately have multiple tokens.
-You may need to subclass `OAuth2::AccessToken`, or write your own custom alternative to it, and pass it in.
-Specify your custom class with the `access_token_class` option.
+## 🔧 Basic Usage
-If you only need one token, you can, as of v2.0.10,
-specify the exact token name you want to extract via the `OAuth2::AccessToken` using
-the `token_name` option.
+### Client Initialization Options
-You'll likely need to do some source diving.
-This gem has 100% test coverage for lines and branches, so the specs are a great place to look for ideas.
-If you have time and energy, please contribute to the documentation!
+`OAuth2::Client.new` accepts several options:
-## 🔧 Basic Usage
+- `:site`: The base URL for the OAuth 2.0 provider.
+- `:authorize_url`: The authorization endpoint (default: `"oauth/authorize"`).
+- `:token_url`: The token endpoint (default: `"oauth/token"`).
+- `:auth_scheme`: The authentication scheme (`:basic_auth`, `:request_body`, `:tls_client_auth`, `:private_key_jwt`). Default is `:basic_auth`.
+- `:connection_opts`: Options for the underlying Faraday connection (timeouts, proxy, etc.).
+- `:raise_errors`: Whether to raise `OAuth2::Error` on 400+ responses (default: `true`).
+
+
+ authorize_url and token_url
### `authorize_url` and `token_url` are on site root (Just Works!)
@@ -394,6 +391,25 @@ client.class.name
# => OAuth2::Client
```
+
+
+### Advanced Initializers
+
+```ruby
+client = OAuth2::Client.new(id, secret, site: site) do |faraday|
+ faraday.request(:url_encoded)
+ faraday.adapter(:net_http_persistent)
+end
+```
+
+### AccessToken Features
+
+Instances of `OAuth2::AccessToken` handle request signing and token expiration.
+
+- **Snake Case & Indifferent Access**: `response.parsed` returns a `SnakyHash` allowing access via string/symbol and snake_case keys even if the provider returns CamelCase.
+- **Auto-Refresh**: You can manually check `token.expired?` and call `token.refresh`.
+- **Serialization**: Persist tokens using `token.to_hash` and restore via `OAuth2::AccessToken.from_hash(client, hash)`.
+
### snake_case and indifferent access in Response#parsed
```ruby
@@ -546,12 +562,12 @@ client = OAuth2::Client.new(
### OAuth2::Response
The `AccessToken` methods `#get`, `#post`, `#put` and `#delete` and the generic `#request`
-will return an instance of the #OAuth2::Response class.
+will return an instance of the `OAuth2::Response` class.
This instance contains a `#parsed` method that will parse the response body and
return a Hash-like [`SnakyHash::StringKeyed`](https://gitlab.com/ruby-oauth/snaky_hash/-/blob/main/lib/snaky_hash/string_keyed.rb) if the `Content-Type` is `application/x-www-form-urlencoded` or if
-the body is a JSON object. It will return an Array if the body is a JSON
-array. Otherwise, it will return the original body string.
+the body is a JSON object. It will return an Array if the body is a JSON
+array. Otherwise, it will return the original body string.
The original response body, headers, and status can be accessed via their
respective methods.
@@ -593,16 +609,22 @@ Response instance will contain the `OAuth2::Error` instance.
### Authorization Grants
-Note on OAuth 2.1 (draft):
+Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
+authentication grant types have helper strategy classes that simplify client
+use. They are available via the [`#auth_code`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/auth_code.rb),
+[`#implicit`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/implicit.rb),
+[`#password`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/password.rb),
+[`#client_credentials`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/client_credentials.rb), and
+[`#assertion`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/assertion.rb) methods respectively.
-- PKCE is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
-- Redirect URIs must be compared using exact string matching by the Authorization Server.
-- The Implicit grant (response_type=token) and the Resource Owner Password Credentials grant are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
-- Bearer tokens in the query string are omitted due to security risks; prefer Authorization header usage.
-- Refresh tokens for public clients must either be sender-constrained (e.g., DPoP/MTLS) or one-time use.
-- The definitions of public and confidential clients are simplified to refer only to whether the client has credentials.
+#### OAuth 2.1 (draft) Note:
-References:
+- **PKCE** is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
+- **Implicit grant** (response_type=token) and **Resource Owner Password Credentials grant** are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
+- **Redirect URIs** must be compared using exact string matching by the Authorization Server.
+
+
+ OAuth 2.1 (draft) References
- OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
- Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
@@ -611,13 +633,7 @@ References:
- Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
- Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
-Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
-authentication grant types have helper strategy classes that simplify client
-use. They are available via the [`#auth_code`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/auth_code.rb),
-[`#implicit`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/implicit.rb),
-[`#password`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/password.rb),
-[`#client_credentials`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/client_credentials.rb), and
-[`#assertion`](https://gitlab.com/ruby-oauth/oauth2/-/blob/main/lib/oauth2/strategy/assertion.rb) methods respectively.
+
These aren't full examples, but demonstrative of the differences between usage for each strategy.
@@ -752,7 +768,7 @@ Notes:
-### Instagram API (verb‑dependent token mode)
+### Verb‑dependent Token Mode
Providers like Instagram require the access token to be sent differently depending on the HTTP verb:
@@ -761,6 +777,14 @@ Providers like Instagram require the access token to be sent differently dependi
Since v2.0.15, you can configure an AccessToken with a verb‑dependent mode. The gem will choose how to send the token based on the request method.
+Tips:
+
+- Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for `GET` requests.
+- If you need a custom rule, you can pass a `Proc` for `mode`, e.g. `mode: ->(verb) { verb == :get ? :query : :header }`.
+
+
+ Instagram API Example
+
Example: exchanging and refreshing long‑lived Instagram tokens, and making API calls
```ruby
@@ -819,10 +843,7 @@ me = long_lived.get("/me", params: {fields: "id,username"}).parsed
# long_lived.post("/me/media", body: {image_url: "https://...", caption: "hello"})
```
-Tips:
-
-- Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for `GET` requests.
-- If you need a custom rule, you can pass a `Proc` for `mode`, e.g. `mode: ->(verb) { verb == :get ? :query : :header }`.
+
### Refresh Tokens
@@ -1063,11 +1084,12 @@ access = client.get_token({
})
```
-### OpenID Connect (OIDC) Notes
+### OpenID Connect (OIDC)
-- If the token response includes an `id_token` (a JWT), this gem surfaces it but does not validate the signature. Use a JWT library and your provider's JWKs to verify it.
-- For private_key_jwt client authentication, provide `auth_scheme: :private_key_jwt` and ensure your key configuration matches the provider requirements.
-- See [OIDC.md](OIDC.md) for a more complete OIDC overview, example, and links to the relevant specifications.
+- If the token response includes an `id_token` (a JWT), this gem surfaces it in `token.params['id_token']`.
+- **Note**: This gem does **not** validate the signature of the `id_token`. You must use a JWT library (like the `jwt` [gem](https://github.com/jwt/ruby-jwt)) and your provider's JWKs to verify it.
+- For `private_key_jwt` client authentication, provide `auth_scheme: :private_key_jwt` and ensure your key configuration matches the provider requirements.
+- See [OIDC.md](OIDC.md) for a more complete OIDC overview and examples.
### Debugging
diff --git a/docs/OAuth2.html b/docs/OAuth2.html
index b27a0542..02f70035 100644
--- a/docs/OAuth2.html
+++ b/docs/OAuth2.html
@@ -373,7 +373,7 @@ ⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
-

if ci_badges.map(&:color).detect { it != "green"} ☝️ let me know, as I may have missed the discord notification.
@@ -83,10 +81,10 @@ 🔐 OAuth 2.0 Authorization Framewor
🌻 Synopsis
OAuth 2.0 is the industry-standard protocol for authorization.
-OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
- desktop applications, mobile phones, and living room devices.
This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications.
+⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
+
Quick Examples
@@ -103,13 +101,14 @@ Quick Examples
NOTE: In the ruby version below, certain params are passed to the get_token call, instead of the client creation.
- OAuth2::Client.new(
+ client = OAuth2::Client.new(
"REDMOND_CLIENT_ID", # client_id
"REDMOND_CLIENT_SECRET", # client_secret
auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
site: "https://login.microsoftonline.com/REDMOND_REDACTED",
-). # The base path for token_url when it is relative
+)
+client.
client_credentials. # There are many other types to choose from!
get_token(resource: "REDMOND_RESOURCE_UUID")
@@ -580,36 +579,42 @@ Compatibility
⚙️ Configuration
-You can turn on additional warnings.
+Global settings for the library:
OAuth2.configure do |config|
- # Turn on a warning like:
- # OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key
config.silence_extra_tokens_warning = false # default: true
- # Set to true if you want to also show warnings about no tokens
- config.silence_no_tokens_warning = false # default: true,
+ config.silence_no_tokens_warning = false # default: true
end
-The “extra tokens” problem comes from ambiguity in the spec about which token is the right token.
-Some OAuth 2.0 standards legitimately have multiple tokens.
-You may need to subclass OAuth2::AccessToken, or write your own custom alternative to it, and pass it in.
-Specify your custom class with the access_token_class option.
+🔧 Basic Usage
-If you only need one token, you can, as of v2.0.10,
-specify the exact token name you want to extract via the OAuth2::AccessToken using
-the token_name option.
+Client Initialization Options
-You’ll likely need to do some source diving.
-This gem has 100% test coverage for lines and branches, so the specs are a great place to look for ideas.
-If you have time and energy, please contribute to the documentation!
+OAuth2::Client.new accepts several options:
-🔧 Basic Usage
+
+ -
+
:site: The base URL for the OAuth 2.0 provider.
+ -
+
:authorize_url: The authorization endpoint (default: "oauth/authorize").
+ -
+
:token_url: The token endpoint (default: "oauth/token").
+ -
+
:auth_scheme: The authentication scheme (:basic_auth, :request_body, :tls_client_auth, :private_key_jwt). Default is :basic_auth.
+ -
+
:connection_opts: Options for the underlying Faraday connection (timeouts, proxy, etc.).
+ -
+
:raise_errors: Whether to raise OAuth2::Error on 400+ responses (default: true).
+
-
+
+ authorize_url and token_url
+
+
authorize_url and token_url are on site root (Just Works!)
-require "oauth2"
+ require "oauth2"
client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org")
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
@@ -621,22 +626,22 @@
# => OAuth2::Response
-Relative authorize_url and token_url (Not on site root, Just Works!)
+ Relative authorize_url and token_url (Not on site root, Just Works!)
-In the above example, the default Authorization URL is oauth/authorize and default Access Token URL is oauth/token, and, as they are missing a leading /, both are relative.
+ In the above example, the default Authorization URL is oauth/authorize and default Access Token URL is oauth/token, and, as they are missing a leading /, both are relative.
-client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/nested/directory/on/your/server")
+ client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/nested/directory/on/your/server")
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
# => "https://example.org/nested/directory/on/your/server/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
-Customize authorize_url and token_url
+ Customize authorize_url and token_url
-
You can specify custom URLs for authorization and access token, and when using a leading / they will not be relative, as shown below:
+ You can specify custom URLs for authorization and access token, and when using a leading / they will not be relative, as shown below:
-client = OAuth2::Client.new(
+ client = OAuth2::Client.new(
"client_id",
"client_secret",
site: "https://example.org/nested/directory/on/your/server",
@@ -650,6 +655,29 @@ Customize authorize_url
+
+
+Advanced Initializers
+
+client = OAuth2::Client.new(id, secret, site: site) do |faraday|
+ faraday.request(:url_encoded)
+ faraday.adapter(:net_http_persistent)
+end
+
+
+AccessToken Features
+
+
Instances of OAuth2::AccessToken handle request signing and token expiration.
+
+
+ -
+Snake Case & Indifferent Access:
response.parsed returns a SnakyHash allowing access via string/symbol and snake_case keys even if the provider returns CamelCase.
+ -
+Auto-Refresh: You can manually check
token.expired? and call token.refresh.
+ -
+Serialization: Persist tokens using
token.to_hash and restore via OAuth2::AccessToken.from_hash(client, hash).
+
+
snake_case and indifferent access in Response#parsed
response = access.get("/api/resource", params: {"query_foo" => "bar"})
@@ -795,12 +823,12 @@ Prefer camelCase over sna
OAuth2::Response
The AccessToken methods #get, #post, #put and #delete and the generic #request
-will return an instance of the #OAuth2::Response class.
+will return an instance of the OAuth2::Response class.
This instance contains a #parsed method that will parse the response body and
return a Hash-like SnakyHash::StringKeyed if the Content-Type is application/x-www-form-urlencoded or if
-the body is a JSON object. It will return an Array if the body is a JSON
-array. Otherwise, it will return the original body string.
+the body is a JSON object. It will return an Array if the body is a JSON
+array. Otherwise, it will return the original body string.
The original response body, headers, and status can be accessed via their
respective methods.
@@ -886,28 +914,6 @@ OAuth2::Error
Authorization Grants
-Note on OAuth 2.1 (draft):
-
-
- - PKCE is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
- - Redirect URIs must be compared using exact string matching by the Authorization Server.
- - The Implicit grant (response_type=token) and the Resource Owner Password Credentials grant are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
- - Bearer tokens in the query string are omitted due to security risks; prefer Authorization header usage.
- - Refresh tokens for public clients must either be sender-constrained (e.g., DPoP/MTLS) or one-time use.
- - The definitions of public and confidential clients are simplified to refer only to whether the client has credentials.
-
-
-References:
-
-
- - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
- - Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
- - FusionAuth: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
- - Okta: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
- - Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
- - Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
-
-
Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
authentication grant types have helper strategy classes that simplify client
use. They are available via the #auth_code,
@@ -916,6 +922,31 @@
Authorization Grants
#client_credentials, and
#assertion methods respectively.
+OAuth 2.1 (draft) Note:
+
+
+ -
+PKCE is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
+ -
+Implicit grant (response_type=token) and Resource Owner Password Credentials grant are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
+ -
+Redirect URIs must be compared using exact string matching by the Authorization Server.
+
+
+
+ OAuth 2.1 (draft) References
+
+
+ - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
+ - Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
+ - FusionAuth: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
+ - Okta: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
+ - Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
+ - Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
+
+
+
+
These aren’t full examples, but demonstrative of the differences between usage for each strategy.
auth_url = client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth/callback")
@@ -1051,7 +1082,7 @@ Examples
-Instagram API (verb‑dependent token mode)
+Verb‑dependent Token Mode
Providers like Instagram require the access token to be sent differently depending on the HTTP verb:
@@ -1062,9 +1093,19 @@ Instagram API (verb‑dependent
Since v2.0.15, you can configure an AccessToken with a verb‑dependent mode. The gem will choose how to send the token based on the request method.
-Example: exchanging and refreshing long‑lived Instagram tokens, and making API calls
+Tips:
-require "oauth2"
+
+ - Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for
GET requests.
+ - If you need a custom rule, you can pass a
Proc for mode, e.g. mode: ->(verb) { verb == :get ? :query : :header }.
+
+
+
+ Instagram API Example
+
+ Example: exchanging and refreshing long‑lived Instagram tokens, and making API calls
+
+ require "oauth2"
# NOTE: Users authenticate via Facebook Login to obtain a short‑lived user token (not shown here).
# See Facebook Login docs for obtaining the initial short‑lived token.
@@ -1119,12 +1160,7 @@ Instagram API (verb‑dependent
# long_lived.post("/me/media", body: {image_url: "https://...", caption: "hello"})
-Tips:
-
-
- - Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for
GET requests.
- - If you need a custom rule, you can pass a
Proc for mode, e.g. mode: ->(verb) { verb == :get ? :query : :header }.
-
+
Refresh Tokens
@@ -1371,12 +1407,14 @@ Making Raw Token Requests
})
-OpenID Connect (OIDC) Notes
+OpenID Connect (OIDC)
- - If the token response includes an
id_token (a JWT), this gem surfaces it but does not validate the signature. Use a JWT library and your provider’s JWKs to verify it.
- - For private_key_jwt client authentication, provide
auth_scheme: :private_key_jwt and ensure your key configuration matches the provider requirements.
- - See OIDC.md for a more complete OIDC overview, example, and links to the relevant specifications.
+ - If the token response includes an
id_token (a JWT), this gem surfaces it in token.params['id_token'].
+ -
+Note: This gem does not validate the signature of the
id_token. You must use a JWT library (like the jwt gem) and your provider’s JWKs to verify it.
+ - For
private_key_jwt client authentication, provide auth_scheme: :private_key_jwt and ensure your key configuration matches the provider requirements.
+ - See OIDC.md for a more complete OIDC overview and examples.
Debugging
@@ -1607,7 +1645,7 @@ Please give the project a star ⭐ ♥
diff --git a/docs/file.REEK.html b/docs/file.REEK.html
index af069f1c..a9332509 100644
--- a/docs/file.REEK.html
+++ b/docs/file.REEK.html
@@ -61,7 +61,7 @@
diff --git a/docs/file.RUBOCOP.html b/docs/file.RUBOCOP.html
index 07742971..8abc32e6 100644
--- a/docs/file.RUBOCOP.html
+++ b/docs/file.RUBOCOP.html
@@ -161,7 +161,7 @@ Benefits of rubocop_gradual
diff --git a/docs/file.SECURITY.html b/docs/file.SECURITY.html
index 55a279be..e341a941 100644
--- a/docs/file.SECURITY.html
+++ b/docs/file.SECURITY.html
@@ -93,7 +93,7 @@ Additional Support
diff --git a/docs/file.THREAT_MODEL.html b/docs/file.THREAT_MODEL.html
index 12ff60e7..ca7b357b 100644
--- a/docs/file.THREAT_MODEL.html
+++ b/docs/file.THREAT_MODEL.html
@@ -206,7 +206,7 @@ 8. References
diff --git a/docs/file.access_token.html b/docs/file.access_token.html
index c4b83ebc..e45013f8 100644
--- a/docs/file.access_token.html
+++ b/docs/file.access_token.html
@@ -84,7 +84,7 @@
diff --git a/docs/file.authenticator.html b/docs/file.authenticator.html
index 493979c7..2209675f 100644
--- a/docs/file.authenticator.html
+++ b/docs/file.authenticator.html
@@ -81,7 +81,7 @@
diff --git a/docs/file.client.html b/docs/file.client.html
index 55199f91..f9161b88 100644
--- a/docs/file.client.html
+++ b/docs/file.client.html
@@ -111,7 +111,7 @@
diff --git a/docs/file.error.html b/docs/file.error.html
index 9a3d11b9..bf58887a 100644
--- a/docs/file.error.html
+++ b/docs/file.error.html
@@ -68,7 +68,7 @@
diff --git a/docs/file.filtered_attributes.html b/docs/file.filtered_attributes.html
index d601ebb5..845bedda 100644
--- a/docs/file.filtered_attributes.html
+++ b/docs/file.filtered_attributes.html
@@ -66,7 +66,7 @@
diff --git a/docs/file.oauth2.html b/docs/file.oauth2.html
index fc822bf5..4f12cc26 100644
--- a/docs/file.oauth2.html
+++ b/docs/file.oauth2.html
@@ -69,7 +69,7 @@
diff --git a/docs/file.response.html b/docs/file.response.html
index 2e56e14f..ef886db7 100644
--- a/docs/file.response.html
+++ b/docs/file.response.html
@@ -77,7 +77,7 @@
diff --git a/docs/file.strategy.html b/docs/file.strategy.html
index d1809de2..c810606e 100644
--- a/docs/file.strategy.html
+++ b/docs/file.strategy.html
@@ -93,7 +93,7 @@
diff --git a/docs/file.version.html b/docs/file.version.html
index d0a0bb19..3afe48a9 100644
--- a/docs/file.version.html
+++ b/docs/file.version.html
@@ -65,7 +65,7 @@
diff --git a/docs/index.html b/docs/index.html
index 2de80d1d..34a9f595 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -61,8 +61,6 @@
🔐 OAuth 2.0 Authorization Framework
-
⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
-

if ci_badges.map(&:color).detect { it != "green"} ☝️ let me know, as I may have missed the discord notification.
@@ -83,10 +81,10 @@ 🔐 OAuth 2.0 Authorization Framewor
🌻 Synopsis
OAuth 2.0 is the industry-standard protocol for authorization.
-OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications,
- desktop applications, mobile phones, and living room devices.
This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications.
+⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC)
+
Quick Examples
@@ -103,13 +101,14 @@ Quick Examples
NOTE: In the ruby version below, certain params are passed to the get_token call, instead of the client creation.
- OAuth2::Client.new(
+ client = OAuth2::Client.new(
"REDMOND_CLIENT_ID", # client_id
"REDMOND_CLIENT_SECRET", # client_secret
auth_scheme: :request_body, # Other modes are supported: :basic_auth, :tls_client_auth, :private_key_jwt
token_url: "oauth2/token", # relative path, except with leading `/`, then absolute path
site: "https://login.microsoftonline.com/REDMOND_REDACTED",
-). # The base path for token_url when it is relative
+)
+client.
client_credentials. # There are many other types to choose from!
get_token(resource: "REDMOND_RESOURCE_UUID")
@@ -580,36 +579,42 @@ Compatibility
⚙️ Configuration
-You can turn on additional warnings.
+Global settings for the library:
OAuth2.configure do |config|
- # Turn on a warning like:
- # OAuth2::AccessToken.from_hash: `hash` contained more than one 'token' key
config.silence_extra_tokens_warning = false # default: true
- # Set to true if you want to also show warnings about no tokens
- config.silence_no_tokens_warning = false # default: true,
+ config.silence_no_tokens_warning = false # default: true
end
-The “extra tokens” problem comes from ambiguity in the spec about which token is the right token.
-Some OAuth 2.0 standards legitimately have multiple tokens.
-You may need to subclass OAuth2::AccessToken, or write your own custom alternative to it, and pass it in.
-Specify your custom class with the access_token_class option.
+🔧 Basic Usage
-If you only need one token, you can, as of v2.0.10,
-specify the exact token name you want to extract via the OAuth2::AccessToken using
-the token_name option.
+Client Initialization Options
-You’ll likely need to do some source diving.
-This gem has 100% test coverage for lines and branches, so the specs are a great place to look for ideas.
-If you have time and energy, please contribute to the documentation!
+OAuth2::Client.new accepts several options:
-🔧 Basic Usage
+
+ -
+
:site: The base URL for the OAuth 2.0 provider.
+ -
+
:authorize_url: The authorization endpoint (default: "oauth/authorize").
+ -
+
:token_url: The token endpoint (default: "oauth/token").
+ -
+
:auth_scheme: The authentication scheme (:basic_auth, :request_body, :tls_client_auth, :private_key_jwt). Default is :basic_auth.
+ -
+
:connection_opts: Options for the underlying Faraday connection (timeouts, proxy, etc.).
+ -
+
:raise_errors: Whether to raise OAuth2::Error on 400+ responses (default: true).
+
-
+
+ authorize_url and token_url
+
+
authorize_url and token_url are on site root (Just Works!)
-require "oauth2"
+ require "oauth2"
client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org")
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
@@ -621,22 +626,22 @@
# => OAuth2::Response
-Relative authorize_url and token_url (Not on site root, Just Works!)
+ Relative authorize_url and token_url (Not on site root, Just Works!)
-In the above example, the default Authorization URL is oauth/authorize and default Access Token URL is oauth/token, and, as they are missing a leading /, both are relative.
+ In the above example, the default Authorization URL is oauth/authorize and default Access Token URL is oauth/token, and, as they are missing a leading /, both are relative.
-client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/nested/directory/on/your/server")
+ client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/nested/directory/on/your/server")
# => #<OAuth2::Client:0x00000001204c8288 @id="client_id", @secret="client_sec...
client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth2/callback")
# => "https://example.org/nested/directory/on/your/server/oauth/authorize?client_id=client_id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Foauth2%2Fcallback&response_type=code"
-Customize authorize_url and token_url
+ Customize authorize_url and token_url
-
You can specify custom URLs for authorization and access token, and when using a leading / they will not be relative, as shown below:
+ You can specify custom URLs for authorization and access token, and when using a leading / they will not be relative, as shown below:
-client = OAuth2::Client.new(
+ client = OAuth2::Client.new(
"client_id",
"client_secret",
site: "https://example.org/nested/directory/on/your/server",
@@ -650,6 +655,29 @@ Customize authorize_url
+
+
+Advanced Initializers
+
+client = OAuth2::Client.new(id, secret, site: site) do |faraday|
+ faraday.request(:url_encoded)
+ faraday.adapter(:net_http_persistent)
+end
+
+
+AccessToken Features
+
+
Instances of OAuth2::AccessToken handle request signing and token expiration.
+
+
+ -
+Snake Case & Indifferent Access:
response.parsed returns a SnakyHash allowing access via string/symbol and snake_case keys even if the provider returns CamelCase.
+ -
+Auto-Refresh: You can manually check
token.expired? and call token.refresh.
+ -
+Serialization: Persist tokens using
token.to_hash and restore via OAuth2::AccessToken.from_hash(client, hash).
+
+
snake_case and indifferent access in Response#parsed
response = access.get("/api/resource", params: {"query_foo" => "bar"})
@@ -795,12 +823,12 @@ Prefer camelCase over sna
OAuth2::Response
The AccessToken methods #get, #post, #put and #delete and the generic #request
-will return an instance of the #OAuth2::Response class.
+will return an instance of the OAuth2::Response class.
This instance contains a #parsed method that will parse the response body and
return a Hash-like SnakyHash::StringKeyed if the Content-Type is application/x-www-form-urlencoded or if
-the body is a JSON object. It will return an Array if the body is a JSON
-array. Otherwise, it will return the original body string.
+the body is a JSON object. It will return an Array if the body is a JSON
+array. Otherwise, it will return the original body string.
The original response body, headers, and status can be accessed via their
respective methods.
@@ -886,28 +914,6 @@ OAuth2::Error
Authorization Grants
-Note on OAuth 2.1 (draft):
-
-
- - PKCE is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
- - Redirect URIs must be compared using exact string matching by the Authorization Server.
- - The Implicit grant (response_type=token) and the Resource Owner Password Credentials grant are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
- - Bearer tokens in the query string are omitted due to security risks; prefer Authorization header usage.
- - Refresh tokens for public clients must either be sender-constrained (e.g., DPoP/MTLS) or one-time use.
- - The definitions of public and confidential clients are simplified to refer only to whether the client has credentials.
-
-
-References:
-
-
- - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
- - Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
- - FusionAuth: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
- - Okta: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
- - Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
- - Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
-
-
Currently, the Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials, and Assertion
authentication grant types have helper strategy classes that simplify client
use. They are available via the #auth_code,
@@ -916,6 +922,31 @@
Authorization Grants
#client_credentials, and
#assertion methods respectively.
+OAuth 2.1 (draft) Note:
+
+
+ -
+PKCE is required for all OAuth clients using the authorization code flow (especially public clients). Implement PKCE in your app when required by your provider. See RFC 7636 and RFC 8252.
+ -
+Implicit grant (response_type=token) and Resource Owner Password Credentials grant are omitted from OAuth 2.1; they remain here for OAuth 2.0 compatibility but should be avoided for new apps.
+ -
+Redirect URIs must be compared using exact string matching by the Authorization Server.
+
+
+
+ OAuth 2.1 (draft) References
+
+
+ - OAuth 2.1 draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13
+ - Aaron Parecki: https://aaronparecki.com/2019/12/12/21/its-time-for-oauth-2-dot-1
+ - FusionAuth: https://fusionauth.io/blog/2020/04/15/whats-new-in-oauth-2-1
+ - Okta: https://developer.okta.com/blog/2019/12/13/oauth-2-1-how-many-rfcs
+ - Video: https://www.youtube.com/watch?v=g_aVPdwBTfw
+ - Differences overview: https://fusionauth.io/learn/expert-advice/oauth/differences-between-oauth-2-oauth-2-1/
+
+
+
+
These aren’t full examples, but demonstrative of the differences between usage for each strategy.
auth_url = client.auth_code.authorize_url(redirect_uri: "http://localhost:8080/oauth/callback")
@@ -1051,7 +1082,7 @@ Examples
-Instagram API (verb‑dependent token mode)
+Verb‑dependent Token Mode
Providers like Instagram require the access token to be sent differently depending on the HTTP verb:
@@ -1062,9 +1093,19 @@ Instagram API (verb‑dependent
Since v2.0.15, you can configure an AccessToken with a verb‑dependent mode. The gem will choose how to send the token based on the request method.
-Example: exchanging and refreshing long‑lived Instagram tokens, and making API calls
+Tips:
-require "oauth2"
+
+ - Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for
GET requests.
+ - If you need a custom rule, you can pass a
Proc for mode, e.g. mode: ->(verb) { verb == :get ? :query : :header }.
+
+
+
+ Instagram API Example
+
+ Example: exchanging and refreshing long‑lived Instagram tokens, and making API calls
+
+ require "oauth2"
# NOTE: Users authenticate via Facebook Login to obtain a short‑lived user token (not shown here).
# See Facebook Login docs for obtaining the initial short‑lived token.
@@ -1119,12 +1160,7 @@ Instagram API (verb‑dependent
# long_lived.post("/me/media", body: {image_url: "https://...", caption: "hello"})
-Tips:
-
-
- - Avoid query‑string bearer tokens unless required by your provider. Instagram explicitly requires it for
GET requests.
- - If you need a custom rule, you can pass a
Proc for mode, e.g. mode: ->(verb) { verb == :get ? :query : :header }.
-
+
Refresh Tokens
@@ -1371,12 +1407,14 @@ Making Raw Token Requests
})
-OpenID Connect (OIDC) Notes
+OpenID Connect (OIDC)
- - If the token response includes an
id_token (a JWT), this gem surfaces it but does not validate the signature. Use a JWT library and your provider’s JWKs to verify it.
- - For private_key_jwt client authentication, provide
auth_scheme: :private_key_jwt and ensure your key configuration matches the provider requirements.
- - See OIDC.md for a more complete OIDC overview, example, and links to the relevant specifications.
+ - If the token response includes an
id_token (a JWT), this gem surfaces it in token.params['id_token'].
+ -
+Note: This gem does not validate the signature of the
id_token. You must use a JWT library (like the jwt gem) and your provider’s JWKs to verify it.
+ - For
private_key_jwt client authentication, provide auth_scheme: :private_key_jwt and ensure your key configuration matches the provider requirements.
+ - See OIDC.md for a more complete OIDC overview and examples.
Debugging
@@ -1607,7 +1645,7 @@ Please give the project a star ⭐ ♥
diff --git a/docs/top-level-namespace.html b/docs/top-level-namespace.html
index 3801380c..177ff205 100644
--- a/docs/top-level-namespace.html
+++ b/docs/top-level-namespace.html
@@ -100,7 +100,7 @@ Defined Under Namespace
From d04aa7234ad4f767ecab0143b4362c7c2bdd574f Mon Sep 17 00:00:00 2001
From: Annibelle Boling
Date: Sun, 1 Mar 2026 02:08:29 -0700
Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9D=20Update=20license=20year=20to?=
=?UTF-8?q?=202026?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
LICENSE.txt | 2 +-
README.md | 2 +-
docs/OAuth2.html | 2 +-
docs/OAuth2/AccessToken.html | 2 +-
docs/OAuth2/Authenticator.html | 2 +-
docs/OAuth2/Client.html | 2 +-
docs/OAuth2/Error.html | 2 +-
docs/OAuth2/FilteredAttributes.html | 2 +-
docs/OAuth2/FilteredAttributes/ClassMethods.html | 2 +-
docs/OAuth2/Response.html | 2 +-
docs/OAuth2/Strategy.html | 2 +-
docs/OAuth2/Strategy/Assertion.html | 2 +-
docs/OAuth2/Strategy/AuthCode.html | 2 +-
docs/OAuth2/Strategy/Base.html | 2 +-
docs/OAuth2/Strategy/ClientCredentials.html | 2 +-
docs/OAuth2/Strategy/Implicit.html | 2 +-
docs/OAuth2/Strategy/Password.html | 2 +-
docs/OAuth2/Version.html | 2 +-
docs/_index.html | 2 +-
docs/file.CHANGELOG.html | 2 +-
docs/file.CITATION.html | 2 +-
docs/file.CODE_OF_CONDUCT.html | 2 +-
docs/file.CONTRIBUTING.html | 2 +-
docs/file.FUNDING.html | 2 +-
docs/file.IRP.html | 2 +-
docs/file.LICENSE.html | 4 ++--
docs/file.OIDC.html | 2 +-
docs/file.README.html | 4 ++--
docs/file.REEK.html | 2 +-
docs/file.RUBOCOP.html | 2 +-
docs/file.SECURITY.html | 2 +-
docs/file.THREAT_MODEL.html | 2 +-
docs/file.access_token.html | 2 +-
docs/file.authenticator.html | 2 +-
docs/file.client.html | 2 +-
docs/file.error.html | 2 +-
docs/file.filtered_attributes.html | 2 +-
docs/file.oauth2.html | 2 +-
docs/file.response.html | 2 +-
docs/file.strategy.html | 2 +-
docs/file.version.html | 2 +-
docs/index.html | 4 ++--
docs/top-level-namespace.html | 2 +-
43 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/LICENSE.txt b/LICENSE.txt
index 41c8a807..7b16ce01 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2017-2025 Peter H. Boling, of Galtzo.com, and oauth2 contributors
+Copyright (c) 2017-2026 Peter H. Boling, of Galtzo.com, and oauth2 contributors
Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
diff --git a/README.md b/README.md
index b77014dd..588a6f16 100644
--- a/README.md
+++ b/README.md
@@ -1261,7 +1261,7 @@ See [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright
-
- Copyright (c) 2017 – 2025 Peter H. Boling, of
+ Copyright (c) 2017 – 2026 Peter H. Boling, of
Galtzo.com
diff --git a/docs/OAuth2.html b/docs/OAuth2.html
index 02f70035..db795699 100644
--- a/docs/OAuth2.html
+++ b/docs/OAuth2.html
@@ -373,7 +373,7 @@
diff --git a/docs/OAuth2/AccessToken.html b/docs/OAuth2/AccessToken.html
index 5048dbe4..686f2ca9 100644
--- a/docs/OAuth2/AccessToken.html
+++ b/docs/OAuth2/AccessToken.html
@@ -3077,7 +3077,7 @@
diff --git a/docs/OAuth2/Authenticator.html b/docs/OAuth2/Authenticator.html
index 84ab5896..e0baaca6 100644
--- a/docs/OAuth2/Authenticator.html
+++ b/docs/OAuth2/Authenticator.html
@@ -883,7 +883,7 @@
diff --git a/docs/OAuth2/Client.html b/docs/OAuth2/Client.html
index 8691bc89..32424196 100644
--- a/docs/OAuth2/Client.html
+++ b/docs/OAuth2/Client.html
@@ -2656,7 +2656,7 @@
diff --git a/docs/OAuth2/Error.html b/docs/OAuth2/Error.html
index d99743fd..7679bdb2 100644
--- a/docs/OAuth2/Error.html
+++ b/docs/OAuth2/Error.html
@@ -772,7 +772,7 @@
diff --git a/docs/OAuth2/FilteredAttributes.html b/docs/OAuth2/FilteredAttributes.html
index 5c044127..52a6cd9b 100644
--- a/docs/OAuth2/FilteredAttributes.html
+++ b/docs/OAuth2/FilteredAttributes.html
@@ -335,7 +335,7 @@
diff --git a/docs/OAuth2/FilteredAttributes/ClassMethods.html b/docs/OAuth2/FilteredAttributes/ClassMethods.html
index 492bb415..4d8dd37e 100644
--- a/docs/OAuth2/FilteredAttributes/ClassMethods.html
+++ b/docs/OAuth2/FilteredAttributes/ClassMethods.html
@@ -472,7 +472,7 @@
diff --git a/docs/OAuth2/Response.html b/docs/OAuth2/Response.html
index 8478cfcc..4d3aedc7 100644
--- a/docs/OAuth2/Response.html
+++ b/docs/OAuth2/Response.html
@@ -1619,7 +1619,7 @@
diff --git a/docs/OAuth2/Strategy.html b/docs/OAuth2/Strategy.html
index 962f555e..e4d80de8 100644
--- a/docs/OAuth2/Strategy.html
+++ b/docs/OAuth2/Strategy.html
@@ -107,7 +107,7 @@ Defined Under Namespace
diff --git a/docs/OAuth2/Strategy/Assertion.html b/docs/OAuth2/Strategy/Assertion.html
index c8242212..83b0a56b 100644
--- a/docs/OAuth2/Strategy/Assertion.html
+++ b/docs/OAuth2/Strategy/Assertion.html
@@ -481,7 +481,7 @@
diff --git a/docs/OAuth2/Strategy/AuthCode.html b/docs/OAuth2/Strategy/AuthCode.html
index f90030b4..b051aa97 100644
--- a/docs/OAuth2/Strategy/AuthCode.html
+++ b/docs/OAuth2/Strategy/AuthCode.html
@@ -483,7 +483,7 @@
diff --git a/docs/OAuth2/Strategy/Base.html b/docs/OAuth2/Strategy/Base.html
index bbe124eb..11c634f0 100644
--- a/docs/OAuth2/Strategy/Base.html
+++ b/docs/OAuth2/Strategy/Base.html
@@ -195,7 +195,7 @@
diff --git a/docs/OAuth2/Strategy/ClientCredentials.html b/docs/OAuth2/Strategy/ClientCredentials.html
index 312cef64..f233fa6f 100644
--- a/docs/OAuth2/Strategy/ClientCredentials.html
+++ b/docs/OAuth2/Strategy/ClientCredentials.html
@@ -343,7 +343,7 @@
diff --git a/docs/OAuth2/Strategy/Implicit.html b/docs/OAuth2/Strategy/Implicit.html
index 52254aa0..3b12ec0d 100644
--- a/docs/OAuth2/Strategy/Implicit.html
+++ b/docs/OAuth2/Strategy/Implicit.html
@@ -420,7 +420,7 @@
diff --git a/docs/OAuth2/Strategy/Password.html b/docs/OAuth2/Strategy/Password.html
index 19be718c..819e0946 100644
--- a/docs/OAuth2/Strategy/Password.html
+++ b/docs/OAuth2/Strategy/Password.html
@@ -374,7 +374,7 @@
diff --git a/docs/OAuth2/Version.html b/docs/OAuth2/Version.html
index 5b1c205f..19966458 100644
--- a/docs/OAuth2/Version.html
+++ b/docs/OAuth2/Version.html
@@ -111,7 +111,7 @@
diff --git a/docs/_index.html b/docs/_index.html
index 5f892b00..caafcc49 100644
--- a/docs/_index.html
+++ b/docs/_index.html
@@ -348,7 +348,7 @@ Namespace Listing A-Z
diff --git a/docs/file.CHANGELOG.html b/docs/file.CHANGELOG.html
index 3c7a3830..b2947ec3 100644
--- a/docs/file.CHANGELOG.html
+++ b/docs/file.CHANGELOG.html
@@ -1322,7 +1322,7 @@
diff --git a/docs/file.CITATION.html b/docs/file.CITATION.html
index 4f74485d..eba7442c 100644
--- a/docs/file.CITATION.html
+++ b/docs/file.CITATION.html
@@ -82,7 +82,7 @@
diff --git a/docs/file.CODE_OF_CONDUCT.html b/docs/file.CODE_OF_CONDUCT.html
index 3ccc9955..cbeb7767 100644
--- a/docs/file.CODE_OF_CONDUCT.html
+++ b/docs/file.CODE_OF_CONDUCT.html
@@ -191,7 +191,7 @@ Attribution
diff --git a/docs/file.CONTRIBUTING.html b/docs/file.CONTRIBUTING.html
index aec9b289..258f8044 100644
--- a/docs/file.CONTRIBUTING.html
+++ b/docs/file.CONTRIBUTING.html
@@ -304,7 +304,7 @@ Manual process
diff --git a/docs/file.FUNDING.html b/docs/file.FUNDING.html
index 4bf4bc21..b9bcffdb 100644
--- a/docs/file.FUNDING.html
+++ b/docs/file.FUNDING.html
@@ -99,7 +99,7 @@ Another Way to Support Open
diff --git a/docs/file.IRP.html b/docs/file.IRP.html
index 2e058620..ee5c595c 100644
--- a/docs/file.IRP.html
+++ b/docs/file.IRP.html
@@ -211,7 +211,7 @@ Appendix: Example checklist
diff --git a/docs/file.LICENSE.html b/docs/file.LICENSE.html
index b13a0af7..590a43a5 100644
--- a/docs/file.LICENSE.html
+++ b/docs/file.LICENSE.html
@@ -57,10 +57,10 @@
- MIT License
Copyright (c) 2017-2025 Peter H. Boling, of Galtzo.com, and oauth2 contributors
Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+ MIT License
Copyright (c) 2017-2026 Peter H. Boling, of Galtzo.com, and oauth2 contributors
Copyright (c) 2011-2013 Michael Bleigh and Intridea, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
diff --git a/docs/file.OIDC.html b/docs/file.OIDC.html
index 164eb4be..c61ee552 100644
--- a/docs/file.OIDC.html
+++ b/docs/file.OIDC.html
@@ -256,7 +256,7 @@ Raw OIDC with ruby-oauth/oauth2
diff --git a/docs/file.README.html b/docs/file.README.html
index b5574fb1..bec62448 100644
--- a/docs/file.README.html
+++ b/docs/file.README.html
@@ -1599,7 +1599,7 @@ © Copyright
-
- Copyright (c) 2017 – 2025 Peter H. Boling, of
+ Copyright (c) 2017 – 2026 Peter H. Boling, of
Galtzo.com
@@ -1645,7 +1645,7 @@
Please give the project a star ⭐ ♥
diff --git a/docs/file.REEK.html b/docs/file.REEK.html
index a9332509..967ab7e7 100644
--- a/docs/file.REEK.html
+++ b/docs/file.REEK.html
@@ -61,7 +61,7 @@
diff --git a/docs/file.RUBOCOP.html b/docs/file.RUBOCOP.html
index 8abc32e6..72d256e2 100644
--- a/docs/file.RUBOCOP.html
+++ b/docs/file.RUBOCOP.html
@@ -161,7 +161,7 @@ Benefits of rubocop_gradual
diff --git a/docs/file.SECURITY.html b/docs/file.SECURITY.html
index e341a941..aa31edb4 100644
--- a/docs/file.SECURITY.html
+++ b/docs/file.SECURITY.html
@@ -93,7 +93,7 @@ Additional Support
diff --git a/docs/file.THREAT_MODEL.html b/docs/file.THREAT_MODEL.html
index ca7b357b..dc3ccd32 100644
--- a/docs/file.THREAT_MODEL.html
+++ b/docs/file.THREAT_MODEL.html
@@ -206,7 +206,7 @@ 8. References
diff --git a/docs/file.access_token.html b/docs/file.access_token.html
index e45013f8..98b3421e 100644
--- a/docs/file.access_token.html
+++ b/docs/file.access_token.html
@@ -84,7 +84,7 @@
diff --git a/docs/file.authenticator.html b/docs/file.authenticator.html
index 2209675f..ba4a93ad 100644
--- a/docs/file.authenticator.html
+++ b/docs/file.authenticator.html
@@ -81,7 +81,7 @@
diff --git a/docs/file.client.html b/docs/file.client.html
index f9161b88..19755b28 100644
--- a/docs/file.client.html
+++ b/docs/file.client.html
@@ -111,7 +111,7 @@
diff --git a/docs/file.error.html b/docs/file.error.html
index bf58887a..116b662b 100644
--- a/docs/file.error.html
+++ b/docs/file.error.html
@@ -68,7 +68,7 @@
diff --git a/docs/file.filtered_attributes.html b/docs/file.filtered_attributes.html
index 845bedda..174cc722 100644
--- a/docs/file.filtered_attributes.html
+++ b/docs/file.filtered_attributes.html
@@ -66,7 +66,7 @@
diff --git a/docs/file.oauth2.html b/docs/file.oauth2.html
index 4f12cc26..5e134511 100644
--- a/docs/file.oauth2.html
+++ b/docs/file.oauth2.html
@@ -69,7 +69,7 @@
diff --git a/docs/file.response.html b/docs/file.response.html
index ef886db7..397e64f3 100644
--- a/docs/file.response.html
+++ b/docs/file.response.html
@@ -77,7 +77,7 @@
diff --git a/docs/file.strategy.html b/docs/file.strategy.html
index c810606e..9da9d508 100644
--- a/docs/file.strategy.html
+++ b/docs/file.strategy.html
@@ -93,7 +93,7 @@
diff --git a/docs/file.version.html b/docs/file.version.html
index 3afe48a9..1d2c0ebd 100644
--- a/docs/file.version.html
+++ b/docs/file.version.html
@@ -65,7 +65,7 @@
diff --git a/docs/index.html b/docs/index.html
index 34a9f595..78d42d62 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1599,7 +1599,7 @@ © Copyright