Giter Site home page Giter Site logo

omniauth-shopify-oauth2's Introduction

Build Status

OmniAuth Shopify

Shopify OAuth2 Strategy for OmniAuth 1.0.

Installing

Add to your Gemfile:

gem 'omniauth-shopify-oauth2'

Then bundle install.

Usage

OmniAuth::Strategies::Shopify is simply a Rack middleware. Read the OmniAuth 1.0 docs for detailed instructions.

Here's a quick example, adding the middleware to a Rails app in config/initializers/omniauth.rb:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, ENV['SHOPIFY_API_KEY'], ENV['SHOPIFY_SHARED_SECRET']
end

Authenticate the user by having them visit /auth/shopify with a shop query parameter of their shop's myshopify.com domain. For example, the following form could be used

<form action="/auth/shopify" method="get">
  <label for="shop">Enter your store's URL:</label>
  <input type="text" name="shop" placeholder="your-shop-url.myshopify.com">
  <button type="submit">Log In</button>
</form>

Or without form /auth/shopify?shop=your-shop-url.myshopify.com Alternatively you can put shop parameter to session as Shopify App do

session['shopify.omniauth_params'] = { shop: params[:shop] }

And finally it's possible to use your own query parameter by overriding default setup method. For example, like below:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify,
    ENV['SHOPIFY_API_KEY'],
    ENV['SHOPIFY_SHARED_SECRET'],
    option :setup, proc { |env|
      strategy = env['omniauth.strategy']



      site = if strategy.request.params['site']
        "https://#{strategy.request.params['site']}"
      else
        ''
      end

      env['omniauth.strategy'].options[:client_options][:site] = site
    }

Configuring

Scope

You can configure the scope, which you pass in to the provider method via a Hash:

  • scope: A comma-separated list of permissions you want to request from the user. See the Shopify API docs for a full list of available permissions.

For example, to request read_products, read_orders and write_content permissions and display the authentication page:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, ENV['SHOPIFY_API_KEY'], ENV['SHOPIFY_SHARED_SECRET'], :scope => 'read_products,read_orders,write_content'
end

Online Access

Shopify offers two different types of access tokens: online access and offline access. You can configure for online-access by passing the per_user_permissions option:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, ENV['SHOPIFY_API_KEY'],
                     ENV['SHOPIFY_SHARED_SECRET'],
                     :scope => 'read_orders',
                     :per_user_permissions => true
end

Authentication Hash

Here's an example Authentication Hash available in request.env['omniauth.auth']:

{
  :provider => 'shopify',
  :uid => 'example.myshopify.com',
  :credentials => {
    :token => 'afasd923kjh0934kf', # OAuth 2.0 access_token, which you store and use to authenticate API requests
  }
}

License

Copyright (c) 2012 by Shopify Inc

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

omniauth-shopify-oauth2's People

Contributors

airhorns avatar andyw8 avatar ccleung avatar christhomson avatar clayton-shopify avatar claytonpassmore avatar clupprich avatar cursedcoder avatar dylanahsmith avatar einstein- avatar garymardell avatar jamiemtdwyer avatar joepym avatar jpotts18 avatar mcrowe avatar mkdynamic avatar mkevinosullivan avatar mllemango avatar mtaher avatar nicday avatar paulomarg avatar peterjm avatar pobed2 avatar ragalie avatar rezaansyed avatar tmlayton avatar tylerball avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

omniauth-shopify-oauth2's Issues

Return online and offline tokens in a single call

Many apps support multiple user accounts, but they still need to make calls to the store in the background using an offline token.

Shopify's Oauth requires two separate calls to obtain different tokens.
This is an issue that prolongs the authentication for many Shopify apps and negatively impact merchant experience.

If the user has the required permissions, the callback can include the offline token as well as the online token. This way apps can get all the information in a single call. This will reduce the auth time significantly.

Updating scopes raises invalid_scope error

setup

  • ruby 2.3.0
  • rails: 4.2.6
  • omniauth-shopify-oauth2: 1.1.14
  • shopify_api: 4.3.0
  • shopify_app: 7.2.0

config/initializers/shopify_app.rb

ShopifyApp.configure do |config|
config.api_key = 'xyz'
config.secret = 'xyz'
config.scope = "write_products"
config.embedded_app = true
end

The current scope in the ShopifyApp initializer is "write_products". I'd like to update the app's permissions on a per user basis to include "write_customers".

