From 48f1579016f181b1f5b959083ce4649bc8c0a707 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: 2025年9月13日 17:55:22 -0600 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=91=B7=20Add=20back=20codeql-analysis?= =?UTF-8?q?.yml=20and=20dependency-review.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/{disabled-workflows => workflows}/codeql-analysis.yml | 0 .github/{disabled-workflows => workflows}/dependency-review.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/{disabled-workflows => workflows}/codeql-analysis.yml (100%) rename .github/{disabled-workflows => workflows}/dependency-review.yml (100%) diff --git a/.github/disabled-workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml similarity index 100% rename from .github/disabled-workflows/codeql-analysis.yml rename to .github/workflows/codeql-analysis.yml diff --git a/.github/disabled-workflows/dependency-review.yml b/.github/workflows/dependency-review.yml similarity index 100% rename from .github/disabled-workflows/dependency-review.yml rename to .github/workflows/dependency-review.yml From 6b5ff6b93947da033d8ea7edf12c7483ffa3149a Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: 2025年9月13日 17:57:10 -0600 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9D=20Apache=20SkyWalking=20Eyes?= =?UTF-8?q?=20(badges)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e3256c73..2493fec4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ⭐️ including OAuth 2.1 draft spec & OpenID Connect (OIDC) -[![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] [![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 Truffle Ruby][🚎9-t-wfi]][🚎9-t-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] +[![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] [![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 Truffle Ruby][🚎9-t-wfi]][🚎9-t-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][🖼️galtzo-discord], as I may have missed the [discord notification][🖼️galtzo-discord]. @@ -73,19 +73,19 @@ If it seems like you are in the wrong place, you might try one of these: ## 💡 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]
[![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]][🚎9-t-wf] [![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]
[![Ruby 2.3 Compat][💎ruby-2.3i]][🚎13-cbs-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] | -| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] | -| Documentation | [![Discussion][⛳gg-discussions-img]][⛳gg-discussions] [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] | -| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] | -| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] | -| Support | [![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] | -| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] | -| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] | +| 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]
[![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]][🚎9-t-wf] [![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]
[![Ruby 2.3 Compat][💎ruby-2.3i]][🚎13-cbs-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] | +| Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] | +| Documentation | [![Discussion][⛳gg-discussions-img]][⛳gg-discussions] [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![Wiki][📜wiki-img]][📜wiki] | +| Compliance | [![License: MIT][📄license-img]][📄license-ref] [![Compatible with Apache Software Projects: Verified by SkyWalking Eyes][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] | +| Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] | +| Support | [![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] | +| Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] | +| `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] | ### Compatibility @@ -1507,6 +1507,8 @@ Thanks for RTFM. ☺️ [🚎13-🔒️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/locked_deps.yml/badge.svg [🚎14-🔓️-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml [🚎14-🔓️-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/unlocked_deps.yml/badge.svg +[🚎15-🪪-wf]: https://github.com/ruby-oauth/oauth2/actions/workflows/license-eye.yml +[🚎15-🪪-wfi]: https://github.com/ruby-oauth/oauth2/actions/workflows/license-eye.yml/badge.svg [💎ruby-2.2i]: https://img.shields.io/badge/Ruby-2.2_(%F0%9F%9A%ABCI)-AABBCC?style=for-the-badge&logo=ruby&logoColor=white [💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3-DF00CA?style=for-the-badge&logo=ruby&logoColor=white [💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge&logo=ruby&logoColor=white @@ -1563,6 +1565,8 @@ Thanks for RTFM. ☺️ [📄license]: LICENSE.txt [📄license-ref]: https://opensource.org/licenses/MIT [📄license-img]: https://img.shields.io/badge/License-MIT-259D6C.svg +[📄license-compat]: https://www.apache.org/legal/resolved.html#category-a +[📄license-compat-img]: https://img.shields.io/badge/Apache_Compatible:_Category_A-✓-259D6C.svg?style=flat&logo=Apache [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat [🚎yard-current]: http://rubydoc.info/gems/oauth2 From f2d6152df7523f9b0470636cc1691369082c7e80 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: 2025年9月13日 17:58:31 -0600 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9D=20CHANGELOG.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41a21eeb..1490f71a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Please file a bug if you notice a violation of semantic versioning. ### Added - Apache SkyWalking Eyes dependency license check ### Changed +- Many improvements to make CI more resilient (past/future proof) ### Deprecated ### Removed ### Fixed From ce89ee70dea2fcf88800d888d8c9cd4a61166917 Mon Sep 17 00:00:00 2001 From: "Peter H. Boling" Date: 2025年9月13日 19:13:07 -0600 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9A=B8=20E2E=20Example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 9 +++ README.md | 57 ++++++++++++++++- config-ssl.json | 22 +++++-- docker-compose-ssl.yml | 4 +- examples/e2e.rb | 138 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 examples/e2e.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 1490f71a..07e2476b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,12 +18,21 @@ Please file a bug if you notice a violation of semantic versioning. ## [Unreleased] ### Added +- E2E example using mock test server added in v2.0.11 + - mock-oauth2-server upgraded to v2.3.0 + - https://github.com/navikt/mock-oauth2-server + - `docker compose -f docker-compose-ssl.yml up -d --wait` + - `ruby examples/e2e.rb` + - `docker compose -f docker-compose-ssl.yml down` + - mock server readiness wait is 90s + - override via E2E_WAIT_TIMEOUT - Apache SkyWalking Eyes dependency license check ### Changed - Many improvements to make CI more resilient (past/future proof) ### Deprecated ### Removed ### Fixed + ### Security ## [2.0.15] - 2025年09月08日 diff --git a/README.md b/README.md index 2493fec4..bd424dbb 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ OAuth 2.0 focuses on client developer simplicity while providing specific author desktop applications, mobile phones, and living room devices. This is a RubyGem for implementing OAuth 2.0 clients (not servers) in Ruby applications. -### Quick Example +### Quick Examples
Convert the following `curl` command into a token request using this gem... @@ -61,6 +61,61 @@ NOTE: `header` - The content type specified in the `curl` is already the default
+
+Complete E2E single file script against [navikt/mock-oauth2-server](https://github.com/navikt/mock-oauth2-server) + +- E2E example using the mock test server added in v2.0.11 + +```console +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: + +```console +➜ 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: + +```console +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//.well-known/openid-configuration, where defaults to "default". +- 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][oauth2-spec] diff --git a/config-ssl.json b/config-ssl.json index f0a8da2e..f906d9e2 100644 --- a/config-ssl.json +++ b/config-ssl.json @@ -1,7 +1,21 @@ { "interactiveLogin": true, "httpServer": { - "type": "NettyWrapper", - "ssl": {} - } -} \ No newline at end of file + "type": "NettyWrapper" + }, + "tokenCallbacks": [ + { + "issuerId": "default", + "requestMappings": [ + { + "requestParam": "grant_type", + "match": "client_credentials", + "claims": { + "sub": "demo-sub", + "aud": ["demo-aud"] + } + } + ] + } + ] +} diff --git a/docker-compose-ssl.yml b/docker-compose-ssl.yml index 9a17fbba..f482d41d 100644 --- a/docker-compose-ssl.yml +++ b/docker-compose-ssl.yml @@ -1,9 +1,9 @@ services: mock-oauth2-server: - image: ghcr.io/navikt/mock-oauth2-server:2.1.11 + image: ghcr.io/navikt/mock-oauth2-server:2.3.0 + restart: unless-stopped ports: - "8080:8080" - hostname: host.docker.internal volumes: - ./config-ssl.json:/app/config.json:Z environment: diff --git a/examples/e2e.rb b/examples/e2e.rb new file mode 100644 index 00000000..22329c1a --- /dev/null +++ b/examples/e2e.rb @@ -0,0 +1,138 @@ +# frozen_string_literal: true + +# End-to-end example using oauth2 gem against a local mock-oauth2-server. +# Prerequisites: +# 1) Start the mock server (HTTP on 8080): +# docker compose -f docker-compose-ssl.yml up -d --wait +# 2) Run this script: +# ruby examples/e2e.rb +# 3) Stop the server when you're done: +# docker compose -f docker-compose-ssl.yml down +# Notes: +# - The mock server uses a self-signed certificate. SSL verification is disabled in this example. +# - Tested down to Ruby 2.4 (avoid newer syntax). + +require "oauth2" +require "json" +require "net/http" +require "uri" + +module E2E + class ClientCredentialsDemo + attr_reader :client_id, :client_secret, :issuer_base, :realm + + # issuer_base: e.g., https://localhost:8080 + # realm: mock-oauth2-server issuer id ("default" by default) + def initialize(client_id, client_secret, issuer_base, realm) + @client_id = client_id + @client_secret = client_secret + @issuer_base = issuer_base + @realm = realm + end + + def run + wait_for_server_ready + well_known = discover + token = fetch_token(well_known) + puts "Access token (truncated): #{token.token[0, 20]}..." + call_userinfo(well_known, token) + puts "E2E complete" + end + + private + + def discovery_url + File.join(@issuer_base, @realm, "/.well-known/openid-configuration") + end + + def wait_for_server_ready(timeout = nil) + timeout = (timeout || ENV["E2E_WAIT_TIMEOUT"] || 90).to_i + uri = URI(discovery_url) + deadline = Time.now + timeout + announced = false + loop do + begin + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == "https" + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri.request_uri) + res = http.request(req) + return if res.code.to_i == 200 + rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ECONNRESET, SocketError, EOFError, OpenSSL::SSL::SSLError + # ignore and retry until timeout + end + unless announced + puts "Waiting for mock OAuth2 server at #{uri} ..." + announced = true + end + break if Time.now>= deadline + sleep(0.5) + end + raise "Server not reachable at #{uri} within #{timeout}s. Ensure it's running: docker compose -f docker-compose-ssl.yml up -d --wait. You can increase the wait by setting E2E_WAIT_TIMEOUT (seconds)." + end + + def discover + uri = URI(discovery_url) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == "https" + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri.request_uri) + res = http.request(req) + unless res.code.to_i == 200 + raise "Discovery failed: #{res.code} #{res.message} - #{res.body}" + end + data = JSON.parse(res.body) + # Expect token_endpoint and possibly userinfo_endpoint + data + end + + def fetch_token(well_known) + client = OAuth2::Client.new( + @client_id, + @client_secret, + site: @issuer_base, + token_url: URI.parse(well_known["token_endpoint"]).request_uri, + ssl: {verify: false}, + auth_scheme: :request_body, # send client creds in request body (compatible default for mock servers) + ) + # Use client_credentials grant + client.client_credentials.get_token + end + + def call_userinfo(well_known, token) + userinfo = well_known["userinfo_endpoint"] + unless userinfo + puts "No userinfo_endpoint advertised by server; skipping userinfo call." + return + end + uri = URI(userinfo) + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = uri.scheme == "https" + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + req = Net::HTTP::Get.new(uri.request_uri) + req["Authorization"] = "Bearer #{token.token}" + res = http.request(req) + puts "userinfo status: #{res.code} #{res.message}" + if res.code.to_i == 200 + begin + body = JSON.parse(res.body) + rescue StandardError + body = res.body + end + puts "userinfo body: #{body.inspect}" + else + puts "userinfo error body: #{res.body}" + end + end + end +end + +if __FILE__ == $PROGRAM_NAME + # These must match the mock server configuration (see config-ssl.json) + client_id = ENV["E2E_CLIENT_ID"] || "demo-client" + client_secret = ENV["E2E_CLIENT_SECRET"] || "demo-secret" + issuer_base = ENV["E2E_ISSUER_BASE"] || "http://localhost:8080" + realm = ENV["E2E_REALM"] || "default" + + E2E::ClientCredentialsDemo.new(client_id, client_secret, issuer_base, realm).run +end

AltStyle によって変換されたページ (->オリジナル) /