Skip to content

Relax dependencies, fix the SHOPLINE flow, and automate releases - #4

Merged
kfalconer merged 1 commit into
masterfrom
modernize-dependencies
Aug 27, 2026
Merged

kfalconer merged 1 commit into
masterfrom
modernize-dependencies

Conversation

@kfalconer

Copy link
Copy Markdown
Contributor

Prepares the first push of omniauth-shopline to RubyGems.org, following the same treatment as omniauth-fdc and omniauth-shipbob.

Dependencies

spec.add_dependency "omniauth", ">= 2.0", "< 4"
spec.add_dependency "omniauth-oauth2", ">= 1.8", "< 3"

A plugin's ceiling becomes a ceiling in every application that installs it, so this allows the next major of both gems rather than blocking consumers on the day one ships. Note ~> 1.6 was already >= 1.6, < 2.0, so it never blocked a released version — permitting the next major is the only real loosening available.

The floors are where OmniAuth 2 support actually begins (omniauth-oauth2 1.8.0 is its first release to require omniauth ~> 2.0), and gemfiles/floor.gemfile plus a CI job run the whole suite against them, so the range is tested rather than assumed.

Strategy fixes

The integration specs added here found the flow could not complete at all. Fixed against SHOPLINE's docs and its official Go SDK:

  • appKey was always empty. The strategy read options.app_key, which the client_id/client_secret positional arguments never set, so the app secret used for signing was nil too. Now read from the positional credentials, with app_key:/app_secret: accepted as named options.
  • The authorize URL was malformed. OAuth2::Client#authorize_url cannot build a URL for a hash-routed page — it produced /admin/oauth-web/?appKey=...#/oauth/authorize, with the query in front of the fragment where the admin page never reads it.
  • The token request signature was wrong. It used the sorted-parameter scheme SHOPLINE uses for GETs; a POST is signed over the request body concatenated with the millisecond timestamp, and the signed string is now the exact body that is sent.
  • Every callback failed csrf_detected. SHOPLINE has no state parameter, so the nonce now travels in customField (the documented pass-through) and is checked against the session. The sign on the callback query is verified the way SHOPLINE's SDK verifies it. Both checks are opt-out via verify_state: false / verify_signature: false.
  • callback_url doubled a mount prefix, since omniauth 2 moved SCRIPT_NAME into callback_path itself.
  • An error payload called fail! from inside build_access_token and handed the resulting Rack triple back as the access token; it now raises OAuth2::Error, which OmniAuth turns into an invalid_credentials failure.

Also: scope reaches OAuth2::AccessToken under a string key (it was a symbol, so extra['scope'] was always nil), an explicitly configured client_options[:site] now survives, the nonce and signature are compared in constant time, and the unused token_params default is gone.

Packaging and tooling

  • MIT LICENSE.txt declared in the gemspec, along with required_ruby_version and the usual metadata; development scaffolding stays out of the built gem.
  • GitHub Actions: CI (Ruby 3.2–4.0 plus the dependency floor), a weekly bundle-audit, and a release workflow that publishes from the GitHub Releases UI via RubyGems trusted publishing (OIDC, no stored API key).
  • 40 integration specs covering the request phase, the token exchange and its signature, state and signature verification, the auth hash, and the callback_url overrides.

Before releasing

  • Exercise a real store once. verify_signature is the change most likely to need its escape hatch if a callback's parameter shape differs from the docs.
  • Ruby 4.0 is in the matrix and untested locally — this PR run is its first real test. Drop the matrix entry if it is red.
  • Deliberately unchanged: uid is still unset (changing it would alter identity records in a consuming app; extra['handle'] carries the store), and handle is read once at build time, so multi-store means one provider entry per store. Both documented in the README.

🤖 Generated with Claude Code

Prepares the first push to RubyGems.org.

Runtime dependencies become `omniauth >= 2.0, < 4` and
`omniauth-oauth2 >= 1.8, < 3`. A plugin's ceiling becomes a ceiling in every
application that installs it, so this allows the next major of both gems
rather than blocking consumers on the day one ships. The floors are where
OmniAuth 2 support actually begins -- omniauth-oauth2 1.8.0 is its first
release to require `omniauth ~> 2.0` -- and a CI job runs the suite against
those floors so the range is tested rather than assumed.

The integration specs added here found the flow could not complete at all,
so this also fixes, against SHOPLINE's docs and its official Go SDK:

- `appKey` was always empty. The strategy read `options.app_key`, which the
  `client_id`/`client_secret` positional arguments never set, so the app
  secret used for signing was nil too.
- The authorize URL put its query in front of the `#` fragment, because
  `OAuth2::Client#authorize_url` cannot build a URL for a hash-routed page.
  SHOPLINE's admin page never read those params.
- The token request was signed with the sorted-parameter scheme SHOPLINE
  uses for GETs. A POST is signed over the request body concatenated with
  the millisecond timestamp.
- Every callback failed `csrf_detected`. SHOPLINE has no `state` parameter,
  so the nonce now travels in `customField`, the documented pass-through,
  and the `sign` on the callback query is verified the way SHOPLINE's SDK
  verifies it. Both checks are opt-out.
- `callback_url` doubled a mount prefix, since omniauth 2 moved SCRIPT_NAME
  into `callback_path` itself.
- An error payload called `fail!` from inside `build_access_token` and
  handed the resulting Rack triple back as the access token.

Packaging and tooling follow omniauth-fdc and omniauth-shipbob: MIT license
declared in the gemspec, development scaffolding kept out of the built gem,
GitHub Actions for CI and a weekly bundle-audit, and releases published from
the GitHub Releases UI via RubyGems trusted publishing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kfalconer
kfalconer merged commit 98f1682 into master Aug 27, 2026
6 checks passed
@kfalconer
kfalconer deleted the modernize-dependencies branch August 27, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant