Search code examples
ruby-on-railsrubyomniauth

How to dynamically set the OmniAuth scope on runtime?


I have previously been pointed to OnmiAuth Dynamic Providers in order to switch provider on runtime, based on the visited domain. My solution is based on omniauth-shopify-oauth2 and this great answer:

Rails.application.config.middleware.use OmniAuth::Builder do
  provider :shopify,
  scope: 'read_orders,read_products',
  setup: lambda { |env|
    request         = ActionDispatch::Request.new(env)
    subdomain       = "#{request.subdomain}" != "" ? "#{request.subdomain}." : ""
    domain          = "#{request.domain}"
    full_domain     = subdomain+domain
    shopify_client  = Rails.cache.fetch("#{full_domain}_shopify_client")

    env['omniauth.strategy'].options.merge!(
      {
        client_id:       shopify_client[:client_id],
        client_secret:   shopify_client[:client_secret]
      }
    )
    env['omniauth.strategy'].options[:client_options][:site] = "https://#{request.GET['shop']}"
  }
end

But now I also need to be able to set the scope dynamically. So "#{full_domain}_shopify_client" from the cache will contain an additional client_permissions key containing e.g. 'read_orders,read_products' or 'read_products'.

How do I refactor my code to be able to do this?


Solution

  • Here's a link which might help: https://github.com/Shopify/omniauth-shopify-oauth2/issues/60

    I re-wrote your script in a way which seems to implement what you want. Add the 'scope' dynamically from the :client_permissions key

    Rails.application.config.middleware.use OmniAuth::Builder do
      provider :shopify,
      setup: lambda { |env|
        request         = ActionDispatch::Request.new(env)
        subdomain       = request.subdomain
        domain          = request.domain
        full_domain     = subdomain+domain
        shopify_client  = Rails.cache.fetch("#{full_domain}_shopify_client")
    
        env['omniauth.strategy'].options.merge!(
          {
            client_id:       shopify_client[:client_id],
            client_secret:   shopify_client[:client_secret],
            scope:           shopify_client[:client_permissions]
            client_options: {
              site: "https://#{request.GET['shop']}"
            },
    
          }
        )
    
    end
    

    If there's an Scope does not match, it may have been tampered with. error, you might have to setup the Rails.cache.fetch("#{full_domain}_shopify_client")[:client_permissions] in the session as well (session['shopify.oauth.scope']).

    strategy = env['omniauth.strategy']
    session = strategy.session.with_indifferent_access
    env['omniauth.strategy'].options[:scope] = session['shopify.oauth.scope']
    

    Inside your setup lambda.

    Then, before redirecting to the oauth callback (from a controller, for example)

    subdomain       = request.subdomain
    domain          = request.domain
    full_domain     = subdomain+domain
    shopify_client  = Rails.cache.fetch("#{full_domain}_shopify_client")
    
    session['shopify.oauth.scope'] = shopify_client[:client_permissions]