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)