diff --git a/app/controllers/spree/api/v1/users_controller_decorator.rb b/app/controllers/spree/api/v1/users_controller_decorator.rb new file mode 100644 index 00000000..94f66c7f --- /dev/null +++ b/app/controllers/spree/api/v1/users_controller_decorator.rb @@ -0,0 +1,64 @@ +module Spree + module Api + module V1 + module UsersControllerDecorator + + def social_login + authentication_method = Spree::AuthenticationMethod.find_by_provider(params[:provider]) + render json: {exception: 'Unsupported provider'}, status: 422 and return unless authentication_method + omniauth_hash = authentication_method.get_omniauth_hash(params[:oauth_token]) + authentication = Spree::UserAuthentication.find_by_provider_and_uid(params[:provider], omniauth_hash['uid']) + + if authentication.present? and authentication.try(:user).present? + render_user_login(authentication.user) + elsif @current_api_user + @current_api_user.apply_omniauth(omniauth_hash) + @current_api_user.save! + render_user_login(@current_api_user) + else + user = Spree::User.find_by_email(params[:email]) || Spree::User.new + user.apply_omniauth(omniauth_hash) + + user.generate_spree_api_key! if user.spree_api_key.blank? + + if user.save! + render_user_login(user) + end + end + + if @order + user = @current_api_user || authentication.user + @order.associate_user!(user) + end + end + + def oauth_providers + auth_methods = Spree::AuthenticationMethod.active_authentication_methods + auth_methods.map! do |auth_method| + oauth_provider = SpreeSocial::OAUTH_PROVIDERS.detect {|p| p[1] == provider} + { + name: oauth_provider[0], + provider: auth_method.provider, + api_key: auth_method.api_key, + signup_support: oauth_provider[2] + } + end + render json: auth_methods, status: :ok + end + + private + + def render_user_login(user) + render :json => {:result => { + :user => "#{user.login}", + :api_key => "#{user.spree_api_key}", + :user_id => "#{user.id}" + }} + end + + end + end + end +end + +Spree::Api::V1::UsersController.prepend(Spree::Api::V1::UsersControllerDecorator) \ No newline at end of file diff --git a/app/controllers/spree/omniauth_callbacks_controller.rb b/app/controllers/spree/omniauth_callbacks_controller.rb index bf826a97..f04dd1ca 100644 --- a/app/controllers/spree/omniauth_callbacks_controller.rb +++ b/app/controllers/spree/omniauth_callbacks_controller.rb @@ -6,7 +6,7 @@ class Spree::OmniauthCallbacksController < Devise::OmniauthCallbacksController def self.provides_callback_for(*providers) providers.each do |provider| - class_eval <<-FUNCTION_DEFS, __FILE__, __LINE__ + 1 + class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{provider} if request.env['omniauth.error'].present? flash[:error] = I18n.t('devise.omniauth_callbacks.failure', kind: auth_hash['provider'], reason: Spree.t(:user_was_not_valid)) @@ -44,7 +44,7 @@ def #{provider} session[:guest_token] = nil end end - FUNCTION_DEFS + RUBY end end diff --git a/app/models/spree/authentication_method.rb b/app/models/spree/authentication_method.rb index b2aeb0bf..df245620 100644 --- a/app/models/spree/authentication_method.rb +++ b/app/models/spree/authentication_method.rb @@ -1,13 +1,49 @@ class Spree::AuthenticationMethod < ActiveRecord::Base validates :provider, :api_key, :api_secret, presence: true + validate :provider_must_be_backed_by_omniauth_strategy + + def self.active_authentication_methods + where(environment: ::Rails.env, active: true) + end + def self.active_authentication_methods? - where(environment: ::Rails.env, active: true).exists? + active_authentication_methods.exists? end - scope :available_for, lambda { |user| + scope :available_for, lambda {|user| sc = where(environment: ::Rails.env) sc = sc.where.not(provider: user.user_authentications.pluck(:provider)) if user && !user.user_authentications.empty? sc } + + def get_omniauth_hash(token) + strategy(token).auth_hash + end + + def provider_must_be_backed_by_omniauth_strategy + errors.add(:provider, 'must be backed by an omniauth strategy') unless strategy_class.safe_constantize + end + + private + + def strategy_class + "::OmniAuth::Strategies::#{provider.classify}".safe_constantize + end + + def client + ::OAuth2::Client.new(api_key, api_secret, strategy_class.default_options.client_options.to_h).tap do |c| + c.site = strategy_class.default_options.client_options['site'] + end + end + + def access_token(token) + ::OAuth2::AccessToken.new(client, token) + end + + def strategy(token) + app = lambda {|env| [200, {}, ["Hello World."]]} + options = [api_key, api_secret] + strategy_class.new(app, *options).tap {|s| s.access_token = access_token(token)} + end end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 203270a4..2b687162 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -14,4 +14,13 @@ def apply_omniauth(omniauth) def password_required? (user_authentications.empty? || !password.blank?) && super end + + def oauth_providers + user_authentications.map do |user_authentication| + { + provider: user_authentication.provider, + uid: user_authentication.uid + } + end + end end diff --git a/config/routes.rb b/config/routes.rb index 7db57cb2..20bb3df3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,4 +11,16 @@ namespace :admin do resources :authentication_methods end + + namespace :api do + namespace :v1 do + resources :users do + collection do + post :social_login + post 'social_login/:provider', to: :social_login + get :oauth_providers + end + end + end + end end diff --git a/lib/spree_social/engine.rb b/lib/spree_social/engine.rb index ceb1bdd5..6c202479 100644 --- a/lib/spree_social/engine.rb +++ b/lib/spree_social/engine.rb @@ -20,6 +20,8 @@ def self.activate Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c| Rails.configuration.cache_classes ? require(c) : load(c) end + + Spree::Api::ApiHelpers.user_attributes.push :oauth_providers end config.to_prepare(&method(:activate).to_proc) diff --git a/spree_social.gemspec b/spree_social.gemspec index 365b0a6a..d1816f48 100644 --- a/spree_social.gemspec +++ b/spree_social.gemspec @@ -46,4 +46,5 @@ Gem::Specification.new do |s| s.add_development_dependency 'guard-rspec' s.add_development_dependency 'rubocop', '>= 0.24.1' s.add_development_dependency 'ruby_dep', '~> 1.3.0' + s.add_development_dependency 'rake', '< 11.0' end