I get the user to hit the following url which includes the "write_customers" scope
https://teststore.myshopify.com/admin/oauth/authorize?client_id=xyz&redirect_uri=https://example.com/auth/shopify/callback&response_type=code&scope=write_products,write_customers

The user gets presented with the correct update screen

Upon accepting the updated permissions the user hits an "invalid_scope | Scope does not match" error.

However - the user now has the write_customers permission enabled even though it looks like we hit an error.

Any ideas on how to avoid this error?

Cheers

Steve

Dynamic scope request in setup phase fails scope tampering check

I'm trying to set the requested scopes from the GET request params. The following was working with 1.1.8, perhaps because scope tampering check was introduced in a later version.

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify,
    ShopifyApp.configuration.api_key,
    ShopifyApp.configuration.secret,
    setup: lambda { |env|
      request = Rack::Request.new(env)
      env['omniauth.strategy'].options[:scope] = request.GET['scope']
      env['omniauth.strategy'].options[:client_options][:site] = "https://#{request.GET['shop']}"
    }

I'm running 1.1.17 and this now gives an error:

OmniAuth::Strategies::OAuth2::CallbackError at /auth/shopify/callback
invalid_scope | Scope does not match, it may have been tampered with.

This is similar to #49 and proposed solutions (#49 (comment)) do not work. The validate_granted_scopes value is not being checked before running the scope validation.

Shopify doesn't send back the scopes granted so I wonder how they are being checked against the ones that were requested.

read_products scope throws OmniAuth::Strategies::OAuth2::CallbackError

When I try to install an application with this scope (read_products or write_products), I get an error:

OmniAuth::Strategies::OAuth2::CallbackError
invalid_scope | Scope does not match, it may have been tampered with.

This happens with all my applications with this scopes

Here is the full trace:

