3
votes

I've deployed an embedded Shopify application to a production server. In this project, shopify_app gem is used to handle popular scenarios such as authentication using omniauth-shopify-oauth2 gem.
Although the application is installed on a shop, the application does not authenticate properly and as a result, does not store the session. It seems that after giving the control of the application to omniauth-oauth2 gem, it redirects the application to the root path which is expected but because the session is not saved and the controller has arround_filter :shopify_session, an infinite loop is created between /auth/shopify?shop=foobar.myshopify.com and /login?shop=foobar.myshopify.com. Finally, shopify admin panel, raises an shopify the application cannot be loaded, please check that your browser allows third party cookies error and we cannot open the application.

This application works well when the application server is on localhost but does not work on production server

Here are some parts of my code that I think might be useful:

server log:

Started GET "/?hmac=HMAC&shop=SHOP&signature=SIGNATURE&timestamp=TIMESTAMP" for 127.0.0.1 at 2015-04-07 20:33:11 +0000
Processing by MainController#index as HTML
  Parameters: {"hmac"=>"HMAC", "shop"=>"SHOP", "signature"=>"SIGNATURE", "timestamp"=>"TIMESTAMP"}
shop_session: 
Redirected to http://PRODUCTION_SERVER_IP/login?shop=SHOP
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)
Started GET "/login?shop=SHOP" for 127.0.0.1 at 2015-04-07 20:33:11 +0000
Processing by SessionsController#new as HTML
  Parameters: {"shop"=>"SHOP"}
  Rendered common/iframe_redirect.html.erb (0.0ms)
Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.0ms)
Started GET "/auth/shopify?shop=SHOP" for 127.0.0.1 at 2015-04-07 20:33:12 +0000
Started GET "/?code=CODE&hmac=HMAC&shop=SHOP&signature=SIGNATURE&timestamp=1428438796" for 127.0.0.1 at 2015-04-07 20:33:12 +0000
Processing by MainController#index as HTML
  Parameters: {"code"=>"CODE", "hmac"=>"HMAC", "shop"=>"SHOP", "signature"=>"SIGNATURE", "timestamp"=>"1428438796"}
Redirected to http://PRODUCTION_SERVER_IP/login?shop=Shop
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)
Started GET "/login?shop=SHOP" for 127.0.0.1 at 2015-04-07 20:33:13 +0000
Processing by SessionsController#new as HTML
  Parameters: {"shop"=>"SHOP"}
  Rendered common/iframe_redirect.html.erb (0.0ms)
Completed 200 OK in 1ms (Views: 0.4ms | ActiveRecord: 0.0ms)
Started GET "/auth/shopify?shop=SHOP" for 127.0.0.1 at 2015-04-07 20:33:13 +0000
Started GET "/?
// And this pattern goes on...

nginx configuration:

upstream app_name {
  server 127.0.0.1:3000;
  server 127.0.0.1:3001;
  server 127.0.0.1:3002;
}
server {
  listen   80;
  server_name PRODUCTION_SERVER_IP;

  access_log /var/www/app_name/log/access.log;
  error_log  /var/www/app_name/log/error.log;
  root     /var/www/app_name/current;
  index index.html;

  location / {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  Host $http_host;
    proxy_redirect  off;
    try_files /system/maintenance.html $uri $uri/index.html $uri.html @ruby;
  }

  location @ruby {
    proxy_pass http://app_name;
    proxy_set_header Host $host;
  }
}

config/initializers/omniauth.rb:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify,
    ShopifyApp.configuration.api_key,
    ShopifyApp.configuration.secret,

    # Example permission scopes - see http://docs.shopify.com/api/tutorials/oauth for full listing
    scope: 'read_products,read_themes, write_themes, read_customers',
    myshopify_domain: ShopifyApp.configuration.myshopify_domain.presence || "myshopify.com",
    callback_url: 'http://PRODUCTION_SERVER_IP',
    setup: lambda {|env|
             params = Rack::Utils.parse_query(env['QUERY_STRING'])
             site_url = "https://#{params['shop']}"
             env['omniauth.strategy'].options[:client_options][:site] = site_url
           }
end

config/initializers/shopify_session_repository.rb:

ShopifySessionRepository.storage = "Shop"

app/controllers/sessions_controller.rb:

class SessionsController < ApplicationController
  layout :false

  def new
    authenticate if params[:shop]
  end

  def show
    if response = request.env['omniauth.auth']
      sess = ShopifyAPI::Session.new(params[:shop],response['credentials']['token'])    
      session[:shopify] = ShopifySessionRepository.store(sess)
      flash[:notice] = "Logged in"
      redirect_to return_address
    else
      flash[:error] = "Could not log in to Shopify store."
      redirect_to :action => 'new'
    end
  end

  protected

  def authenticate
    #
    # Instead of doing a backend redirect we need to do a javascript redirect
    # here. Open the app/views/commom/iframe_redirect.html.erb file to understand why.
    #
    if shop_name = sanitize_shop_param(params)
      @redirect_url = "/auth/shopify?shop=#{shop_name}"
      render "/common/iframe_redirect", :format => [:html], layout: false
    else
      redirect_to return_address
    end
  end

  def return_address
    session[:return_to] || root_url
  end

  def sanitize_shop_param(params)
    return unless params[:shop].present?
    return unless domain = ShopifyApp.configuration.myshopify_domain.presence || "myshopify.com"

    name = params[:shop].to_s.strip
    name += ".#{domain}" if !name.include?(domain) && !name.include?(".")
    name.sub!(%r|https?://|, '')

    u = URI("http://#{name}")
    u.host.ends_with?(".#{domain}") ? u.host : nil
  end
end

Any idea that on why it is not storing the session after authorization?

4
did you check your callback url and the api key/Shared are correct?alexandresaiz
Yes, callback url is set to production server ip and api keys are correct too. If they were wrong, the application could not get installed on a shop but it is. The problem is that the application is not creating and storing a session for the shop and as a result, arround_filter :shopify_session causes redirection to login and then auth and then login and ...Pooya
@Pooya Did you get any solution?aton1004

4 Answers

0
votes

This might happen if the callback url is the same as the url you use for registration. Try to investigate the request parameters, or give another callback url to return to. You will probably see that the first request is different from the subsequent requests.

0
votes

It seems that changing the following line of code in shopify_app.rb solves the issue. config.embedded_app = false I think it causes new issues though.

0
votes

And a proper solution. Copy and paste the code from the official Shopify App git repo, both the session and callback controller. Then update Shopify API and App gem in your gemfile. It's been fixed recently I think.

0
votes

Enabling third-party cookies in my browser fixed this issue for me.