Codebase list ruby-omniauth-facebook / ec5c6f63-75c3-4eb9-8fa8-fe5c6d2682af/main lib / omniauth / strategies / facebook.rb
ec5c6f63-75c3-4eb9-8fa8-fe5c6d2682af/main

Tree @ec5c6f63-75c3-4eb9-8fa8-fe5c6d2682af/main (Download .tar.gz)

facebook.rb @ec5c6f63-75c3-4eb9-8fa8-fe5c6d2682af/main

260cb64
0e51abc
260cb64
 
4d6f3f6
260cb64
 
 
 
 
 
 
 
 
83eed0a
 
 
260cb64
 
 
83eed0a
 
260cb64
 
 
 
 
 
 
 
 
 
 
 
 
4d6f3f6
260cb64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3c9bba
260cb64
 
4d6f3f6
83eed0a
 
 
4d6f3f6
83eed0a
4d6f3f6
 
 
7cd9a52
 
 
4d6f3f6
 
0e51abc
a3c9bba
260cb64
 
4d6f3f6
 
7cd9a52
260cb64
7cd9a52
260cb64
 
0e51abc
 
260cb64
 
 
 
 
 
 
4d6f3f6
 
260cb64
7cd9a52
260cb64
 
4d6f3f6
260cb64
 
 
 
 
 
 
 
 
4d6f3f6
 
 
7cd9a52
 
4d6f3f6
260cb64
 
 
 
7cd9a52
0e51abc
260cb64
 
7cd9a52
 
260cb64
 
 
 
4d6f3f6
7cd9a52
260cb64
 
 
7cd9a52
260cb64
7cd9a52
 
 
 
 
260cb64
 
 
 
7cd9a52
 
260cb64
 
7cd9a52
260cb64
 
 
 
 
 
 
 
 
 
4d6f3f6
 
a3c9bba
83eed0a
4d6f3f6
a3c9bba
83eed0a
4d6f3f6
 
 
 
 
 
 
 
 
 
 
260cb64
 
 
require 'omniauth/strategies/oauth2'
require 'omniauth/facebook/signed_request'
require 'openssl'
require 'rack/utils'
require 'uri'

module OmniAuth
  module Strategies
    class Facebook < OmniAuth::Strategies::OAuth2
      class NoAuthorizationCodeError < StandardError; end

      DEFAULT_SCOPE = 'email'

      option :client_options, {
        site: 'https://graph.facebook.com/v2.6',
        authorize_url: "https://www.facebook.com/v2.6/dialog/oauth",
        token_url: 'oauth/access_token'
      }

      option :access_token_options, {
        header_format: 'OAuth %s',
        param_name: 'access_token'
      }

      option :authorize_options, [:scope, :display, :auth_type]

      uid { raw_info['id'] }

      info do
        prune!({
          'nickname' => raw_info['username'],
          'email' => raw_info['email'],
          'name' => raw_info['name'],
          'first_name' => raw_info['first_name'],
          'last_name' => raw_info['last_name'],
          'image' => image_url(uid, options),
          'description' => raw_info['bio'],
          'urls' => {
            'Facebook' => raw_info['link'],
            'Website' => raw_info['website']
          },
          'location' => (raw_info['location'] || {})['name'],
          'verified' => raw_info['verified']
        })
      end

      extra do
        hash = {}
        hash['raw_info'] = raw_info unless skip_info?
        prune! hash
      end

      def raw_info
        @raw_info ||= access_token.get('me', info_options).parsed || {}
      end

      def info_options
        params = {appsecret_proof: appsecret_proof}
        params.merge!({fields: (options[:info_fields] || 'name,email')})
        params.merge!({locale: options[:locale]}) if options[:locale]

        { params: params }
      end

      def callback_phase
        with_authorization_code! do
          super
        end
      rescue NoAuthorizationCodeError => e
        fail!(:no_authorization_code, e)
      rescue OmniAuth::Facebook::SignedRequest::UnknownSignatureAlgorithmError => e
        fail!(:unknown_signature_algorithm, e)
      end

      # NOTE If we're using code from the signed request then FB sets the redirect_uri to '' during the authorize
      #      phase and it must match during the access_token phase:
      #      https://github.com/facebook/facebook-php-sdk/blob/master/src/base_facebook.php#L477
      def callback_url
        if @authorization_code_from_signed_request_in_cookie
          ''
        else
          # Fixes regression in omniauth-oauth2 v1.4.0 by https://github.com/intridea/omniauth-oauth2/commit/85fdbe117c2a4400d001a6368cc359d88f40abc7
          options[:callback_url] || (full_host + script_name + callback_path)
        end
      end

      def access_token_options
        options.access_token_options.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
      end

      # You can pass +display+, +scope+, or +auth_type+ params to the auth request, if you need to set them dynamically.
      # You can also set these options in the OmniAuth config :authorize_params option.
      #
      # For example: /auth/facebook?display=popup
      def authorize_params
        super.tap do |params|
          %w[display scope auth_type].each do |v|
            if request.params[v]
              params[v.to_sym] = request.params[v]
            end
          end

          params[:scope] ||= DEFAULT_SCOPE
        end
      end

      protected

      def build_access_token
        super.tap do |token|
          token.options.merge!(access_token_options)
        end
      end

      private

      def signed_request_from_cookie
        @signed_request_from_cookie ||= raw_signed_request_from_cookie && OmniAuth::Facebook::SignedRequest.parse(raw_signed_request_from_cookie, client.secret)
      end

      def raw_signed_request_from_cookie
        request.cookies["fbsr_#{client.id}"]
      end

      # Picks the authorization code in order, from:
      #
      # 1. The request 'code' param (manual callback from standard server-side flow)
      # 2. A signed request from cookie (passed from the client during the client-side flow)
      def with_authorization_code!
        if request.params.key?('code')
          yield
        elsif code_from_signed_request = signed_request_from_cookie && signed_request_from_cookie['code']
          request.params['code'] = code_from_signed_request
          @authorization_code_from_signed_request_in_cookie = true
          # NOTE The code from the signed fbsr_XXX cookie is set by the FB JS SDK will confirm that the identity of the
          #      user contained in the signed request matches the user loading the app.
          original_provider_ignores_state = options.provider_ignores_state
          options.provider_ignores_state = true
          begin
            yield
          ensure
            request.params.delete('code')
            @authorization_code_from_signed_request_in_cookie = false
            options.provider_ignores_state = original_provider_ignores_state
          end
        else
          raise NoAuthorizationCodeError, 'must pass either a `code` (via URL or by an `fbsr_XXX` signed request cookie)'
        end
      end

      def prune!(hash)
        hash.delete_if do |_, value|
          prune!(value) if value.is_a?(Hash)
          value.nil? || (value.respond_to?(:empty?) && value.empty?)
        end
      end

      def image_url(uid, options)
        uri_class = options[:secure_image_url] ? URI::HTTPS : URI::HTTP
        site_uri = URI.parse(client.site)
        url = uri_class.build({host: site_uri.host, path: "#{site_uri.path}/#{uid}/picture"})

        query = if options[:image_size].is_a?(String) || options[:image_size].is_a?(Symbol)
          { type: options[:image_size] }
        elsif options[:image_size].is_a?(Hash)
          options[:image_size]
        end
        url.query = Rack::Utils.build_query(query) if query

        url.to_s
      end

      def appsecret_proof
        @appsecret_proof ||= OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, client.secret, access_token.token)
      end
    end
  end
end