Skip to content

[Docs request] Add concurrent access mode examples (offline & online auth) #618

Description

@matias-eduardo

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

  1. 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?
  2. 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 ❤️

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions