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×tamp=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×tamp=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?
arround_filter :shopify_session
causes redirection to login and then auth and then login and ... – Pooya