Class: OmniAuth::Strategies::Shopify

Inherits:
Object
  • Object
show all
Includes:
OmniAuth::Strategy
Defined in:
lib/omniauth/strategies/shopify.rb

Overview

Authentication strategy for connecting with APIs constructed using the [OAuth 2.0 Specification](tools.ietf.org/html/draft-ietf-oauth-v2-10). You must generally register your application with the provider and utilize an application id and secret in order to authenticate using OAuth 2.0.

Defined Under Namespace

Classes: CallbackError

Constant Summary collapse

DEFAULT_SCOPE =

Available scopes: content themes products customers orders script_tags shipping read_* or write_*

'read_products'
SCOPE_DELIMITER =
','
MINUTE =
60
CODE_EXPIRES_AFTER =
10 * MINUTE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#access_tokenObject

Returns the value of attribute access_token.



69
70
71
# File 'lib/omniauth/strategies/shopify.rb', line 69

def access_token
  @access_token
end

Class Method Details

.encoded_params_for_signature(params) ⇒ Object



156
157
158
159
160
161
# File 'lib/omniauth/strategies/shopify.rb', line 156

def self.encoded_params_for_signature(params)
  params = params.dup
  params.delete('hmac')
  params.delete('signature') # deprecated signature
  Rack::Utils.build_query(params.sort)
end

.hmac_sign(encoded_params, secret) ⇒ Object



163
164
165
# File 'lib/omniauth/strategies/shopify.rb', line 163

def self.hmac_sign(encoded_params, secret)
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, secret, encoded_params)
end

.inherited(subclass) ⇒ Object



18
19
20
# File 'lib/omniauth/strategies/shopify.rb', line 18

def self.inherited(subclass)
  OmniAuth::Strategy.included(subclass)
end

Instance Method Details

#authorize_paramsObject



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/omniauth/strategies/shopify.rb', line 220

def authorize_params
  options.authorize_params[:state] = SecureRandom.hex(24)

  if OmniAuth.config.test_mode
    @env ||= {}
    @env["rack.session"] ||= {}
  end

  params = options.authorize_params
                  .merge(options_for("authorize"))
                  .merge(pkce_authorize_params)

  session["omniauth.pkce.verifier"] = options.pkce_verifier if options.pkce
  session["omniauth.state"] = params[:state]
  oauth_session["omniauth.state"] = params[:state]

  params[:scope] = normalized_scopes(params[:scope] || DEFAULT_SCOPE).join(SCOPE_DELIMITER)
  params[:grant_options] = ['per-user'] if options[:per_user_permissions]

  params
end

#callback_phaseObject



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/omniauth/strategies/shopify.rb', line 193

def callback_phase
  return fail!(:invalid_site, CallbackError.new(:invalid_site, "OAuth endpoint is not a myshopify site.")) unless valid_site?
  return fail!(:invalid_signature, CallbackError.new(:invalid_signature, "Signature does not match, it may have been tampered with.")) unless valid_signature?

  token = build_access_token
  unless valid_permissions?(token)
    return fail!(:invalid_permissions, CallbackError.new(:invalid_permissions, "Requested API access mode does not match."))
  end

  error = request.params["error_reason"] || request.params["error"]
  if !options.provider_ignores_state && (request.params["state"].to_s.empty? || request.params["state"] != fetch_and_delete_omniauth_state)
    fail!(:csrf_detected, CallbackError.new(:csrf_detected, "CSRF detected"))
  elsif error
    fail!(error, CallbackError.new(request.params["error"], request.params["error_description"] || request.params["error_reason"], request.params["error_uri"]))
  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

#callback_urlObject



242
243
244
# File 'lib/omniauth/strategies/shopify.rb', line 242

def callback_url
  options[:callback_url] || full_host + script_name + callback_path
end

#clientObject



114
115
116
# File 'lib/omniauth/strategies/shopify.rb', line 114

def client
  ::OAuth2::Client.new(options.client_id, options.client_secret, deep_symbolize(options.client_options))
end

#fix_httpsObject



176
177
178
# File 'lib/omniauth/strategies/shopify.rb', line 176

def fix_https
  options[:client_options][:site] = options[:client_options][:site].gsub(/\Ahttp\:/, 'https:')
end

#normalized_scopes(scopes) ⇒ Object



150
151
152
153
154
# File 'lib/omniauth/strategies/shopify.rb', line 150

def normalized_scopes(scopes)
  scope_list = scopes.to_s.split(SCOPE_DELIMITER).map(&:strip).reject(&:empty?).uniq
  ignore_scopes = scope_list.map { |scope| scope =~ /\A(unauthenticated_)?write_(.*)\z/ && "#{$1}read_#{$2}" }.compact
  scope_list - ignore_scopes
end

#request_phaseObject



185
186
187
188
189
190
191
# File 'lib/omniauth/strategies/shopify.rb', line 185

def request_phase
  if valid_site?
    redirect client.auth_code.authorize_url({:redirect_uri => callback_url}.merge(authorize_params))
  else
    fail!(:invalid_site)
  end
end

#setup_phaseObject



180
181
182
183
# File 'lib/omniauth/strategies/shopify.rb', line 180

def setup_phase
  super
  fix_https
end

#token_paramsObject



126
127
128
# File 'lib/omniauth/strategies/shopify.rb', line 126

def token_params
  options.token_params.merge(options_for("token")).merge(pkce_token_params)
end

#valid_permissions?(token) ⇒ Boolean

Returns:

  • (Boolean)


167
168
169
170
171
172
173
174
# File 'lib/omniauth/strategies/shopify.rb', line 167

def valid_permissions?(token)
  return false unless token

  return true if options[:per_user_permissions] && token['associated_user']
  return true if !options[:per_user_permissions] && !token['associated_user']

  false
end

#valid_signature?Boolean

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/omniauth/strategies/shopify.rb', line 134

def valid_signature?
  return false unless request.POST.empty?

  params = request.GET
  signature = params['hmac']
  timestamp = params['timestamp']
  return false unless signature && timestamp

  return false unless timestamp.to_i > Time.now.to_i - CODE_EXPIRES_AFTER

  new_secret = options.client_secret
  old_secret = options.old_client_secret

  validate_signature(new_secret) || (old_secret && validate_signature(old_secret))
end

#valid_site?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/omniauth/strategies/shopify.rb', line 130

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