Oauth2
🔐 oauth2 - A Ruby wrapper for the OAuth 2.0, & 2.1 Authorization Frameworks, including OpenID Connect (OIDC)
Install / Use
/learn @ruby-oauth/Oauth2README
🔐 OAuth 2.0 Authorization Framework
[![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, as I may have missed the discord notification.
if ci_badges.map(&:color).all? { it == "green"} 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
[![OpenCollective Backers][🖇osc-backers-i]][🖇osc-backers] [![OpenCollective Sponsors][🖇osc-sponsors-i]][🖇osc-sponsors] [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate on Polar][🖇polar-img]][🖇polar] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
<details> <summary>👣 How will this project approach the September 2025 hostile takeover of RubyGems? 🚑️</summary>I've summarized my thoughts in this blog post.
</details>🌻 Synopsis
OAuth 2.0 is the industry-standard protocol for authorization. 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
<details markdown="1"> <summary>Convert the following `curl` command into a token request using this gem...</summary>curl --request POST \
--url 'https://login.microsoftonline.com/REDMOND_REDACTED/oauth2/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=REDMOND_CLIENT_ID \
--data client_secret=REDMOND_CLIENT_SECRET \
--data resource=REDMOND_RESOURCE_UUID
NOTE: In the ruby version below, certain params are passed to the get_token call, instead of the client creation.
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",
)
client.
client_credentials. # There are many other types to choose from!
get_token(resource: "REDMOND_RESOURCE_UUID")
NOTE: header - The content type specified in the curl is already the default!
- E2E example uses navikt/mock-oauth2-server, which was added in v2.0.11
- E2E example does not ship with the released gem, so clone the source to play with it.
docker compose -f docker-compose-ssl.yml up -d --wait
ruby examples/e2e.rb
# If your machine is slow or Docker pulls are cold, increase the wait:
E2E_WAIT_TIMEOUT=120 ruby examples/e2e.rb
# The mock server serves HTTP on 8080; the example points to http://localhost:8080 by default.
The output should be something like this:
➜ ruby examples/e2e.rb
Access token (truncated): eyJraWQiOiJkZWZhdWx0...
userinfo status: 200
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"}
E2E complete
Make sure to shut down the mock server when you are done:
docker compose -f docker-compose-ssl.yml down
Troubleshooting: validate connectivity to the mock server
- Check container status and port mapping:
docker compose -f docker-compose-ssl.yml ps
- From the host, try the discovery URL directly (this is what the example uses by default):
curl -v http://localhost:8080/default/.well-known/openid-configuration- If that fails immediately, also try:
curl -v --connect-timeout 2 http://127.0.0.1:8080/default/.well-known/openid-configuration
- From inside the container (to distinguish container vs. host networking):
docker exec -it oauth2-mock-oauth2-server-1 curl -v http://127.0.0.1:8080/default/.well-known/openid-configuration
- Simple TCP probe from the host:
nc -vz localhost 8080 # or: ruby -rsocket -e 'TCPSocket.new("localhost",8080).close; puts "tcp ok"'
- Inspect which host port 8080 is bound to (should be 8080):
docker inspect -f '{{ (index (index .NetworkSettings.Ports "8080/tcp") 0).HostPort }}' oauth2-mock-oauth2-server-1
- Look at server logs for readiness/errors:
docker logs -n 200 oauth2-mock-oauth2-server-1
- On Linux, ensure nothing else is bound to 8080 and that firewall/SELinux aren’t blocking:
ss -ltnp | grep :8080
Notes
- Discovery URL pattern is:
http://localhost:8080/<realm>/.well-known/openid-configuration, where<realm>defaults todefault. - You can change these with env vars when running the example:
E2E_ISSUER_BASE(default: http://localhost:8080)E2E_REALM(default: default)
If it seems like you are in the wrong place, you might try one of these:
- OAuth 2.0 Spec
- doorkeeper gem for OAuth 2.0 server/provider implementation.
- oauth sibling gem for OAuth 1.0a implementations in Ruby.
💡 Info you can shake a stick at
| Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] | |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Works with JRuby | ![JRuby 9.1 Compat][💎jruby-9.1i] ![JRuby 9.2 Compat][💎jruby-9.2i] ![JRuby 9.3 Compat][💎jruby-9.3i] <br/> [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf] | | Works with Truffle Ruby | ![Truffle Ruby 22.3 Compat][💎truby-22.3i] ![Truffle Ruby 23.0 Compat][💎truby-23.0i] ![Truffle Ruby 23.1 Compat][💎truby-23.1i] <br/> [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] | | Works with MRI Ruby 3 | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf] | | Works with MRI Ruby 2 | ![Ruby 2.2 Compat][💎ruby-2.2i] <br/> [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf] | | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼🏫expsup-upwork-img]][👨🏼🏫expsup-upwork] [![Get help from me on Codementor][👨🏼🏫expsup-codementor-img]][👨🏼🏫expsup-codementor] | | Source | [