Overview
Shopify's OAuth API and Omniauth gem supports both offline and online access modes. We should provide a clear example of how to use them both, concurrently, in our apps. More specifically, how to setup online access for only the ShopifyApp::AuthenticatedController while still maintaining offline access for ShopifyApp::AppProxyVerification and ShopifyApp::WebhookVerification.
Previous discussions
This has been discussed before, see #580, but there has not been a clear solution moving forward. As of today, there aren't any public repos in Github implementing this either. From what I understood, you can build two Shopify tokens, one for offline and one for online, but I'm not sure how to scope them properly.
Examples
Here's an example of what I'm talking about:
Offline setup (default) <-- This works
AuthenticatedControllers, proxy app, and webhooks work properly, but you can't access individual user info on AuthenticatedControllers.
# config/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
# Offline access
provider :shopify,
ShopifyApp.configuration.api_key,
ShopifyApp.configuration.secret,
scope: ShopifyApp.configuration.scope,
setup: lambda { |env|
strategy = env['omniauth.strategy']
shopify_auth_params = strategy.session['shopify.omniauth_params']&.with_indifferent_access
shop = if shopify_auth_params.present?
"https://#{shopify_auth_params[:shop]}"
else
''
end
strategy.options[:client_options][:site] = shop
}
end
Online setup <-- This works
AuthenticatedControllers work properly, but proxy app and webhooks are Unauthorized.
# config/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
# Online access
provider :shopify,
ShopifyApp.configuration.api_key,
ShopifyApp.configuration.secret,
scope: ShopifyApp.configuration.scope,
per_user_permissions: true, # <-- this changed
setup: lambda { |env|
strategy = env['omniauth.strategy']
shopify_auth_params = strategy.session['shopify.omniauth_params']&.with_indifferent_access
shop = if shopify_auth_params.present?
"https://#{shopify_auth_params[:shop]}"
else
''
end
strategy.options[:client_options][:site] = shop
}
end
Concurrent setup (offline & online) <-- How does this work?
Expected: AuthenticatedControllers, proxy app, and webhooks work properly, and you can access the Shopify user info on AuthenticatedControllers.
# config/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
# Offline access
provider :shopify,
ShopifyApp.configuration.api_key,
ShopifyApp.configuration.secret,
scope: ShopifyApp.configuration.scope,
setup: lambda { |env|
strategy = env['omniauth.strategy']
shopify_auth_params = strategy.session['shopify.omniauth_params']&.with_indifferent_access
shop = if shopify_auth_params.present?
"https://#{shopify_auth_params[:shop]}"
else
''
end
strategy.options[:client_options][:site] = shop
}
# Online access
provider :shopify,
ShopifyApp.configuration.api_key,
ShopifyApp.configuration.secret,
scope: ShopifyApp.configuration.scope,
per_user_permissions: true,
setup: lambda { |env|
# ??? <-- Would this be different than the one above?
}
end
Does this make sense? Or am I missing something obvious?
If someone has solved this in a way that is compatible with this gem, could you share it here? I'm willing to write the proper documentation once we've figured this out.
Things I've learned so far
- You can generate a new session using the Shopify API directly. Could adding an
around_action to the AuthenticatedController that finds or creates a new online access session work? Instead of using the Omniauth::Builder?
- We need to toggle between the offline and online token headers depending on what kind of access a certain action requires. As seen on Shopify's OAuth instructions I believe we would have to set the proper token on each request.
Related Notes
IMO concurrent access should be the default behavior for this gem. It's one less configuration step, shouldn't break current implementations, is more secure for embedded apps, and would allow use of proxy apps, webhooks, and embedded apps with users (together) right from the get-go, which I feel is a common, and amazing, combination for apps.
Next steps
I'm opening this issue to see if I'm missing something before working on a potential solution. And if the feedback is positive, to see if anyone would like to help.
Thank you ❤️
Overview
Shopify's OAuth API and Omniauth gem supports both offline and online access modes. We should provide a clear example of how to use them both, concurrently, in our apps. More specifically, how to setup online access for only the
ShopifyApp::AuthenticatedControllerwhile still maintaining offline access forShopifyApp::AppProxyVerificationandShopifyApp::WebhookVerification.Previous discussions
This has been discussed before, see #580, but there has not been a clear solution moving forward. As of today, there aren't any public repos in Github implementing this either. From what I understood, you can build two Shopify tokens, one for offline and one for online, but I'm not sure how to scope them properly.
Examples
Here's an example of what I'm talking about:
Offline setup (default) <-- This works
AuthenticatedControllers, proxy app, and webhooks work properly, but you can't access individual user info on AuthenticatedControllers.
Online setup <-- This works
AuthenticatedControllers work properly, but proxy app and webhooks are
Unauthorized.Concurrent setup (offline & online) <-- How does this work?
Expected: AuthenticatedControllers, proxy app, and webhooks work properly, and you can access the Shopify user info on AuthenticatedControllers.
Does this make sense? Or am I missing something obvious?
If someone has solved this in a way that is compatible with this gem, could you share it here? I'm willing to write the proper documentation once we've figured this out.
Things I've learned so far
around_actionto the AuthenticatedController that finds or creates a new online access session work? Instead of using the Omniauth::Builder?Related Notes
IMO concurrent access should be the default behavior for this gem. It's one less configuration step, shouldn't break current implementations, is more secure for embedded apps, and would allow use of proxy apps, webhooks, and embedded apps with users (together) right from the get-go, which I feel is a common, and amazing, combination for apps.
Next steps
I'm opening this issue to see if I'm missing something before working on a potential solution. And if the feedback is positive, to see if anyone would like to help.
Thank you ❤️