Shopify / Shopify/shopify_app

Ruby 4.0 compatibility: CGI.parse removed in LoginProtection#return_address_with_params

Open Beginner friendly
#2,057 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Ruby
Stars
1.9k
Forks
739
Avg merge
21h 2m
Merged PRs (30d)
1

Description

Summary

LoginProtection#return_address_with_params calls CGI.parse which was removed from Ruby's standard library in Ruby 4.0. This causes a NoMethodError on every OAuth callback, making authentication completely broken on Ruby 4.0.

Affected versions

Confirmed broken in shopify_app 22.5.2 and 23.0.1 (per the source). Ruby 4.0.1.

Location

lib/shopify_app/controller_concerns/login_protection.rb

def return_address_with_params(params)
  uri = URI(base_return_address)
  uri.query = CGI.parse(uri.query.to_s)       # ← NoMethodError on Ruby 4.0
    .symbolize_keys
    .transform_values { |v| v.one? ? v.first : v }
    .merge(params)
    .to_query
  uri.to_s
end

Root cause

CGI was separated from Ruby's stdlib into a standalone gem in Ruby 3.4 (with a deprecation warning) and fully removed in Ruby 4.0. See ruby/cgi#82.

Suggested fix

Replace with Rack::Utils.parse_query, which is already available as a transitive dependency of Rails, returns a plain hash directly (no array-unwrapping needed), and is idiomatic for query string parsing in a Rack/Rails context:

def return_address_with_params(params)
  uri = URI(base_return_address)
  uri.query = Rack::Utils.parse_query(uri.query.to_s)
    .symbolize_keys
    .merge(params)
    .to_query
  uri.to_s
end

This is cleaner than the original because Rack::Utils.parse_query returns string values directly (not arrays), so the .transform_values unwrapping step is no longer needed.

Alternatives considered
  • Add gem 'cgi' to the gemspec: Would restore CGI.parse without code changes, but adds an external dependency purely to keep an awkward API that wraps every value in an array.
  • URI.decode_www_form(...).to_h: Pure stdlib, no extra deps, but more verbose than the Rack alternative.

Workaround

Apps running Ruby 4.0 can monkey-patch the method in an initializer:

# config/initializers/shopify_app_ruby4_compat.rb
module ShopifyApp
  module LoginProtection
    private

    def return_address_with_params(params)
      uri = URI(base_return_address)
      uri.query = URI.decode_www_form(uri.query.to_s).to_h
                    .transform_keys(&:to_sym)
                    .merge(params)
                    .to_query
      uri.to_s
    end
  end
end

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with lib/shopify_app/controller_concerns/login_protection.rb and the return_address_with_params entry point. Reproduce the OAuth callback failure on Ruby 4.0, then verify that query parameters still merge into the return address without NoMethodError. Done means Ruby 4.0 compatibility is restored while preserving the existing URL behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rails, ruby
Domain
authentication
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.