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
Summary
LoginProtection#return_address_with_paramscallsCGI.parsewhich was removed from Ruby's standard library in Ruby 4.0. This causes aNoMethodErroron every OAuth callback, making authentication completely broken on Ruby 4.0.Affected versions
Confirmed broken in
shopify_app 22.5.2and23.0.1(per the source). Ruby 4.0.1.Location
lib/shopify_app/controller_concerns/login_protection.rbRoot cause
CGIwas 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:This is cleaner than the original because
Rack::Utils.parse_queryreturns string values directly (not arrays), so the.transform_valuesunwrapping step is no longer needed.Alternatives considered
gem 'cgi'to the gemspec: Would restoreCGI.parsewithout 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: