# ruby-oauth/oauth2: a low-level OAuth 2.0 client gem, and what its README has to say about that

> A Ruby client library for OAuth 2.0, the OAuth 2.1 draft and OpenID Connect, maintained by a single developer who now does this full time. Where the gem fits, and where the README stops.

**ruby-oauth/oauth2** — 🔐 oauth2 - A Ruby wrapper for the OAuth 2.0, & 2.1 Authorization Frameworks, including OpenID Connect (OIDC)

- Repository: https://github.com/ruby-oauth/oauth2
- Website: https://oauth2.galtzo.com/
- Stars: 2,181 · Forks: 631
- Language: Ruby
- License: NOASSERTION
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/ruby-oauth-oauth2

## A client library, explicitly not a server framework

The Synopsis section is short and specific about scope. OAuth 2.0 is described as the industry-standard protocol for authorization, and this is a RubyGem for implementing OAuth 2.0 clients, with the parenthetical not servers doing real work in the sentence. The README then puts it in its own words: this gem is a low-level OAuth 2.0 client that talks to an authorization server to obtain and use tokens.

That framing matters more for a security library than for most gems, because a client library's job is to get the fiddly parts of a protocol exactly right so an application author does not have to. The description on GitHub says the gem is a wrapper for OAuth 2.0 and 2.1 authorization frameworks including OpenID Connect, and the repository topics list authentication, authorization, oauth2, oauth2-client, oidc, oidc-client and security.

What the gem supports, per the README, includes the OAuth 2.1 draft specification and OpenID Connect. There is a separate OIDC.md at the top of the tree for that half of the story, plus a THREAT_MODEL.md and an IRP.md, which is a good sign for a library that handles credentials.

GitHub reports 2,179 stars, 628 forks and only 3 open issues, with the last push on 2026-09-28 and the default branch `main`. Three open issues on a gem with 628 forks reads as a project with a settled API surface and a narrow maintenance burden.

## Client authentication is where the four schemes come in

The first worked example in the README is a translation exercise: take a curl command against Microsoft identity platform and express it in Ruby. The curl is ordinary client credentials, and the Ruby equivalent shows what the gem actually configures:

