Announcing oauth2 v2.0.17 — Static Hash for verb‑dependent token mode (Instagram‑friendly)
The oauth2 v2.0.17 is a small but useful release: it adds support for configuring verb‑dependent token transmission with a static Hash in addition to the previously available Proc. This makes integrations like Instagram’s Graph API a little simpler and slightly more performant.
TL;DR
- v2.0.15 introduced verb‑dependent token mode, so you could decide per HTTP verb whether the access token should be sent in the query string or the Authorization header.
- v2.0.17 lets you provide that mapping as a static Hash instead of a Proc.
- Example mapping:
{get: :query, post: :header, delete: :header}
.
Why this matters
Some APIs (notably Instagram’s Graph API) require you to send the access token in the query for GET requests (e.g., ?access_token=...
), but in the Authorization header for POST/DELETE (e.g., Authorization: Bearer ...
).
In v2.0.15 we added support for a verb‑dependent mode via a Proc, like:
{mode: ->(verb) { verb == :get ? :query : :header }}
In v2.0.17 you can now configure the same behavior using a static Hash, which avoids calling a Proc for each request, keeps intent obvious at a glance, and can be trivially serialized or reused:
verb_dependent_token = {get: :query, post: :header, delete: :header}
Example: Instagram Graph API with a static Hash
Below is a concrete example that exchanges a short‑lived token for a long‑lived token, refreshes it, and then makes API calls — all while automatically placing the token in the right place per HTTP verb using the static Hash mode.
require "oauth2"
client = OAuth2::Client.new(nil, nil, site: "https://graph.instagram.com")
# Start with a short‑lived token you already obtained via Facebook Login
verb_dependent_token = {get: :query, post: :header, delete: :header}
short_lived = OAuth2::AccessToken.new(
client,
ENV["IG_SHORT_LIVED_TOKEN"],
mode: verb_dependent_token,
)
# 1) Exchange for a long‑lived token (GET with token in query)
# Endpoint: GET https://graph.instagram.com/access_token
# Params: grant_type=ig_exchange_token, client_secret=APP_SECRET
exchange = short_lived.get(
"/access_token",
params: {
grant_type: "ig_exchange_token",
client_secret: ENV["IG_APP_SECRET"],
# access_token will be added automatically in the query
},
)
long_lived_token_value = exchange.parsed["access_token"]
long_lived = OAuth2::AccessToken.new(
client,
long_lived_token_value,
mode: verb_dependent_token,
)
# 2) Refresh the long‑lived token (GET with token in query)
# Endpoint: GET https://graph.instagram.com/refresh_access_token
refresh_resp = long_lived.get(
"/refresh_access_token",
params: {grant_type: "ig_refresh_token"},
)
long_lived = OAuth2::AccessToken.new(
client,
refresh_resp.parsed["access_token"],
mode: verb_dependent_token,
)
# 3) Typical API GET request (token automatically in query)
me = long_lived.get("/me", params: {fields: "id,username"}).parsed
# 4) Example POST (token automatically in Authorization header)
# long_lived.post("/me/media", body: {image_url: "https://...", caption: "hello"})
Notes
- Instagram is a special case that explicitly requires query‑string tokens for GET endpoints. For most providers you should prefer header‑based tokens when possible.
- If you need custom logic beyond a simple mapping, you can still use a Proc:
mode: ->(verb) { ... }
.
Migrating from Proc to Hash
If you already use the v2.0.15 Proc style:
{mode: ->(verb) { verb == :get ? :query : :header }}
You can switch to the Hash form in v2.0.17:
{mode: {get: :query, post: :header, delete: :header}}
Both are supported; choose whichever best fits your app. The Hash form is generally a bit faster and more explicit, while the Proc form is endlessly versatile!
Release links
- Changelog entry: https://github.com/ruby-oauth/oauth2/blob/main/CHANGELOG.md#2017---2025-09-15
- Tag: https://github.com/ruby-oauth/oauth2/releases/tag/v2.0.17
- PR: Add Hash‑based verb‑dependent mode https://github.com/ruby-oauth/oauth2/pull/682
Thanks
Thanks to everyone using oauth2 and filing issues. Keep the feedback coming!
Support & Funding Info
I am a full-time FLOSS maintainer. If you find my work valuable I ask that you become a sponsor. Every dollar helps!
Photo (cropped) by Wonder KIM on Unsplash
Top comments (0)