ruby_shopify_app 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.babelrc +5 -0
- data/.github/CODEOWNERS +2 -0
- data/.github/ISSUE_TEMPLATE/bug-report.md +63 -0
- data/.github/ISSUE_TEMPLATE/config.yml +1 -0
- data/.github/ISSUE_TEMPLATE/feature-request.md +33 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
- data/.github/probots.yml +2 -0
- data/.github/workflows/build.yml +40 -0
- data/.github/workflows/release.yml +24 -0
- data/.github/workflows/rubocop.yml +22 -0
- data/.gitignore +14 -0
- data/.nvmrc +1 -0
- data/.rubocop.yml +18 -0
- data/.ruby-version +1 -0
- data/CHANGELOG-OLD.md +643 -0
- data/CHANGELOG.md +6 -0
- data/CONTRIBUTING.md +81 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +280 -0
- data/LICENSE +19 -0
- data/README.md +132 -0
- data/Rakefile +7 -0
- data/SECURITY.md +59 -0
- data/app/assets/images/storage_access.svg +1 -0
- data/app/assets/javascripts/shopify_app/app_bridge_2.0.12.js +10 -0
- data/app/assets/javascripts/shopify_app/app_bridge_redirect.js +22 -0
- data/app/assets/javascripts/shopify_app/enable_cookies.js +3 -0
- data/app/assets/javascripts/shopify_app/itp_helper.js +40 -0
- data/app/assets/javascripts/shopify_app/partition_cookies.js +8 -0
- data/app/assets/javascripts/shopify_app/post_redirect.js +9 -0
- data/app/assets/javascripts/shopify_app/redirect.js +31 -0
- data/app/assets/javascripts/shopify_app/request_storage_access.js +3 -0
- data/app/assets/javascripts/shopify_app/storage_access.js +148 -0
- data/app/assets/javascripts/shopify_app/storage_access_redirect.js +17 -0
- data/app/assets/javascripts/shopify_app/top_level.js +2 -0
- data/app/assets/javascripts/shopify_app/top_level_interaction.js +11 -0
- data/app/controllers/concerns/shopify_app/authenticated.rb +16 -0
- data/app/controllers/concerns/shopify_app/ensure_authenticated_links.rb +39 -0
- data/app/controllers/concerns/shopify_app/require_known_shop.rb +40 -0
- data/app/controllers/concerns/shopify_app/shop_access_scopes_verification.rb +32 -0
- data/app/controllers/shopify_app/authenticated_controller.rb +8 -0
- data/app/controllers/shopify_app/callback_controller.rb +195 -0
- data/app/controllers/shopify_app/extension_verification_controller.rb +15 -0
- data/app/controllers/shopify_app/sessions_controller.rb +202 -0
- data/app/controllers/shopify_app/webhooks_controller.rb +36 -0
- data/app/views/shopify_app/partials/_button_styles.html.erb +109 -0
- data/app/views/shopify_app/partials/_card_styles.html.erb +33 -0
- data/app/views/shopify_app/partials/_empty_state_styles.html.erb +98 -0
- data/app/views/shopify_app/partials/_form_styles.html.erb +56 -0
- data/app/views/shopify_app/partials/_layout_styles.html.erb +182 -0
- data/app/views/shopify_app/partials/_typography_styles.html.erb +35 -0
- data/app/views/shopify_app/sessions/enable_cookies.html.erb +70 -0
- data/app/views/shopify_app/sessions/new.html.erb +51 -0
- data/app/views/shopify_app/sessions/request_storage_access.html.erb +68 -0
- data/app/views/shopify_app/sessions/top_level_interaction.html.erb +63 -0
- data/app/views/shopify_app/shared/post_redirect_to_auth_shopify.html.erb +13 -0
- data/app/views/shopify_app/shared/redirect.html.erb +23 -0
- data/config/locales/cs.yml +23 -0
- data/config/locales/da.yml +20 -0
- data/config/locales/de.yml +22 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/es.yml +22 -0
- data/config/locales/fi.yml +20 -0
- data/config/locales/fr.yml +23 -0
- data/config/locales/it.yml +21 -0
- data/config/locales/ja.yml +17 -0
- data/config/locales/ko.yml +19 -0
- data/config/locales/nb.yml +21 -0
- data/config/locales/nl.yml +21 -0
- data/config/locales/pl.yml +21 -0
- data/config/locales/pt-BR.yml +21 -0
- data/config/locales/pt-PT.yml +22 -0
- data/config/locales/sv.yml +21 -0
- data/config/locales/th.yml +20 -0
- data/config/locales/tr.yml +22 -0
- data/config/locales/vi.yml +22 -0
- data/config/locales/zh-CN.yml +16 -0
- data/config/locales/zh-TW.yml +16 -0
- data/config/routes.rb +23 -0
- data/docs/Quickstart.md +31 -0
- data/docs/Releasing.md +21 -0
- data/docs/Troubleshooting.md +159 -0
- data/docs/Upgrading.md +132 -0
- data/docs/shopify_app/authentication.md +124 -0
- data/docs/shopify_app/engine.md +82 -0
- data/docs/shopify_app/generators.md +127 -0
- data/docs/shopify_app/handling-access-scopes-changes.md +24 -0
- data/docs/shopify_app/script-tags.md +28 -0
- data/docs/shopify_app/session-repository.md +88 -0
- data/docs/shopify_app/testing.md +38 -0
- data/docs/shopify_app/webhooks.md +72 -0
- data/images/app-proxy-screenshot.png +0 -0
- data/karma.conf.js +44 -0
- data/lib/generators/shopify_app/add_after_authenticate_job/add_after_authenticate_job_generator.rb +47 -0
- data/lib/generators/shopify_app/add_after_authenticate_job/templates/after_authenticate_job.rb +11 -0
- data/lib/generators/shopify_app/add_marketing_activity_extension/add_marketing_activity_extension_generator.rb +40 -0
- data/lib/generators/shopify_app/add_marketing_activity_extension/templates/marketing_activities_controller.rb +62 -0
- data/lib/generators/shopify_app/add_webhook/add_webhook_generator.rb +69 -0
- data/lib/generators/shopify_app/add_webhook/templates/webhook_job.rb.tt +13 -0
- data/lib/generators/shopify_app/app_proxy_controller/app_proxy_controller_generator.rb +26 -0
- data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_controller.rb +8 -0
- data/lib/generators/shopify_app/app_proxy_controller/templates/app_proxy_route.rb +11 -0
- data/lib/generators/shopify_app/app_proxy_controller/templates/index.html.erb +19 -0
- data/lib/generators/shopify_app/authenticated_controller/authenticated_controller_generator.rb +15 -0
- data/lib/generators/shopify_app/authenticated_controller/templates/authenticated_controller.rb +5 -0
- data/lib/generators/shopify_app/controllers/controllers_generator.rb +30 -0
- data/lib/generators/shopify_app/home_controller/home_controller_generator.rb +53 -0
- data/lib/generators/shopify_app/home_controller/templates/home_controller.rb +18 -0
- data/lib/generators/shopify_app/home_controller/templates/index.html.erb +75 -0
- data/lib/generators/shopify_app/home_controller/templates/unauthenticated_home_controller.rb +12 -0
- data/lib/generators/shopify_app/install/install_generator.rb +121 -0
- data/lib/generators/shopify_app/install/templates/_flash_messages.html.erb +3 -0
- data/lib/generators/shopify_app/install/templates/embedded_app.html.erb +44 -0
- data/lib/generators/shopify_app/install/templates/flash_messages.js +24 -0
- data/lib/generators/shopify_app/install/templates/omniauth.rb +4 -0
- data/lib/generators/shopify_app/install/templates/session_store.rb +4 -0
- data/lib/generators/shopify_app/install/templates/shopify_app.js +15 -0
- data/lib/generators/shopify_app/install/templates/shopify_app.rb.tt +25 -0
- data/lib/generators/shopify_app/install/templates/shopify_app_importmap.js +13 -0
- data/lib/generators/shopify_app/install/templates/shopify_app_index.js +2 -0
- data/lib/generators/shopify_app/install/templates/shopify_provider.rb.tt +8 -0
- data/lib/generators/shopify_app/install/templates/user_agent.rb +6 -0
- data/lib/generators/shopify_app/products_controller/products_controller_generator.rb +19 -0
- data/lib/generators/shopify_app/products_controller/templates/products_controller.rb +8 -0
- data/lib/generators/shopify_app/rotate_shopify_token_job/rotate_shopify_token_job_generator.rb +16 -0
- data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token.rake +17 -0
- data/lib/generators/shopify_app/rotate_shopify_token_job/templates/rotate_shopify_token_job.rb +42 -0
- data/lib/generators/shopify_app/routes/routes_generator.rb +32 -0
- data/lib/generators/shopify_app/routes/templates/routes.rb +12 -0
- data/lib/generators/shopify_app/shop_model/shop_model_generator.rb +70 -0
- data/lib/generators/shopify_app/shop_model/templates/db/migrate/add_shop_access_scopes_column.erb +5 -0
- data/lib/generators/shopify_app/shop_model/templates/db/migrate/create_shops.erb +15 -0
- data/lib/generators/shopify_app/shop_model/templates/shop.rb +8 -0
- data/lib/generators/shopify_app/shop_model/templates/shops.yml +3 -0
- data/lib/generators/shopify_app/shopify_app_generator.rb +18 -0
- data/lib/generators/shopify_app/user_model/templates/db/migrate/add_user_access_scopes_column.erb +5 -0
- data/lib/generators/shopify_app/user_model/templates/db/migrate/create_users.erb +16 -0
- data/lib/generators/shopify_app/user_model/templates/user.rb +8 -0
- data/lib/generators/shopify_app/user_model/templates/users.yml +4 -0
- data/lib/generators/shopify_app/user_model/user_model_generator.rb +70 -0
- data/lib/generators/shopify_app/views/views_generator.rb +30 -0
- data/lib/shopify_app/access_scopes/noop_strategy.rb +13 -0
- data/lib/shopify_app/access_scopes/shop_strategy.rb +24 -0
- data/lib/shopify_app/access_scopes/user_strategy.rb +41 -0
- data/lib/shopify_app/configuration.rb +119 -0
- data/lib/shopify_app/controller_concerns/app_proxy_verification.rb +38 -0
- data/lib/shopify_app/controller_concerns/csrf_protection.rb +15 -0
- data/lib/shopify_app/controller_concerns/embedded_app.rb +20 -0
- data/lib/shopify_app/controller_concerns/itp.rb +45 -0
- data/lib/shopify_app/controller_concerns/localization.rb +23 -0
- data/lib/shopify_app/controller_concerns/login_protection.rb +259 -0
- data/lib/shopify_app/controller_concerns/payload_verification.rb +24 -0
- data/lib/shopify_app/controller_concerns/webhook_verification.rb +23 -0
- data/lib/shopify_app/engine.rb +47 -0
- data/lib/shopify_app/jobs/scripttags_manager_job.rb +16 -0
- data/lib/shopify_app/jobs/webhooks_manager_job.rb +16 -0
- data/lib/shopify_app/managers/scripttags_manager.rb +78 -0
- data/lib/shopify_app/managers/webhooks_manager.rb +62 -0
- data/lib/shopify_app/middleware/jwt_middleware.rb +43 -0
- data/lib/shopify_app/middleware/same_site_cookie_middleware.rb +34 -0
- data/lib/shopify_app/omniauth/omniauth_configuration.rb +64 -0
- data/lib/shopify_app/session/in_memory_session_store.rb +31 -0
- data/lib/shopify_app/session/in_memory_shop_session_store.rb +16 -0
- data/lib/shopify_app/session/in_memory_user_session_store.rb +16 -0
- data/lib/shopify_app/session/jwt.rb +67 -0
- data/lib/shopify_app/session/null_user_session_store.rb +22 -0
- data/lib/shopify_app/session/session_repository.rb +56 -0
- data/lib/shopify_app/session/session_storage.rb +20 -0
- data/lib/shopify_app/session/shop_session_storage.rb +42 -0
- data/lib/shopify_app/session/shop_session_storage_with_scopes.rb +58 -0
- data/lib/shopify_app/session/user_session_storage.rb +42 -0
- data/lib/shopify_app/session/user_session_storage_with_scopes.rb +58 -0
- data/lib/shopify_app/test_helpers/all.rb +2 -0
- data/lib/shopify_app/test_helpers/webhook_verification_helper.rb +17 -0
- data/lib/shopify_app/utils.rb +37 -0
- data/lib/shopify_app/version.rb +4 -0
- data/lib/shopify_app.rb +80 -0
- data/package.json +27 -0
- data/service.yml +4 -0
- data/shipit.rubygems.yml +4 -0
- data/shopify_app.gemspec +39 -0
- data/translation.yml +7 -0
- data/webpack.config.js +24 -0
- data/yarn.lock +5230 -0
- metadata +465 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyApp
|
|
3
|
+
module Generators
|
|
4
|
+
class ShopifyAppGenerator < Rails::Generators::Base
|
|
5
|
+
def initialize(args, *options)
|
|
6
|
+
@opts = options.first
|
|
7
|
+
super(args, *options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run_all_generators
|
|
11
|
+
generate("shopify_app:install #{@opts.join(' ')}")
|
|
12
|
+
generate("shopify_app:shop_model #{@opts.join(' ')}")
|
|
13
|
+
generate("shopify_app:authenticated_controller")
|
|
14
|
+
generate("shopify_app:home_controller #{@opts.join(' ')}")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CreateUsers < ActiveRecord::Migration[<%= rails_migration_version %>]
|
|
2
|
+
def self.up
|
|
3
|
+
create_table :users do |t|
|
|
4
|
+
t.bigint :shopify_user_id, null: false
|
|
5
|
+
t.string :shopify_domain, null: false
|
|
6
|
+
t.string :shopify_token, null: false
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :users, :shopify_user_id, unique: true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.down
|
|
14
|
+
drop_table :users
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rails/generators/base'
|
|
3
|
+
require 'rails/generators/active_record'
|
|
4
|
+
|
|
5
|
+
module ShopifyApp
|
|
6
|
+
module Generators
|
|
7
|
+
class UserModelGenerator < Rails::Generators::Base
|
|
8
|
+
include Rails::Generators::Migration
|
|
9
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
10
|
+
|
|
11
|
+
class_option :new_shopify_cli_app, type: :boolean, default: false
|
|
12
|
+
|
|
13
|
+
def create_user_model
|
|
14
|
+
copy_file('user.rb', 'app/models/user.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_user_migration
|
|
18
|
+
migration_template('db/migrate/create_users.erb', 'db/migrate/create_users.rb')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def create_scopes_storage_in_user_model
|
|
22
|
+
scopes_column_prompt = <<~PROMPT
|
|
23
|
+
It is highly recommended that apps record the access scopes granted by \
|
|
24
|
+
merchants during app installation. See app/models/user.rb to modify how \
|
|
25
|
+
access scopes are stored and retrieved.
|
|
26
|
+
|
|
27
|
+
[WARNING] You will need to update the access_scopes accessors in the User model \
|
|
28
|
+
to allow shopify_app to store and retrieve scopes when going through OAuth.
|
|
29
|
+
|
|
30
|
+
The following migration will add an `access_scopes` column to the User model. \
|
|
31
|
+
Do you want to include this migration? [y/n]
|
|
32
|
+
PROMPT
|
|
33
|
+
|
|
34
|
+
if new_shopify_cli_app? || Rails.env.test? || yes?(scopes_column_prompt)
|
|
35
|
+
migration_template(
|
|
36
|
+
'db/migrate/add_user_access_scopes_column.erb',
|
|
37
|
+
'db/migrate/add_user_access_scopes_column.rb'
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def update_shopify_app_initializer
|
|
43
|
+
gsub_file('config/initializers/shopify_app.rb', 'ShopifyApp::InMemoryUserSessionStore', 'User')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def create_user_fixtures
|
|
47
|
+
copy_file('users.yml', 'test/fixtures/users.yml')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def new_shopify_cli_app?
|
|
53
|
+
options['new_shopify_cli_app']
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def rails_migration_version
|
|
57
|
+
Rails.version.match(/\d\.\d/)[0]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
private :next_migration_number
|
|
62
|
+
|
|
63
|
+
# for generating a timestamp when using `create_migration`
|
|
64
|
+
def next_migration_number(dir)
|
|
65
|
+
ActiveRecord::Generators::Base.next_migration_number(dir)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'rails/generators/base'
|
|
3
|
+
|
|
4
|
+
module ShopifyApp
|
|
5
|
+
module Generators
|
|
6
|
+
class ViewsGenerator < Rails::Generators::Base
|
|
7
|
+
source_root File.expand_path("../../../../..", __FILE__)
|
|
8
|
+
|
|
9
|
+
def create_views
|
|
10
|
+
views.each do |view|
|
|
11
|
+
copy_file(view)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def views
|
|
18
|
+
files_within_root('.', 'app/views/**/*.*')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def files_within_root(prefix, glob)
|
|
22
|
+
root = "#{self.class.source_root}/#{prefix}"
|
|
23
|
+
|
|
24
|
+
Dir["#{root}/#{glob}"].sort.map do |full_path|
|
|
25
|
+
full_path.sub(root, '.').gsub('/./', '/')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyApp
|
|
4
|
+
module AccessScopes
|
|
5
|
+
class ShopStrategy
|
|
6
|
+
class << self
|
|
7
|
+
def update_access_scopes?(shop_domain)
|
|
8
|
+
shop_access_scopes = shop_access_scopes(shop_domain)
|
|
9
|
+
configuration_access_scopes != shop_access_scopes
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def shop_access_scopes(shop_domain)
|
|
15
|
+
ShopifyApp::SessionRepository.retrieve_shop_session_by_shopify_domain(shop_domain)&.access_scopes
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def configuration_access_scopes
|
|
19
|
+
ShopifyAPI::ApiAccess.new(ShopifyApp.configuration.shop_access_scopes)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyApp
|
|
4
|
+
module AccessScopes
|
|
5
|
+
class UserStrategy
|
|
6
|
+
class InvalidInput < StandardError; end
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def update_access_scopes?(user_id: nil, shopify_user_id: nil)
|
|
10
|
+
return update_access_scopes_for_user_id?(user_id) if user_id
|
|
11
|
+
return update_access_scopes_for_shopify_user_id?(shopify_user_id) if shopify_user_id
|
|
12
|
+
raise(InvalidInput, '#update_access_scopes? requires user_id or shopify_user_id parameter inputs')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def update_access_scopes_for_user_id?(user_id)
|
|
18
|
+
user_access_scopes = user_access_scopes_by_user_id(user_id)
|
|
19
|
+
configuration_access_scopes != user_access_scopes
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update_access_scopes_for_shopify_user_id?(shopify_user_id)
|
|
23
|
+
user_access_scopes = user_access_scopes_by_shopify_user_id(shopify_user_id)
|
|
24
|
+
configuration_access_scopes != user_access_scopes
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def user_access_scopes_by_user_id(user_id)
|
|
28
|
+
ShopifyApp::SessionRepository.retrieve_user_session(user_id)&.access_scopes
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def user_access_scopes_by_shopify_user_id(shopify_user_id)
|
|
32
|
+
ShopifyApp::SessionRepository.retrieve_user_session_by_shopify_user_id(shopify_user_id)&.access_scopes
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def configuration_access_scopes
|
|
36
|
+
ShopifyAPI::ApiAccess.new(ShopifyApp.configuration.user_access_scopes)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyApp
|
|
3
|
+
class Configuration
|
|
4
|
+
# Shopify App settings. These values should match the configuration
|
|
5
|
+
# for the app in your Shopify Partners page. Change your settings in
|
|
6
|
+
# `config/initializers/shopify_app.rb`
|
|
7
|
+
attr_accessor :application_name
|
|
8
|
+
attr_accessor :api_key
|
|
9
|
+
attr_accessor :secret
|
|
10
|
+
attr_accessor :old_secret
|
|
11
|
+
attr_accessor :scope
|
|
12
|
+
attr_writer :shop_access_scopes
|
|
13
|
+
attr_writer :user_access_scopes
|
|
14
|
+
attr_accessor :embedded_app
|
|
15
|
+
alias_method :embedded_app?, :embedded_app
|
|
16
|
+
attr_accessor :webhooks
|
|
17
|
+
attr_accessor :scripttags
|
|
18
|
+
attr_accessor :after_authenticate_job
|
|
19
|
+
attr_accessor :api_version
|
|
20
|
+
|
|
21
|
+
attr_accessor :reauth_on_access_scope_changes
|
|
22
|
+
|
|
23
|
+
# customise urls
|
|
24
|
+
attr_accessor :root_url
|
|
25
|
+
attr_writer :login_url
|
|
26
|
+
|
|
27
|
+
# customise ActiveJob queue names
|
|
28
|
+
attr_accessor :scripttags_manager_queue_name
|
|
29
|
+
attr_accessor :webhooks_manager_queue_name
|
|
30
|
+
|
|
31
|
+
# configure myshopify domain for local shopify development
|
|
32
|
+
attr_accessor :myshopify_domain
|
|
33
|
+
|
|
34
|
+
# ability to have webpacker installed but not used in this gem and the generators
|
|
35
|
+
attr_accessor :disable_webpacker
|
|
36
|
+
|
|
37
|
+
# allow namespacing webhook jobs
|
|
38
|
+
attr_accessor :webhook_jobs_namespace
|
|
39
|
+
|
|
40
|
+
# allow enabling of same site none on cookies
|
|
41
|
+
attr_writer :enable_same_site_none
|
|
42
|
+
|
|
43
|
+
# allow enabling jwt headers for authentication
|
|
44
|
+
attr_accessor :allow_jwt_authentication
|
|
45
|
+
|
|
46
|
+
attr_accessor :allow_cookie_authentication
|
|
47
|
+
|
|
48
|
+
def initialize
|
|
49
|
+
@root_url = '/'
|
|
50
|
+
@myshopify_domain = 'myshopify.com'
|
|
51
|
+
@scripttags_manager_queue_name = Rails.application.config.active_job.queue_name
|
|
52
|
+
@webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
|
|
53
|
+
@disable_webpacker = ENV['SHOPIFY_APP_DISABLE_WEBPACKER'].present?
|
|
54
|
+
@allow_cookie_authentication = true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def login_url
|
|
58
|
+
@login_url || File.join(@root_url, 'login')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def user_session_repository=(klass)
|
|
62
|
+
ShopifyApp::SessionRepository.user_storage = klass
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def user_session_repository
|
|
66
|
+
ShopifyApp::SessionRepository.user_storage
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def shop_session_repository=(klass)
|
|
70
|
+
ShopifyApp::SessionRepository.shop_storage = klass
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def shop_session_repository
|
|
74
|
+
ShopifyApp::SessionRepository.shop_storage
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def shop_access_scopes_strategy
|
|
78
|
+
return ShopifyApp::AccessScopes::NoopStrategy unless reauth_on_access_scope_changes
|
|
79
|
+
ShopifyApp::AccessScopes::ShopStrategy
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def user_access_scopes_strategy
|
|
83
|
+
return ShopifyApp::AccessScopes::NoopStrategy unless reauth_on_access_scope_changes
|
|
84
|
+
ShopifyApp::AccessScopes::UserStrategy
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def has_webhooks?
|
|
88
|
+
webhooks.present?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def has_scripttags?
|
|
92
|
+
scripttags.present?
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def enable_same_site_none
|
|
96
|
+
!Rails.env.test? && (@enable_same_site_none.nil? ? embedded_app? : @enable_same_site_none)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def shop_access_scopes
|
|
100
|
+
@shop_access_scopes || scope
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def user_access_scopes
|
|
104
|
+
@user_access_scopes || scope
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.configuration
|
|
109
|
+
@configuration ||= Configuration.new
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def self.configuration=(config)
|
|
113
|
+
@configuration = config
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.configure
|
|
117
|
+
yield configuration
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyApp
|
|
3
|
+
module AppProxyVerification
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
included do
|
|
6
|
+
skip_before_action :verify_authenticity_token, raise: false
|
|
7
|
+
before_action :verify_proxy_request
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def verify_proxy_request
|
|
11
|
+
return head(:forbidden) unless query_string_valid?(request.query_string)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def query_string_valid?(query_string)
|
|
17
|
+
query_hash = Rack::Utils.parse_query(query_string)
|
|
18
|
+
|
|
19
|
+
signature = query_hash.delete('signature')
|
|
20
|
+
return false if signature.nil?
|
|
21
|
+
|
|
22
|
+
ActiveSupport::SecurityUtils.secure_compare(
|
|
23
|
+
calculated_signature(query_hash),
|
|
24
|
+
signature
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def calculated_signature(query_hash_without_signature)
|
|
29
|
+
sorted_params = query_hash_without_signature.collect { |k, v| "#{k}=#{Array(v).join(',')}" }.sort.join
|
|
30
|
+
|
|
31
|
+
OpenSSL::HMAC.hexdigest(
|
|
32
|
+
OpenSSL::Digest.new('sha256'),
|
|
33
|
+
ShopifyApp.configuration.secret,
|
|
34
|
+
sorted_params
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyApp
|
|
3
|
+
module CsrfProtection
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
included do
|
|
6
|
+
protect_from_forgery with: :exception, unless: :valid_session_token?
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def valid_session_token?
|
|
12
|
+
request.env['jwt.shopify_domain']
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyApp
|
|
3
|
+
module EmbeddedApp
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
if ShopifyApp.configuration.embedded_app?
|
|
8
|
+
after_action(:set_esdk_headers)
|
|
9
|
+
layout('embedded_app')
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def set_esdk_headers
|
|
16
|
+
response.set_header('P3P', 'CP="Not used"')
|
|
17
|
+
response.headers.except!('X-Frame-Options')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ShopifyApp
|
|
4
|
+
# Cookie management helpers required for ITP implementation
|
|
5
|
+
module Itp
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def set_test_cookie
|
|
9
|
+
return unless ShopifyApp.configuration.embedded_app?
|
|
10
|
+
return unless user_agent_can_partition_cookies
|
|
11
|
+
|
|
12
|
+
session['shopify.cookies_persist'] = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def set_top_level_oauth_cookie
|
|
16
|
+
session['shopify.top_level_oauth'] = true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def clear_top_level_oauth_cookie
|
|
20
|
+
session.delete('shopify.top_level_oauth')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def user_agent_is_mobile
|
|
24
|
+
user_agent = BrowserSniffer.new(request.user_agent).browser_info
|
|
25
|
+
|
|
26
|
+
user_agent[:name].to_s.match(/Shopify\sMobile/)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def user_agent_is_pos
|
|
30
|
+
user_agent = BrowserSniffer.new(request.user_agent).browser_info
|
|
31
|
+
|
|
32
|
+
user_agent[:name].to_s.match(/Shopify\sPOS/)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def user_agent_can_partition_cookies
|
|
36
|
+
user_agent = BrowserSniffer.new(request.user_agent).browser_info
|
|
37
|
+
|
|
38
|
+
is_safari = user_agent[:name].to_s.match(/Safari/)
|
|
39
|
+
|
|
40
|
+
return false unless is_safari
|
|
41
|
+
|
|
42
|
+
user_agent[:version].to_s.match(/12\.0/)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module ShopifyApp
|
|
3
|
+
module Localization
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
included do
|
|
7
|
+
before_action :set_locale
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def set_locale
|
|
13
|
+
if params[:locale]
|
|
14
|
+
session[:locale] = params[:locale]
|
|
15
|
+
else
|
|
16
|
+
session[:locale] ||= I18n.default_locale
|
|
17
|
+
end
|
|
18
|
+
I18n.locale = session[:locale]
|
|
19
|
+
rescue I18n::InvalidLocale
|
|
20
|
+
I18n.locale = I18n.default_locale
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|