Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject http_authorization headers using omniauth and oauth2 in consumer so the authentication occurs in provider?

I'm building an ecomm service using Spree which uses Devise, it contains all the data from our users, but we are using it as a service only, via APIs, so we have an application (rails 3.1), the client/consumer, which using OmniAuth, has a custom strategy, that via Oauth2, redirects requests to the provider, shows the sign is page, and then let the request go through the authorize method, and then the whole oauth2 process works flawlessly until the callback and then the user gets logged into our consumer.

Until this part, everything works correctly, this is my strategy class:

module OmniAuth
  module Strategies
    class MyStrategy < OAuth2
      def initialize(app, api_key = nil, secret_key = nil, options = {}, &block)
        client_options = {
          :site =>  CUSTOM_PROVIDER_URL,
          :authorize_url => "#{CUSTOM_PROVIDER_URL}/auth/my_provider/authorize",
          :token_url => "#{CUSTOM_PROVIDER_URL}/auth/my_provider/access_token"
        }
        super(app, :onyx_oauth, api_key, secret_key, client_options, &block)
      end

      protected

      def user_data
        response = @access_token.get("/auth/my_provider/user.json");
        if response.status.to_i == 200
          @data ||= MultiJson.decode(response.body);
        else 
          raise 'Service error'
        end
      end

      def request_phase
        options[:scope] ||= "read"
        super
      end

      def user_hash
        user_data
      end

      def auth_hash
        OmniAuth::Utils.deep_merge(super, {
          'uid' => user_data["uid"],
          'user_info' => user_data['user_info'],
          'extra' => user_data['extra']
        })
      end
    end
  end
end

What I'm trying to do is to have a sign in form in the client application, and then using my custom strategy, send those credentials (email/password) to the provider (I'm thinking via HTTP_AUTHORIZATION headers), to log the user into the provider skipping the sign in form in the provider, so after devise authenticates the user, it pass through the authorize method and let the whole Oauth2 process continue.

I been trying several things that include defining a method call(env) in the strategy and set the env['HTTP_AUTHORIZATION'] with the credentials like "email:password" before calling super. I also included in request_phase method the 'HTTP_AUTHORIZATION' key in the options hash, even inside a :headers key too. My last attempt was to include it in the headers hash into the Faraday connection object within the client object (OAuth2::Client instance) of the class.

After debugging almost the whole process, and trying to include it everywhere, my provider keeps not getting that particular key in the headers, even debugging the provider when Devise is testing each strategy... but not luck after all.

I really appreciate if someone would tell me what I'm doing wrong or if what I'm trying to accomplish cannot be done in the way I'm doing it or if there is a simpler way to do it..

Thanks in advance, sorry for the very long post/question, and thanks again for your time!

like image 428
Roberto Cataneo Avatar asked Oct 09 '22 14:10

Roberto Cataneo


1 Answers

I think this might be what you are looking for:

Taken from https://github.com/jackdempsey/omniauth-reddit

module OmniAuth
  module Strategies
    class Reddit < OmniAuth::Strategies::OAuth2
      #class NoAuthorizationCodeError < StandardError; end

      option :name, "reddit"
      option :authorize_options, [:scope, :duration]

      option :client_options, {
        site: 'https://oauth.reddit.com',
        token_url: 'https://ssl.reddit.com/api/v1/access_token'
      }

      uid { raw_info['id'] }

      info do
        {
          name: raw_info['name']
        }
      end

      extra do
        {'raw_info' => raw_info}
      end
      def raw_info
        @raw_info ||= access_token.get('/api/v1/me').parsed || {}
      end

      def build_access_token
        options.token_params.merge!(:headers => {'Authorization' => basic_auth_header })
        super
      end

      def basic_auth_header
        "Basic " + Base64.strict_encode64("#{options[:client_id]}:#{options[:client_secret]}")
      end

      MOBILE_USER_AGENTS =  'webos|ipod|iphone|mobile'

      def request_phase
        options[:client_options].authorize_url = mobile_request? ? 'https://ssl.reddit.com/api/v1/authorize.compact' : 'https://ssl.reddit.com/api/v1/authorize'
        super
      end

      def mobile_request?
        ua = Rack::Request.new(@env).user_agent.to_s
        ua.downcase =~ Regexp.new(MOBILE_USER_AGENTS)
      end


    end
  end
end
like image 98
Peter Kirby Avatar answered Oct 13 '22 11:10

Peter Kirby