Shopify/omniauth-shopify-oauth2

OmniAuth::Strategies::Shopify.valid_site? and local environment

andreas-sotnik opened this issue ยท 15 comments

To test my Shopify Application I use my local environment with the url https://127.0.0.1:3000/. I set up Shopify application for the correct redirect urls (and other settings), get all the API keys and when I try to login in Shopify Store admin, I see the error:

OmniAuth::Error at /auth/shopify
invalid_site

I found the reason. It's the method: OmniAuth::Strategies::Shopify.valid_site?

def valid_site?
  !!(/\A(https|http)\:\/\/[a-zA-Z0-9][a-zA-Z0-9\-]*\.#{Regexp.quote(options[:myshopify_domain])}[\/]?\z/ =~ options[:client_options][:site])
end

This method doesn't allow to use local environment urls to sign in (while my staging and production environments on Heroku work just fine).

A few versions ago I was be able to sign in for local environment as well, but after last update (bundle update) gems were updated and I stuck with this issue.

Can confirm

I'm also stuck here, My app is working fine on heroku but when i use local env to test it, it raises this error
invalid site

@Shine18

I suggest the following temporary fix:

  1. Fork this repo
  2. Patch file lib/omniauth/strategies/shopify.rb with something like this (fix their valid_site? method):
def valid_site?
  (/(localhost|127.0.0.1)/ =~ options[:client_options][:site]) || (!!(/\A(https|http)\:\/\/[a-zA-Z0-9][a-zA-Z0-9\-]*\.#{Regexp.quote(options[:myshopify_domain])}[\/]?\z/ =~ options[:client_options][:site]))
end
  1. In your Gemfile use fixed version:
gem 'omniauth-shopify-oauth2', github: '<YOUR USERNAME>/omniauth-shopify-oauth2', ref: '<REF OF YOUR FIX>' (or without ref at all)

It should work well with other gems like shopify_app (this is my case).

I know it may not be the best solution but it works for me. And once they will fix and close this issue (if ever) then we will be able to get back to the original repository.

Thanks,i will give it a try!

@andreas-sotnik It works, Thanks man!

@Shine18 Happy to help!

And I think I found even better solution that does not require forking the repo.

  1. Just use gem 'omniauth-shopify-oauth2' in Gemfile as described in the documentation
  2. Create a file config/initializers/omniauth_shopify_oauth2.rb with the following content:
if Rails.env.development?
  module OmniAuth
    module Strategies
      class Shopify
        class << self
          def valid_site?
            true # WE DON'T EVEN NEED SPECIAL CHECKS HERE AS THIS CODE WORKS ONLY ON DEVELOPMENT ENVIRONMENT
          end
        end
      end
    end
  end
end

This way we can get all the updates for original gem without having to constantly syncing it with the fork.

Make sure you do not use this type of patch in production. Not validating that the site ends in .myshopify.com will result in key security assumptions being broken in your app, and potentially security issues putting merchant data at risk of being compromised.

@EiNSTeiN- Yes, I already edited my comment and added if Rails.env.development? check there. Thanks!

I just started seeing this today for the first time. I don't know what caused the error, since I just started the local server like the usual, except that this time, I can't login successfully.

Update: I only see this when I open the app in Shopify admin and I'm not logged in, in the app. I get the login prompt inside the iframe on shopify. Perhaps having the shopify domain perform a full page redirect to localhost/auth/shopify is what causes the error.

I did some tracing on my test app and isolate the code that produces the problem.

shopify_app/lib/generators/shopify_app/install/templates/shopify_provider.rb
Specifically, line 8 in the above file.

shopify_auth_params = strategy.session['shopify.omniauth_params']&.with_indifferent_access

During request phase, strategy.session is available and return the expected value. However, during callback phase, strategy.session is not loaded. I introspect it during a pry session. Session is a ActionDispatch::Request::Session. session.empty? return true. session.exists? return nil.

In heroku, the session is loaded and exist in both phases.

Hi all, I've made session the default method for storing the shop. Please try latest master and feel free to reopen if you have issues.

I'm having issues with this change. How does the value get set on the session? I'm not using shopify_app. I'm generating the route with devise in a Rails app and using the devise initializer instead of the omniauth initializer in config/initializers/devise.rb like so

  config.omniauth :shopify, ENV['SHOPIFY_CLIENT_ID'], ENV['SHOPIFY_CLIENT_SECRET'], {
    scope: "read_products, write_products, read_orders, write_orders, read_fulfillments, write_fulfillments"
  }

When I inspect the values incoming to omniauth/strategies/shopify.rb I find my shop in strategy.session['omniauth.params']['shop'] and strategy.session['shopify.omniauth_params'] is nil. How can I make sure this is set correctly to provide strategy.session['shopify.omniauth_params'] set this correctly in my code?

I'm running into this error as well. I have tried overwriting #valid_site?. I have also added the shop query param that links to a development site I made in their partner portal. Still getting the invalid site error

I was experiencing the same error, this solution worked for me:

ShopifyApp.configure do |config|
...
  config.enable_same_site_none = Rails.env.production?
end

I had the same problem, when I load:

https://mycoolshopifyapp.com/login?shop=myfoo.myshopify.com

I got the "OmniAuth::Error invalid_site" message. In my case I could fixed it. According to the routes, the "/login" GET path triggers the "new" method:

login GET /login(.:format) shopify_app/sessions#new

if params[:shop] exists, shopify_app/sessions#create method is called.

If the browser is not Safari, the file:

app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb

is rendered and there is an automatic redirection in its code, instead of:

<%= form_tag '/auth/shopify', id: 'redirect-form' %>

it should be:

<%= form_tag '/auth/shopify', id: 'redirect-form' do %>
    <%= hidden_field_tag(:shop, params['shop']) %>
<% end %>

since /auth/shopify is expecting the "shop" param because in your file config/initializers/omniauth.rb you have something like:

setup: lambda { |env|
      strategy = env['omniauth.strategy']

      shop = if strategy.request.params['shop']

Now all works like a charm.

Btw, the POST /auth/shopify route doesn't exist, I guess OmniAuth intercepts the request in the middleware, and executes setup_phase.

At the end OmniAuth redirects to:

https://myfoo.myshopify.com/admin/oauth/authorize?client_id=8bfake_c13092&redirect_uri=https%3A%2F%2Fmycoolshopifyapp.com/%2Fauth%2Fshopify%2Fcallback&response_type=code&scope=write_script_tags&state=d2f7b62e940a34ea25f93065ff08b05d98aae39803a1ab91

Note the "state" param is set, so we can avoid the message:

OmniAuth::Strategies::OAuth2::CallbackError (csrf_detected | CSRF detected)

Don't use provider_ignores_state: true, in your config/initializers/omniauth.rb, since it's a security risk. Shopify sets the "state" value properly. If you see the CSRF error, delete all your cookies and sessions.