Cjoerg
Cjoerg

Reputation: 1325

How to get the permanent token for Shopify (Rails)?

I am trying to authenticate my new Shopify app. First, my authenticate method redirects the shop owner to Shopify's authentication page:

def authenticate
  ShopifyAPI::Session.setup({:api_key => "123", :secret => "456"})
  session = ShopifyAPI::Session.new("someshop.myshopify.com")
  redirect_to session.create_permission_url(["read_orders"], "https://myapp.com/shopify/post_authenticate?user=someshop")
end

Once the shop owner has approved the integration, the redirect uri triggers my post_authenticate method:

def post_authenticate
  ShopifyAPI::Session.setup({:api_key => "123", :secret => "456"})
  session = ShopifyAPI::Session.new("#{params[:user]}.myshopify.com")
  token = session.request_token(:code => params[:code], :signature => params[:signature], :timestamp => params[:timestamp])
end

But the request_token method returns the following error:

#<ShopifyAPI::ValidationException: Invalid Signature: Possible malicious login>

I have read somewhere that you need to be in the same ShopifyAPI session while doing all of this, but it does not say so in the documentation. And the example app takes a very different approach than the documentation.

Upvotes: 2

Views: 1580

Answers (1)

agmcleod
agmcleod

Reputation: 13621

As per my comment, I utilize the omniauth methodology for authenticating. Here's a gist of the code for reference. https://gist.github.com/agmcleod/7106363317ebd082d3df. Put all the snippets below.

class ApplicationController < ActionController::Base
  protect_from_forgery
  force_ssl
  helper_method :current_shop, :shopify_session

protected

  def current_shop
    @current_shop ||= Shop.find(session[:shop_id]) if session[:shop_id].present?
  end

  def shopify_session
    if current_shop.nil?
      redirect_to new_login_url
    else
      begin
        session = ShopifyAPI::Session.new(current_shop.url, current_shop.token)
        ShopifyAPI::Base.activate_session(session)
        yield
      ensure
        ShopifyAPI::Base.clear_session
      end
    end
  end
end

In my login controller:

def create
  omniauth = request.env['omniauth.auth']
  if omniauth && omniauth[:provider] && omniauth[:provider] == "shopify"
    shop = Shop.find_or_create_by_url(params[:shop].gsub(/https?\:\/\//, ""))
    shop.update_attribute(:token, omniauth['credentials'].token)
    shopify_session = ShopifyAPI::Session.new(shop.url, shop.token)
    session[:shop_id] = shop.to_param
    redirect_to root_url
  else
    flash[:error] = "Something went wrong"
    redirect_to root_url
  end
end

config/initializers/omniauth.rb

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, Settings.api_key, Settings.api_secret,
    scope: 'write_products,write_script_tags,read_orders',
    setup: lambda { |env| params = Rack::Utils.parse_query(env['QUERY_STRING'])
                        env['omniauth.strategy'].options[:client_options][:site] = "http://#{params['shop']}" }
end

Then in your routes file, map the create action of your session appropriately:

match '/auth/shopify/callback', :to => 'login#create'

From there i use the shopify_session method as an around filter on the appropriate controllers.

Upvotes: 2

Related Questions