```ruby
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 comment on `auth_scheme` is the most useful line in the README. Four modes are named: `:request_body`, `:basic_auth`, `:tls_client_auth` and `:private_key_jwt`. That covers the options a real deployment runs into, and mTLS and private key JWT are the two that most hand-rolled OAuth clients get wrong or skip entirely.

Two other details are worth copying into your own code. Request parameters are passed to `get_token` rather than at client construction, and `token_url` is relative to `site` unless it starts with a slash. The README also flags that the content type in the curl command, application/x-www-form-urlencoded, is already the default in the gem.

## Running the whole thing locally against a mock server

There is a complete end to end example in the repository at `examples/e2e.rb`, and the README documents exactly how to run it. The example targets navikt/mock-oauth2-server, which was added in v2.0.11, and it does not ship inside the released gem, so you need to clone the source to try it.

```console
docker compose -f docker-compose-ssl.yml up -d --wait
ruby examples/e2e.rb
```

The compose file has an ssl variant in its name and the tree carries a `certs/` directory and a `config-ssl.json`, which suggests the example covers TLS rather than plain HTTP for at least part of its run. The mock server serves HTTP on 8080 and the example points there by default.

What the example prints is worth reading, because it shows the shape of an OIDC userinfo response:

```console
userinfo body: {"sub" => "demo-sub", "aud" => ["demo-aud"], "nbf" => 1757816758000, "iss" => "http://localhost:8080/default", "exp" => 1757820358000, "iat" => 1757816758000, "jti" => "d63b97a7-ebe5-4dea-93e6-d542caba6104"}
```

A library that documents a local mock server, a full script and a shutdown command is telling you something about how it expects to be evaluated. There is a long troubleshooting section for when the mock server is not reachable, distinguishing container networking from host networking, and the discovery URL can be overridden with `E2E_ISSUER_BASE` and `E2E_REALM`.

## A repository that reads like a security policy in file form

The top level of the tree is where this project differs from a typical gem. Alongside the expected Gemfile, Rakefile and CHANGELOG.md there is SECURITY.md, THREAT_MODEL.md, CODE_OF_CONDUCT.md and CITATION.cff, and the automation is unusually visible: a Dangerfile, an overcommit configuration, a pre-commit config, a reek configuration and separate rubocop files with a `.rubocop_gradual.lock`.

The gradual rubocop lock is the detail that catches my eye. It implies a strategy where offenses are recorded and ratcheted rather than fixed all at once, which is how large Ruby codebases deal with a linter they did not start out meeting. There are also qlty configuration files for code health, a `.licenserc.yaml` for automated license header checking, and CI workflows that are named as badges in the README for heads, runtime dependencies at HEAD, truffle ruby, jruby, locked and unlocked dependencies, test coverage and style.

Coverage badges appear four times over, for Codecov, Coveralls and QLTY separately. That redundancy is a hobby, or a hobby that became a job.

There is also a `checksums/` directory, an `Appraisals` file for testing against multiple dependency sets, a `gemfiles/` directory and a devcontainer setup, so local development and multi-version testing are both first-class concerns.

## Licensing stated three ways, and a release note worth reading

Here is a disagreement that a careful reader should resolve rather than accept. GitHub reports this repository's license field as `NOASSERTION`, which is what you get when a license cannot be classified automatically. Against that, the README carries a License MIT badge, and the top of the tree contains both a `LICENSE.md` and a separate `MIT.md`. The workspace is also set up with an OpenCollective configuration and a FUNDING.md.

So the metadata field is the only thing saying anything unclear, and the files and the badge say MIT. Read LICENSE.md, since the presence of two license files is itself worth a glance to see whether they differ or whether one is a pointer.

The release history makes the same point from a different angle. Version 2.0.25 was published on 2026-07-15 and its notes report 100 percent line coverage across 558 lines in 15 files, 97.89 percent branch coverage across 190 branches, and 88.35 percent documented. Those three numbers are printed in the changelog rather than left to a badge, which tells you the maintainer treats them as a deliverable.

That same release added JRuby 10.1 and TruffleRuby 34.0 support, raised the runtime floor for `anonymous_loader` to 0.1.3 and for `auth-sanitizer` to 0.2.3, regenerated project metadata and CI workflows with kettle-jem v7.0.0, and fixed license file configuration in the packaged gem. The `auth-sanitizer` dependency is itself a signal: this gem pulls in a dedicated sanitizer for authorization values rather than hand-rolling one.

## The alternatives table, and the README that cuts off

The last section of the README is a comparison table of other libraries, with this gem included as the first row for comparison. The README tells you why it is there: if the low-level client role is not what you need, one of the other libraries may be a better fit.

The table itself is cut off partway through, mid row, with no visible library names beyond the framing text. That is a real limitation of working from this page, and it is worth saying plainly rather than guessing at what the alternatives are. What you can see is the shape of the comparison: a Library column, a Role column, and a When to use it column. The first row is this gem, labelled as an OAuth 2.0 client that talks to an authorization server.

So the practical guidance is to treat the alternatives question as open and check it yourself. If you need an authorization server in Ruby rather than a client, this gem is explicitly the wrong tool, and there are dedicated server libraries. If you need a client, the four authentication schemes plus OIDC support plus a locally runnable example script are a specific enough feature list to evaluate against whatever you use now.

The homepage is oauth2.galtzo.com, which is the maintainer's own site rather than a shared project page, and the README's social infrastructure is a Discord server, a Mastodon presence, and a blog post about the September 2025 hostile takeover of RubyGems, which is a whole separate piece of context the maintainer clearly felt mattered for how this ecosystem is run.

## Conclusion

The distinguishing thing about this gem is that it does not try to be an OAuth framework. It is a low-level client that talks to an authorization server, it supports all four client authentication schemes rather than one, and it ships a documented path for running against a mock server locally with docker compose and a single script. Two things to weigh before adopting it. First, licensing is stated three ways: GitHub reports the license field as NOASSERTION, the tree carries both LICENSE.md and MIT.md, and the README's badge says MIT, so read the files rather than the metadata. Second, the README's comparison table of alternative libraries is cut off mid-row, which is a good reason to browse the alternatives section yourself. Start from the client_credentials example in the README, and use `examples/e2e.rb` against the mock server before pointing anything at a real provider.

## FAQ

### Is OAuth 2.0 obsolete?

The gem's answer is no, but it also tracks the update. Its description says it wraps OAuth 2.0 and 2.1 authorization frameworks, and the README states that it includes the OAuth 2.1 draft specification alongside OpenID Connect. OAuth 2.1 is a consolidation draft that removes the deprecated flows rather than a replacement protocol, so the practical reading is that OAuth 2.0 clients still need to exist and this one is being kept current against the draft.

### What client authentication methods does the oauth2 gem support?

Four. A comment in the README's first example names them: :request_body, :basic_auth, :tls_client_auth and :private_key_jwt. The default in the example is :request_body, which matches how the equivalent curl command sends credentials in a form-encoded body.

### Is this gem an OAuth server or an OAuth client?

A client. The README is explicit that this is a RubyGem for implementing OAuth 2.0 clients, not servers, and calls it a low-level client that talks to an authorization server to obtain and use tokens. The final section points readers to other libraries when the low-level client role is not what they need.

### How do I test the oauth2 gem without calling a real provider?

The repository ships an end to end example script at examples/e2e.rb that targets navikt/mock-oauth2-server. You start it with docker compose against docker-compose-ssl.yml, run the script, and shut it down. The example does not ship with the released gem, so you need to clone the source to run it.

## Sources

- [Issues](https://github.com/ruby-oauth/oauth2/issues)
- [Project website](https://oauth2.galtzo.com/)
- [README](https://github.com/ruby-oauth/oauth2/blob/main/README.md)
- [Releases](https://github.com/ruby-oauth/oauth2/releases)
- [ruby-oauth/oauth2 on GitHub](https://github.com/ruby-oauth/oauth2)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ruby-oauth-oauth2
