diff --git a/app/controllers/devise/oauth_callbacks_controller.rb b/app/controllers/devise/oauth_callbacks_controller.rb index 445fd42c..7ba2b0c2 100644 --- a/app/controllers/devise/oauth_callbacks_controller.rb +++ b/app/controllers/devise/oauth_callbacks_controller.rb @@ -1,4 +1,4 @@ class Devise::OauthCallbacksController < ApplicationController include Devise::Controllers::InternalHelpers include Devise::Oauth::Helpers -end +end \ No newline at end of file diff --git a/lib/devise.rb b/lib/devise.rb index 165513df..406954e2 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -31,7 +31,7 @@ module Devise end # Constants which holds devise configuration for extensions. Those should - # not be modified by the "end user". + # not be modified by the "end user" (this is why they are constants). ALL = [] CONTROLLERS = ActiveSupport::OrderedHash.new ROUTES = ActiveSupport::OrderedHash.new @@ -115,10 +115,6 @@ module Devise mattr_accessor :encryptor @@encryptor = nil - # Store scopes mappings. - mattr_accessor :mappings - @@mappings = ActiveSupport::OrderedHash.new - # Tells if devise should apply the schema in ORMs where devise declaration # and schema belongs to the same class (as Datamapper and Mongoid). mattr_accessor :apply_schema @@ -173,9 +169,13 @@ module Devise # PRIVATE CONFIGURATION + # Store scopes mappings. + mattr_reader :mappings + @@mappings = ActiveSupport::OrderedHash.new + # Oauth configurations. mattr_reader :oauth_configs - @@oauth_configs = {} + @@oauth_configs = ActiveSupport::OrderedHash.new # Private methods to interface with Warden. mattr_accessor :warden_config @@ -195,7 +195,7 @@ module Devise @@oauth_providers.uniq! Devise::Oauth::Helpers.create_action(provider) - @@oauth_configs[provider] = Devise::Oauth::Config.new(*args) + @@oauth_configs[provider] = Devise::Oauth::Config.new(provider, *args) end def self.use_default_scope=(*) diff --git a/lib/devise/controllers/internal_helpers.rb b/lib/devise/controllers/internal_helpers.rb index 95669d4a..e99467a7 100644 --- a/lib/devise/controllers/internal_helpers.rb +++ b/lib/devise/controllers/internal_helpers.rb @@ -54,7 +54,12 @@ module Devise # Checks whether it's a devise mapped resource or not. def is_devise_resource? #:nodoc: - raise ActionController::UnknownAction unless devise_mapping + unknown_action!("Could not find devise mapping for #{request.fullpath}.") unless devise_mapping + end + + def unknown_action!(msg) + logger.debug msg + raise ActionController::UnknownAction, msg end # Sets the resource creating an instance variable @@ -90,12 +95,12 @@ module Devise # # Please refer to README or en.yml locale file to check what messages are # available. - def set_flash_message(key, kind) + def set_flash_message(key, kind) #:nodoc: flash[key] = I18n.t(:"#{resource_name}.#{kind}", :resource_name => resource_name, :scope => [:devise, controller_name.to_sym], :default => kind) end - def clean_up_passwords(object) + def clean_up_passwords(object) #:nodoc: object.clean_up_passwords if object.respond_to?(:clean_up_passwords) end end diff --git a/lib/devise/models/oauthable.rb b/lib/devise/models/oauthable.rb index a48675ae..288d0809 100644 --- a/lib/devise/models/oauthable.rb +++ b/lib/devise/models/oauthable.rb @@ -4,10 +4,26 @@ module Devise extend ActiveSupport::Concern module ClassMethods - def oauth_configs + def oauth_configs #:nodoc: Devise.oauth_configs.slice(*oauth_providers) end + # Pass a token stored in the database to this object to get an OAuth2::AccessToken + # object back, as the one received in your model hook. + # + # For each provider you add, you may want to add a hook to retrieve the token based + # on the column you stored the token in the database. For example, you may want to + # the following for twitter: + # + # def oauth_twitter_token + # @oauth_twitter_token ||= self.class.oauth_access_token(:twitter, twitter_token) + # end + # + # You can call get, post, put and delete in this object to access Twitter's API. + def oauth_access_token(provider, token) + oauth_configs[provider].access_token(token) + end + Devise::Models.config(self, :oauth_providers) end end diff --git a/lib/devise/oauth/config.rb b/lib/devise/oauth/config.rb index bcf4f5ad..5b7f24d3 100644 --- a/lib/devise/oauth/config.rb +++ b/lib/devise/oauth/config.rb @@ -1,12 +1,28 @@ +require 'active_support/core_ext/array/wrap' + module Devise module Oauth class Config - attr_reader :scope, :client + attr_reader :name, :scope, :client - def initialize(app_id, app_secret, options) - @scope = Array.wrap(options.delete(:scope)).join(",") + def initialize(name, app_id, app_secret, options) + @name = name + @scope = Array.wrap(options.delete(:scope)) @client = OAuth2::Client.new(app_id, app_secret, options) end + + def authorize_url(options) + options[:scope] ||= @scope.join(',') + client.web_server.authorize_url(options) + end + + def access_token_by_code(code) + client.web_server.get_access_token(code) + end + + def access_token_by_token(token) + OAuth2::AccessToken.new(client, token) + end end end end \ No newline at end of file diff --git a/lib/devise/oauth/helpers.rb b/lib/devise/oauth/helpers.rb index 7e712aba..0bc7e072 100644 --- a/lib/devise/oauth/helpers.rb +++ b/lib/devise/oauth/helpers.rb @@ -9,7 +9,7 @@ module Devise end included do - helpers = %w(oauth_callback oauth_config oauth_client) + helpers = %w(oauth_callback oauth_config) hide_action *helpers helper_method *helpers before_filter :is_oauth_callback? @@ -23,15 +23,22 @@ module Devise @oauth_client ||= resource_class.oauth_configs[oauth_callback] end - def oauth_client - @oauth_client ||= oauth_config.client - end - protected def is_oauth_callback? - raise ActionController::UnknownAction unless oauth_config - raise ActionController::UnknownAction unless params[:code] + unless params[:code] + unknown_action! "Skipping OAuth #{outh_callback.inspect} callback because code was not sent." + end + + unless oauth_config + unknown_action! "Skipping OAuth #{outh_callback.inspect} callback because provider " << + "could not be found in model #{resource_name.inspect}." + end + + unless resource_class.respond_to?(oauth_model_callback) + raise "#{resource_class.name} does not respond to to OAuth callback #{oauth_model_callback.inspect}. " << + "Check the OAuth section in the README for more information." + end end def oauth_model_callback @@ -39,12 +46,14 @@ module Devise end def callback_action - access_token = oauth_client.web_server.get_access_token(params[:code]) - self.resource = User.send(oauth_model_callback, access_token, signed_in_resource) + access_token = oauth_config.access_token_by_code(params[:code]) + self.resource = resource_class.send(oauth_model_callback, access_token, signed_in_resource) if resource.persisted? + # ADD FLASH MESSAGE sign_in_and_redirect resource_name, resource, :event => :authentication else + # STORE STUFF IN SESSION render_for_oauth end end diff --git a/lib/devise/oauth/url_helpers.rb b/lib/devise/oauth/url_helpers.rb index c0a29a2b..0ebe80c7 100644 --- a/lib/devise/oauth/url_helpers.rb +++ b/lib/devise/oauth/url_helpers.rb @@ -1,7 +1,32 @@ module Devise module Oauth module UrlHelpers + [:path, :url].each do |path_or_url| + class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1 + def oauth_callback_#{path_or_url}(resource_or_scope, *args) + scope = Devise::Mapping.find_scope!(resource_or_scope) + send("\#{scope}_oauth_callback_#{path_or_url}", *args) + end + URL_HELPERS + end + def oauth_authorize_url(resource_or_scope, *args) + scope = Devise::Mapping.find_scope!(resource_or_scope) + send("#{scope}_oauth_authorize_url}", *args) + end + + Devise.mappings.each_key do |scope| + class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1 + def #{scope}_oauth_authorize_url(provider, options={}) + if config = Devise.oauth_configs[provider.to_sym] + options[:redirect_uri] ||= #{scope}_oauth_callback_url + config.authorize_url(options) + else + raise ArgumentError, "Could not find oauth provider #{provider.inspect}" + end + end + URL_HELPERS + end end end end \ No newline at end of file