Started GET "/auth/shopify" for 37.17.20.204 at 2018-05-02 15:34:29 +0300
Cannot render console from 37.17.20.204! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
I, [2018-05-02T15:34:29.128429 #10178]  INFO -- omniauth: (shopify) Setup endpoint detected, running now.
I, [2018-05-02T15:34:29.128967 #10178]  INFO -- omniauth: (shopify) Request phase initiated.
Started GET "/auth/shopify/callback?code=*CODE*&shop=*SHOP*&state=*STATE*&timestamp=1525264472" for 37.17.20.204 at 2018-05-02 15:34:32 +0300
Cannot render console from 37.17.20.204! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
I, [2018-05-02T15:34:32.491714 #10178]  INFO -- omniauth: (shopify) Setup endpoint detected, running now.
I, [2018-05-02T15:34:32.492135 #10178]  INFO -- omniauth: (shopify) Callback phase initiated.
E, [2018-05-02T15:34:33.271535 #10178] ERROR -- omniauth: (shopify) Authentication failure! invalid_scope: OmniAuth::Strategies::OAuth2::CallbackError, invalid_scope | Scope does not match, it may have been tampered with.

OmniAuth::Strategies::OAuth2::CallbackError (invalid_scope | Scope does not match, it may have been tampered with.):

appsignal (2.5.1) lib/appsignal/rack/rails_instrumentation.rb:30:in `call_with_appsignal_monitoring'
appsignal (2.5.1) lib/appsignal/rack/rails_instrumentation.rb:15:in `call'

Production environment specific problem - Invalid Site

I am running a heroku app with this gem installed in a production environment, the problem is that only in this environment when the oauth calls finishes and the store user authenticates the app, the validation here: https://github.com/Shopify/omniauth-shopify-oauth2/blob/master/lib/omniauth/strategies/shopify.rb#L27, returns false.

This only happens when running production environment on heroku, while running production environment on my local machine works fine.

Btw, i am using cloudflare for DNS and when i route the redirect_uri from my cloudflare dns (e.g. example.com) it doesn't work, but when i do it via the herokuapp subdomain (e.g. myapp.herokuapp.com) it works, and valid_site? returns true.

Memoization - a potential cause for race conditions between threads.

In OmniAuth/Strategies/Shopify.rb - build_access_token method uses memoization which may cause a potential race condition. Refer to https://bearmetal.eu/theden/how-do-i-know-whether-my-rails-app-is-thread-safe-or-not/

  1. Supposed thread A has successfully build the token but yet to assign it to the instance variable.
  2. Thread B access the variable and since it is not yet set, it tries to build the token using same params. This result in error since code can only be used once.

I did have this bug occasionally where I received an error from Shopify. Unfortunately, I could not replicate it.

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

  spec.license = 'MIT'
  # or
  spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

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

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.

omniauth-oauth2 1.4.x will break redirect_uri

When using omniauth-oauth2 1.3.x, the redirect_uri gets passed back to Shopify as something like example.com/auth/shopify/callback

However, when using omniauth-oauth2 1.4.x, the redirect_uri will get passed back to Shopify as example.com/auth/shopify/callback?shop=myshop.myshopify.com

This breaks the oauth2 handshake since the Shopfiy API uses a strict interpretation of the redirect uri, and adds the ?shop= param by itself.

Some possible solutions:

  • Relax the strict checking on Shopify API end
  • Version lock omniauth-oauth2 to 1.3.x
  • Find out what changed in 1.4.x and override the redirect_uri generation

Verify hmac in `setup` callback

I'm opening this issue so I can fix it later on. In the setup callback we set the shop url from the query parameters. Since we allow the authorize phase to be initiated in response to a GET request, it's possible for someone to maliciously force a user to go through the oauth flow, and the app will receive a valid access token in response, all without user interaction. It would be much better to avoid csrf login, so we should verify the hmac and timestamp in the url in the setup callback before initiating the authorize phase.

Why there is no integration for Omniauth 2.0

I was trying to do some gem updates and update the ruby version for my app and I'm having problem updating Omniauth to 2.0 because of this gem. Is there any reason to why there is no integration to omniauth 2.0

build_access_token raises un-rescued Faraday error that bubble to apps using shopify-app

We expect that the underlying omniauth gem will reliably raise a connection errors with a fail when we call build_access_token:

https://github.com/omniauth/omniauth-oauth2/blob/10e1a42c7a49ad4488fe9085f814681e8848fbf6/lib/omniauth/strategies/oauth2.rb#L66-L83

      def callback_phase # rubocop:disable AbcSize, CyclomaticComplexity, MethodLength, PerceivedComplexity
        error = request.params["error_reason"] || request.params["error"]
        if error
          fail!(error, CallbackError.new(request.params["error"], request.params["error_description"] || request.params["error_reason"], request.params["error_uri"]))
        elsif !options.provider_ignores_state && (request.params["state"].to_s.empty? || request.params["state"] != session.delete("omniauth.state"))
          fail!(:csrf_detected, CallbackError.new(:csrf_detected, "CSRF detected"))
        else
          self.access_token = build_access_token
          self.access_token = access_token.refresh! if access_token.expired?
          super
        end
      rescue ::OAuth2::Error, CallbackError => e
        fail!(:invalid_credentials, e)
      rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
        fail!(:timeout, e)
      rescue ::SocketError => e
        fail!(:failed_to_connect, e)
      end

The underlying connection can close, which normally raises a Faraday ConnectionFailed error.

vendor/ruby-2.5.3/lib/ruby/2.5.0/net/protocol.rb:189:in `rbuf_fill': end of file reached (Faraday::ConnectionFailed)
    from vendor/ruby-2.5.3/lib/ruby/2.5.0/net/protocol.rb:157:in `readuntil'
    from vendor/ruby-2.5.3/lib/ruby/2.5.0/net/protocol.rb:167:in `readline'
    from vendor/ruby-2.5.3/lib/ruby/2.5.0/net/http/response.rb:40:in `read_status_line'
    from vendor/ruby-2.5.3/lib/ruby/2.5.0/net/http/response.rb:29:in `read_new'
[snip]

Recently, we saw some internal app fail to authenticate in a new fashion which stress a new failure mode to these request.

Options that I I see:

  1. We rescue in the omniauth-shopify-oauth2 as the oauth code path in shopify-app is merely asking for a token and shouldn't care about why it failed
  2. We push this concern up to the superclass omniauth-oauth2 to make sure Faraday errors are rescued
  3. We let it bubble up to shopify-app and rescue it there

In any event, this should not reach Shopify apps without being first handled.

Authentication failure! invalid_site encountered with iOS App

When accessing an app via the iOS Shopify app, the Authentication failure! invalid_site encountered error is returned. When accessing it a second time, the app loads without issue. This happens on a fresh app, using version 8.2.5 of the shopify_app gem, running on heroku and is repeatable.

I have added some logging to the code and in valid_site? the options[:client_options][:site] is blank the first time the app is accessed, explaining the fact that the app doesn't load.

def valid_site?
puts "SITE NAME: #{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

Output from my logs: omniauth-shopify-oauth2.log

Any clues as to why this is happening?

path_prefix assignment breaks oAuth flow - Please fix this...

Trying to auth for endpoint https://www.fizzbuzz.com/shopify instead of https://www.fizzbuzz.com

used to work (v 1.1.8) with a simple change to config in the rack config file:

use OmniAuth::Builder do

# allow us to connect this App via the /shopify route instead of just the root URL /
configure do |config|
  config.path_prefix = '/shopify/auth'
end

provider :shopify, ENV['SHOPIFY_API_KEY'], ENV['SHOPIFY_API_SECRET'],
       :scope => SCOPE,
       :setup => lambda { |env| params = Rack::Utils.parse_query(env['QUERY_STRING'])
                                env['omniauth.strategy'].options[:client_options][:site] = "https://#{params['shop']}" }
end

Now that change breaks the oAuth flow and the request to authenticate a shop ends up dying without a redirect URI with the response from Shopify:

Using JS to bust out of the iframe with oAuth I do that standard thing... and die:

@redirect_url = "/shopify/auth/shopify?shop=#{shop_name}"

     rendering a redirect now... /shopify/auth/shopify?shop=fixbuzz.myshopify.com
 INFO -- omniauth: (shopify) Setup endpoint detected, running now.
 Request Phase: #<Rack::Request:0x007f0cb28713e0 @env={"SERVER_SOFTWARE"=>"thin 1.6.3 codename Protein Powder", "SERVER_NAME"=>"celebrity-owned.herokuapp.com", "rack.input"=>#<StringIO:0x007f0cb28728d0>, "rack.version"=>[1, 0], "rack.errors"=>#<IO:<STDERR>>, "rack.multithread"=>false, "rack.multiprocess"=>false, "rack.run_once"=>false, "REQUEST_METHOD"=>"GET", "REQUEST_PATH"=>"/shopify/auth/shopify", "PATH_INFO"=>"/shopify/auth/shopify", "QUERY_STRING"=>"shop=fixbuzz.myshopify.com", "REQUEST_URI"=>"/shopify/auth/shopify?shop=fixbuzz.myshopify.com", "HTTP_VERSION"=>"HTTP/1.1", "HTTP_HOST"=>"celebrity-owned.herokuapp.com", "HTTP_CONNECTION"=>"close", "HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "HTTP_UPGRADE_INSECURE_REQUESTS"=>"1", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", "HTTP_REFERER"=>"http://celebrity-owned.herokuapp.com/shopify/login", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate, sdch", "HTTP_ACCEPT_LANGUAGE"=>"en,en-US;q=0.8,fr;q=0.6", "HTTP_X_REQUEST_ID"=>"686bff7a-2acd-40cd-9997-cd421bb0e9b3", "HTTP_X_FORWARDED_FOR"=>"67.70.24.228", "HTTP_X_FORWARDED_PROTO"=>"http", "HTTP_X_FORWARDED_PORT"=>"80", "HTTP_VIA"=>"1.1 vegur", "HTTP_CONNECT_TIME"=>"2", "HTTP_X_REQUEST_START"=>"1441039792615", "HTTP_TOTAL_ROUTE_TIME"=>"0", "GATEWAY_INTERFACE"=>"CGI/1.2", "SERVER_PORT"=>"80", "SERVER_PROTOCOL"=>"HTTP/1.1", "rack.url_scheme"=>"http", "SCRIPT_NAME"=>"", "REMOTE_ADDR"=>"10.238.8.204", "async.callback"=>#<Method: Thin::Connection#post_process>, "async.close"=>#<EventMachine::DefaultDeferrable:0x007f0cb2871ea8>, "rack.session"=>{"session_id"=>"8f2451b624fa1b3eaadf37b986b43efaa1676282db51ea7e8110392902782211", "omniauth.params"=>{"shop"=>"fixbuzz.myshopify.com"}, "omniauth.origin"=>"http://celebrity-owned.herokuapp.com/shopify/login"}, "rack.session.options"=>{:path=>"/", :domain=>nil, :expire_after=>86400, :secure=>true, :httponly=>true, :defer=>false, :renew=>false, :sidbits=>128, :secure_random=>SecureRandom, :secret=>"6d0ea6158819b727fb40e4deaf67887f", :coder=>#<Rack::Session::Cookie::Base64::Marshal:0x007f0cb2408d80>}, "omniauth.strategy"=>#<OmniAuth::Strategies::Shopify>, "rack.request.query_string"=>"shop=fixbuzz.myshopify.com", "rack.request.query_hash"=>{"shop"=>"fixbuzz.myshopify.com"}, "rack.request.cookie_hash"=>{}, "rack.session.unpacked_cookie_data"=>{"session_id"=>"8f2451b624fa1b3eaadf37b986b43efaa1676282db51ea7e8110392902782211"}}, @params={"shop"=>"fixbuzz.myshopify.com"}>
 valid site: domain: myshopify.com, site: https://fixbuzz.myshopify.com
 "POST /shopify/login HTTP/1.1" 200 107 0.0833
 INFO -- omniauth: (shopify) Request phase initiated.
 at=info method=GET path="/shopify/auth/shopify?shop=fixbuzz.myshopify.com" host=celebrity-owned.herokuapp.com request_id=686bff7a-2acd-40cd-9997-cd421bb0e9b3 fwd="67.70.24.228" dyno=web.1 connect=2ms service=71ms status=302 bytes=768

400_-_oauth_error_invalid_request

instead of prompting the user with a permissions screen.

Note that a perfectly good redirect URI is setup in the partner screen for the App too... listed as https://someapp.com/shopify/auth/shopify/callback, a route that used to work fine where the first /shopify was sending the request to the route looking for /auth/:provider/callback where :provider was shopify...

Use both type of access modes

Hi,
As the doc said It’s also possible for an app to use both access modes at the same time, by using different tokens when appropriate. After Googling, I found an similar issue of Nodejs (link)
Can you give some hints so I can archive both access modes in my application with this library or any other Ruby library?

Thank you!

How to update scopes and then allow user to re-auth?

I am developing a Shopify app in Rails that was created using shopify-app-cli. When I built the app I initially set the scope as read_products, but now I realize I need additional scopes.

At first when I just tried to manually generate a new permission URL for my development store with the additional scopes, it gave me issue 49 error. I then updated config.scope in shopify_app.rb to reflect my new scopes and then restarted my ngrok tunnel, but now I am getting a CSRF error (see below). I imagine this is due to a lack of state parameter or not using the correct one.

How do I resolve this without having to recreate a brand new app from scratch with the correct permissions? Thank you.

Screen Shot 2020-08-19 at 2 58 13 PM

build_access_token can bubble up a OAuth2::Error

build_access_token can fail and raise a OAuth2::Error when the response code is 400. For example, "Oauth error invalid_request: The authorization code was not found or was already used"

This doesn't happen often in my app, but it would be nice not to show the user a 500 error page.

Gist of the full HTML response from shopify: https://gist.github.com/jeffblake/13b3c8fe3100f76b0b115a0024029333

Stack trace:

Screen Shot 2019-12-09 at 5 56 41 PM

I'm thinking in the callback_phase method, it should rescue the OAuth2::Error and fail appropriately, e.g.

fail!(:invalid_auth_code, CallbackError.new(:invalid_auth_code, "The authorization code was not found or was already used"))

Users aren't signed into a new shop when clicking app link and already signed in

Background

We just upgraded to the latest version of Shopify app from 7.2.0 to 9.0.1 (omniauth-shopify_oauth2 1.5.0 -> 2.1.0) and we're seeing an issue involving users clicking app links from their shop. We're not using an embedded app.

Scenario

  1. User is signed into app as shop1.myshopify.com
  2. User clicks app link from shop2.myshopify.com
  3. User redirects to app and remains signed in as shop1.myshopify.com

Problem

This seems to me because of the following code:

 option :setup, proc { |env|
        strategy = env['omniauth.strategy']

        shopify_auth_params = strategy.session['shopify.omniauth_params'] && strategy.session['shopify.omniauth_params'].with_indifferent_access
        shop = if shopify_auth_params && shopify_auth_params['shop']
          "https://#{shopify_auth_params['shop']}"
        else
          ''
        end

        strategy.options[:client_options][:site] = shop
}

i.e. we're not checking the params for the request params for the shop domain.

Fix

Changing the code to the following seems to resolve the issue:

  strategy = env["omniauth.strategy"]
  request = Rack::Request.new(env)

  shop = request.params.fetch("shop", strategy.session["shopify.omniauth_params"]&.[](:shop))
  strategy.options[:client_options][:site] = shop ? "https://#{shop}" : ""

i.e. use the shop in params if it exists, or fall back to usual behaviour.

Is there anything wrong with this approach? Am I missing something as to why this isn't the normal behaviour?

running to server

root@instance-2:/mnt/zapstore.com/shopifyapp/zap# rails s
=> Booting Puma
=> Rails 5.1.4 application starting in development
=> Run rails server -h for more startup options
Puma starting in single mode...

  • Version 3.11.0 (ruby 2.4.2-p198), codename: Love Song
  • Min threads: 5, max threads: 5
  • Environment: development
  • Listening on tcp://0.0.0.0:3000
    Use Ctrl-C to stop
    ^C- Gracefully stopping, waiting for requests to finish
    === puma shutdown: 2017-12-18 06:10:58 +0000 ===
  • Goodbye!
    Exiting

valid_site? fails on 2.0.0

2.0.0 broke the shopify omniauth feature in my app and I had to downgrade back to 1.2.1. As I've seen in some other threads, the valid_site? method is returning false because of the change to how the shop value is accessed through the session. It seems like a lot of people are using this gem in conjunction with shopify_app, which I'm not. I'm using Devise and the devise initializer instead of the omniauth initializer to provide the keys and set the scopes we want to request.

When I inspect the values incoming to omniauth/strategies/shopify.rb#valid_site? I find my shop value in strategy.session['omniauth.params']['shop'] and nothing in strategy.session['shopify.omniauth_params']. I'm not sure what is setting this session value or how to tweak it in my code to provide it in the way the that 2.0.0 change expects. Any insight? Thanks

Is anyone willing to work a paid consult on this Gem?

I realize opening an issue may not be the best way to go about this, but I'm looking for someone who can perform a consult regarding the capabilities and implementation of this Gem. I'm ryan.burnette on Skype and I'd be happy to pay for the consult. Thanks!

Issue with OAuth2 flow

Setup: Rails 4, omniauth-1.2.1, omniauth-shopify 0.1.0

config/initializers/omniauth.rb

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, ENV['SHOPIFY_API_KEY'], ENV['SHOPIFY_SHARED_SECRET'],
           scope: ENV['SHOPIFY_API_SCOPE']
end

I start the OAuth web flow by redirecting the user to /auth/shopify?shop=abcdxyz.myshopify.com. This correctly redirects me to the /admin/oauth/authorize page in my shopify store. There I accept the perms. When shopify redirects back to my callback, before it hits my rails controller for the callback, the omniauth strategy in the Rack middleware throws this error

http://cl.ly/image/2S3o3P3k2t38

Thoughts? I'm looking through the omniauth strategy, and the error is when its trying to create the password by appending the token from the strategy. But where does that token get set ? Also what kind of token is that?

can't find admin info

I need to know basic informations about the admin which authenticated with my app but those informations do not seem to be in request.env['omniauth.auth'].
What is the best way to get those infos after authentication ?

[False Positive] invalid_scope, Scope does not match, it may have been tampered with.

An issue presented in v1.1.12 and v1.1.13. Scope validation was added to the shopify auth strategy. The issue exists because we pass the desired scopes as a comma-separated list, and in those gem versions, split that string on commas and did no further normalization. Example:

"write_products, read_products"

is not equivalent to

"write_products,read_products"

Note the space in the first version for readability. This is also the recommendation in the shopify_app gem documentation. This issue may present for any user who followed the docs exactly and had spaces in their scope string.

The Fix

If you can bump the gem version:

Upgrading to v1.1.14 fixes this issue.

If you can not bump the gem version:

Remove the spaces from your api client's request string "scope1, scope2" -> "scope1,scope2"

Affected code

Rails.application.config.middleware.use OmniAuth::Builder do
    provider :shopify, Rails.application.config.shopify.api_key, Rails.application.config.shopify.secret,
    # Bad code:
    # scope: 'write_products, read_channels, write_admin_notifications',
    # Good code:
    scope: 'write_products,read_channels,write_admin_notifications',
end

The way forward

I would like to recommend that we introduce a new major version where scopes passed to the omniauth builder are an array, removing the likelihood of a user introducing this trivial difference.

Note:

v1.1.12 and v1.1.13 should be pulled on Monday, Feb. 29. We don't want to break any gem users' deploys for the weekend. This issue exists so that users with this error have some documentation to find a solution.

CC: @EiNSTeiN- @richgilbank @harismahmood89 @alexaitken

Cannot rescue OmniAuth::Strategies::OAuth2::CallbackError exception

What's the proper way to rescue the CallbackError raised here? We're seeing a lot of OmniAuth::Strategies::OAuth2::CallbackError errors in our Airbrake logs, as it seems the failure endpoint is not being invoked when CallBackError is raised. We've resorted to doing some client-side validation of the customer's myshopify domain prior to redirection, but would love to make use of the failure endpoint if possible.

Here's an abbreviated backtrace from our app:

[PROJECT_ROOT]/vendor/bundle/ruby/1.9.1/gems/omniauth-shopify-oauth2-1.1.2/lib/omniauth/strategies/shopify.rb:27:in setup_phase
[PROJECT_ROOT]/vendor/bundle/ruby/1.9.1/gems/omniauth-1.1.4/lib/omniauth/strategy.rb:195:in request_call
[PROJECT_ROOT]/vendor/bundle/ruby/1.9.1/gems/omniauth-1.1.4/lib/omniauth/strategy.rb:181:in call!
[PROJECT_ROOT]/vendor/bundle/ruby/1.9.1/gems/omniauth-1.1.4/lib/omniauth/strategy.rb:164:in call
[PROJECT_ROOT]/vendor/bundle/ruby/1.9.1/gems/omniauth-1.1.4/lib/omniauth/builder.rb:49:in call
...

Some question about create product api

Hi

I'm developing a shopify app and I request api with parameters like this:

[
    'title' => 'Red Square Neck Rayon Woman's Party Dress',
    'sku' => '1414829809',
    'body_html' => 'Red Square Neck Rayon Woman's Party Dress',
    'vendor' => '自营品牌',
    'product_type' => '其他产品',
    'image' => 'http://slshop.qimuyu.com/files/images/2016/goods/test/Party---Occasions/Red-Square-Neck-Rayon-Womans-Party-Dress-191862-2262485.jpg',
    'images' => [
        (int) 0 => [
            'src' => 'http://slshop.qimuyu.com/files/images/2016/goods/test/Party---Occasions/Red-Square-Neck-Rayon-Womans-Party-Dress-191862-2262485.jpg'
        ],
        (int) 1 => [
            'src' => 'http://slshop.qimuyu.com/files/images/2016/goods/test/Party---Occasions/Red-Square-Neck-Rayon-Womans-Party-Dress-191862-784024.jpg'
        ]
    ],
    'variants' => [
        (int) 0 => [
            'price' => (float) 112.79,
            'sku' => '1414829809-0',
            'option1' => 'Grey',
            'option2' => 'M'
        ],
        (int) 1 => [
            'price' => (float) 112.79,
            'sku' => '1414829809-1',
            'option1' => 'Grey',
            'option2' => 'L'
        ],
        (int) 2 => [
            'price' => (float) 112.79,
            'sku' => '1414829809-2',
            'option1' => 'Red',
            'option2' => 'XS'
        ],
        (int) 3 => [
            'price' => (float) 112.79,
            'sku' => '1414829809-3',
            'option1' => 'Black',
            'option2' => 'L'
        ]
    ]
]

Response like:

[
    'id' => (float) 7938748041,
    'title' => 'Red Square Neck Rayon Woman's Party Dress',
    'body_html' => 'Red Square Neck Rayon Woman's Party Dress',
    'vendor' => '自营品牌',
    'product_type' => '其他产品',
    'created_at' => '2016-09-29T05:58:19Z',
    'handle' => 'red-square-neck-rayon-womans-party-dress-1',
    'updated_at' => '2016-09-29T05:58:19Z',
    'published_at' => '2016-09-29T05:58:19Z',
    'template_suffix' => null,
    'published_scope' => 'global',
    'tags' => '',
    'variants' => [
        (int) 0 => [
            'id' => (float) 25563613193,
            'product_id' => (float) 7938748041,
            'title' => 'Default Title',
            'price' => '0.00',
            'sku' => '',
            'position' => (int) 1,
            'grams' => (int) 0,
            'inventory_policy' => 'deny',
            'compare_at_price' => null,
            'fulfillment_service' => 'manual',
            'inventory_management' => null,
            'option1' => 'Default Title',
            'option2' => null,
            'option3' => null,
            'created_at' => '2016-09-29T05:58:19Z',
            'updated_at' => '2016-09-29T05:58:19Z',
            'taxable' => true,
            'barcode' => null,
            'image_id' => null,
            'inventory_quantity' => (int) 1,
            'weight' => (float) 0,
            'weight_unit' => 'kg',
            'old_inventory_quantity' => (int) 1,
            'requires_shipping' => true
        ]
    ],
    'options' => [
        (int) 0 => [
            'id' => (float) 9505479881,
            'product_id' => (float) 7938748041,
            'name' => 'Title',
            'position' => (int) 1,
            'values' => [
                (int) 0 => 'Default Title'
            ]
        ]
    ],
    'images' => [],
    'image' => null
]

As you know, the product I created does not have price,image and variants. Any parameter are missing.
Look forward to your reply.

Tao

broken with upcoming oauth2 2.0 gem

latest (master) version of oauth2 gem switched auth_scheme to basic_auth so it passes auth code in auth header instead see https://github.com/oauth-xx/oauth2/blob/4f57713f338bd8945ea7628a0f54660137c5c10c/CHANGELOG.md#unreleased

since omniauth-shopify-oauth2 depends on omniauth-oauth2 which depends on oauth2 - and all relay on "default" behaviour it is likely fail when oauth2 2.0 released.

Please set auth_scheme explicitly to request_body, this is easy to do with builder or inside the gem client_options:

use OmniAuth::Builder do
  provider :shopify, SHOPIFY_KEY, SHOPIFY_SECRET,
    client_options: {
      auth_scheme: :request_body
    }
end

Switching providers in the Rails app, based on the request.domain of a request?

The initializer for this gem is supposed to look like this:

# config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, ENV['SHOPIFY_API_KEY'], ENV['SHOPIFY_SHARED_SECRET']
end

However, in our Rails app resides a few different brands who offers the same functionality. Through out the entire app, the request.domain of a request determines which brand you are exposed to (brand1.example.com, brand2.example.com, etc.).

We can easily store brand specific credentials and redirect the users to the brand specific authorization path:

https://example.myshopify.com/admin/oauth/authorize?client_id=brand1&scope=read_orders,read_products&redirect_uri=https://brand1.example.com/auth/shopify/callback

But it's less clear how we administrate having different providers for the middleware, chosen based on the visited request.domain. Any idea how to set this up?

Specify online access in params

I'm working through a situation that I think requires getting both online and offline access. My understanding is that, in order for a public app to be approved, it must support a 'sign up' flow directly from the App Store, which would suggest online access so I can get the user's name and email to create an account for them. Moving forward, the app will make requests to Shopify in the background (without a user session), so I think should use an offline token.

Therefore, I'd like to fetch an online token to facilitate registration and subsequently an offline token for data syncing. Can this library support that? i.e. Passing per_user_permissions at request time?

Users signing up gets redircted to /auth/failure?message=csrf_detected&strategy=shopify

I am redirecting my users to:

https://some-store.myshopify.com/admin/oauth/authorize?client_id=123&scope=read_orders,read_products&redirect_uri=https://my.domain.com/auth/shopify/callback

and after the user confirms the app he gets redirected back to:

https://my.domain.com/auth/shopify/callback?code=123&hmac=123&shop=some-store.myshopify.com&signature=123&timestamp=1464696912

The next thing I can see in my log is that my server is redirecting the user to:

https://my.domain.com/auth/failure?message=csrf_detected&strategy=shopify

I have no idea why this is happening, but I suspect that it is because of some failure that happens while I use this gem.

My setup is:

# -*- encoding : utf-8 -*-
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify, "key", "secret",
    scope: 'read_orders,read_products',
    setup: lambda { |env| params = Rack::Utils.parse_query(env['QUERY_STRING'])
                        env['omniauth.strategy'].options[:client_options][:site] = "http://#{params['shop']}" }
end

The method that transfer the user to his shop is:

    def create
        ShopifyAPI::Session.setup({:api_key => "key", :secret => "secret"})
        session         = ShopifyAPI::Session.new("#{params[:username]}.myshopify.com")
                # The callback URL I am using matches the one entered as Redirection URL in my Shopify partner account.
        redirect_to session.create_permission_url(["read_orders", "read_products"], "https://my.domain.com/auth/shopify/callback")
    end

And the method that received the callback looks like this

    def auth_callback
        ...
        omniauth = request.env['omniauth.auth']
        if omniauth && omniauth[:provider] && omniauth[:provider] == "shopify"
            username    = params[:shop].split(".").first
            token       = omniauth['credentials'].token
        end
        ...
    end

Any idea why this is happening?

User gets redirected to "/auth/shopify" with routing error

After using a nested route, I am getting an exception for routing error No route matches [GET] "/auth/shopify". Is there a way around this so that the setup callback gets triggered? Moreover, is there a way to trigger the callback manually so that I can avoid my page not found route getting triggered?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.