DEV Community

Cover image for Ann: oauth2 v2.0.12 (w/ support for kids)
Peter H. Boling
Peter H. Boling

Posted on

Ann: oauth2 v2.0.12 (w/ support for kids)

Photo (cropped) by Johnny Cohen on Unsplash

Before I get to the new kid (IETF RFC 7515 JSON Web Signature - JWS) support, for key discovery and management, I want to highlight a new feature that was in v2.0.11, but not documented until v2.0.12.

I recieved many bug reports related to serialization issues. This is because the SnakyHash returned by parsed did not natively serialize (ye olde dump/load cycle) nicely.

Now it can easily serialize/deserialize to/from JSON.

There are two ways to do this, globally, or discretely. The discrete way is recommended.

Global Serialization Config

Globally configure SnakyHash::StringKeyed to use the serializer. Put this in your code somewhere reasonable (like an initializer for Rails).

SnakyHash::StringKeyed.class_eval do
  extend SnakyHash::Serializer
end
Enter fullscreen mode Exit fullscreen mode

Discrete Serialization Config

Discretely configure a custom Snaky Hash class to use the serializer.

class MySnakyHash < SnakyHash::StringKeyed
  # Give this hash class `dump` and `load` abilities!
  extend SnakyHash::Serializer
end

# And tell your client to use the custom class in each call:
client = OAuth2::Client.new("client_id", "client_secret", site: "https://example.org/oauth2")
token = client.get_token({snaky_hash_klass: MySnakyHash})
Enter fullscreen mode Exit fullscreen mode

Serialization Extensions

These extensions work regardless of whether you used the global or discrete config above.

I duly warn you that these examples are silly. You've been duly warned.

class MySnakyHash < SnakyHash::StringKeyed
  # Give this hash class `dump` and `load` abilities!
  extend SnakyHash::Serializer

  #### Serialization Extentions
  #
  # Act on the non-hash values (including the values of hashes) as they are dumped to JSON
  # In other words, this retains nested hashes, and only the deepest leaf nodes become bananas.
  dump_value_extensions.add(:to_fruit) do |value|
    "banana" # => Make values "banana" on dump
  end

  # Act on the non-hash values (including the values of hashes) as they are loaded from the JSON dump
  # In other words, this retains nested hashes, and only the deepest leaf nodes become ***.
  load_value_extensions.add(:to_stars) do |value|
    "***" # Turn dumped bananas into *** when they are loaded
  end

  # Act on the entire hash as it is prepared for dumping to JSON
  dump_hash_extensions.add(:to_cheese) do |value|
    if value.is_a?(Hash)
      value.transform_keys do |key|
        split = key.split("_")
        first_word = split[0]
        key.sub(first_word, "cheese")
      end
    else
      value
    end
  end

  # Act on the entire hash as it is loaded from the JSON dump
  load_hash_extensions.add(:to_pizza) do |value|
    if value.is_a?(Hash)
      res = self.new
      value.keys.each_with_object(res) do |key, result|
        split = key.split("_")
        last_word = split[-1]
        new_key = key.sub(last_word, "pizza")
        result[new_key] = value[key]
      end
      res
    else
      value
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

See response_spec.rb, or the oauth-xx/snaky_hash gem for more ideas.

IETF RFC 7515 JSON Web Signature - JWS

Yes, now it is time to talk about support for kids.

Why?

The kid parameter is highly recommended in scenarios where a signer has multiple keys or undergoes regular key rotation, as it allows the JWT recipient to efficiently locate the appropriate public key for signature validation.

To facilitate more robust and scalable OAuth2 client implementations, especially for integrations with providers that leverage kid for key discovery and management, this feature becomes essential. It aligns the library's JWT generation capabilities more closely with common production practices and the broader JWT ecosystem.

How?

  • The private method build_assertion within OAuth2::Strategy::Assertion has been modified.
  • It now conditionally checks for a :kid key within the encoding_opts hash passed to get_token.
  • If :kid is provided, it is correctly added as a header parameter to the JWT using JWT.encode(..., headers).

Of course, when supporting kids, I also mean #FreePalestine, and #SayNoToGenocide!

Now for some sweet release notes...

2.0.12 - 2025-05-31

  • TAG: v2.0.12
  • Line Coverage: 100.0% (520 / 520)
  • Branch Coverage: 100.0% (174 / 174)
  • 80.00% documented ## What's Changed
  • feat: Add Key ID (kid) support to JWT assertions by @mridang in https://github.com/oauth-xx/oauth2/pull/652
    • IETF rfc7515 JSON Web Signature - JWS
  • More Documentation by @pboling
    • Documented Serialization Extensions
    • Added Gatzo.com FLOSS logo by @Aboling0, CC BY-SA 4.0
    • Documentation site @ https://oauth2.galtzo.com now complete

New Contributors

Full Changelog: https://github.com/oauth-xx/oauth2/compare/v2.0.11...v2.0.12

Also, since I didn't say much about the v2.0.11 release, here's that:

2.0.11 - 2025-05-23

  • TAG: v2.0.11
  • COVERAGE: 100.00% -- 518/518 lines in 14 files
  • BRANCH COVERAGE: 100.00% -- 172/172 branches in 14 files
  • 80.00% documented ### Added
  • gh651 - :snaky_hash_klass option (@pboling)
  • More documentation
  • Codeberg as ethical mirror (@pboling)
  • Don't check for cert if SKIP_GEM_SIGNING is set (@pboling)
  • All runtime deps, including oauth-xx sibling gems, are now tested against HEAD (@pboling)
  • YARD config, GFM compatible with relative file links (@pboling)
  • Documentation site on GitHub Pages (@pboling)
  • !649 - Test compatibility with all key minor versions of Hashie v0, v1, v2, v3, v4, v5, HEAD (@pboling)
  • gh651 - Mock OAuth2 server for testing (@pboling)
  • gh651 - Upgraded to snaky_hash v2.0.3 (@pboling)
    • Provides solution for serialization issues
  • Updated spec.homepage_uri in gemspec to GitHub Pages YARD documentation site (@pboling)

    Fixed

  • gh650 - Regression in return type of OAuth2::Response#parsed (@pboling)
  • Incorrect documentation related to silencing warnings (@pboling)

Top comments (0)