mirror of
https://github.com/heartcombo/devise.git
synced 2022-11-09 12:18:31 -05:00
Implement error handling for OAuth.
This commit is contained in:
parent
b87dc84079
commit
b31d60ce7c
6 changed files with 77 additions and 33 deletions
|
@ -31,7 +31,8 @@ en:
|
||||||
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
|
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
|
||||||
unlocked: 'Your account was successfully unlocked. You are now signed in.'
|
unlocked: 'Your account was successfully unlocked. You are now signed in.'
|
||||||
oauth_callbacks:
|
oauth_callbacks:
|
||||||
default: 'Successfully authorized from %{kind} account.'
|
success: 'Successfully authorized from %{kind} account.'
|
||||||
|
failure: 'Could not authorize you from %{kind} because "%{reason}".'
|
||||||
mailer:
|
mailer:
|
||||||
confirmation_instructions:
|
confirmation_instructions:
|
||||||
subject: 'Confirmation instructions'
|
subject: 'Confirmation instructions'
|
||||||
|
|
|
@ -97,9 +97,9 @@ module Devise
|
||||||
# available.
|
# available.
|
||||||
def set_flash_message(key, kind, options={}) #:nodoc:
|
def set_flash_message(key, kind, options={}) #:nodoc:
|
||||||
options[:scope] = "devise.#{controller_name}"
|
options[:scope] = "devise.#{controller_name}"
|
||||||
options[:default] = Array(options[:default]).unshift(kind)
|
options[:default] = Array(options[:default]).unshift(kind.to_sym)
|
||||||
options[:resource_name] = resource_name
|
options[:resource_name] = resource_name
|
||||||
flash[key] = I18n.t(:"#{resource_name}.#{kind}", options)
|
flash[key] = I18n.t("#{resource_name}.#{kind}", options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def clean_up_passwords(object) #:nodoc:
|
def clean_up_passwords(object) #:nodoc:
|
||||||
|
|
|
@ -16,8 +16,8 @@ module Devise
|
||||||
client.web_server.authorize_url(options)
|
client.web_server.authorize_url(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def access_token_by_code(code)
|
def access_token_by_code(code, redirect_uri=nil)
|
||||||
client.web_server.get_access_token(code)
|
client.web_server.get_access_token(code, :redirect_uri => redirect_uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def access_token_by_token(token)
|
def access_token_by_token(token)
|
||||||
|
|
|
@ -12,19 +12,7 @@ module Devise
|
||||||
def oauth_callback
|
def oauth_callback
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
alias :oauth_provider :oauth_callback
|
||||||
protected
|
|
||||||
|
|
||||||
def render_for_oauth
|
|
||||||
render_with_scope oauth_callback
|
|
||||||
rescue ActionView::MissingTemplate
|
|
||||||
render_with_scope :new, devise_mapping.controllers[:registrations]
|
|
||||||
end
|
|
||||||
|
|
||||||
# The default hook used by oauth to specify the redirect url.
|
|
||||||
def after_oauth_sign_in_path_for(resource_or_scope)
|
|
||||||
after_sign_in_path_for(resource_or_scope)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -12,54 +12,109 @@ module Devise
|
||||||
helpers = %w(oauth_config)
|
helpers = %w(oauth_config)
|
||||||
hide_action *helpers
|
hide_action *helpers
|
||||||
helper_method *helpers
|
helper_method *helpers
|
||||||
before_filter :is_oauth_callback?
|
before_filter :valid_oauth_callback?, :error_happened?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the oauth_callback (also aliases oauth_provider) as a symbol.
|
||||||
|
# For example: :github.
|
||||||
def oauth_callback
|
def oauth_callback
|
||||||
@oauth_callback ||= action_name.to_sym
|
@oauth_callback ||= action_name.to_sym
|
||||||
end
|
end
|
||||||
|
alias :oauth_provider :oauth_callback
|
||||||
|
|
||||||
|
# Returns the configuration object for this oauth callback.
|
||||||
def oauth_config
|
def oauth_config
|
||||||
@oauth_client ||= resource_class.oauth_configs[oauth_callback]
|
@oauth_client ||= resource_class.oauth_configs[oauth_callback]
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def is_oauth_callback?
|
# This method checks three things:
|
||||||
unless params[:code]
|
#
|
||||||
unknown_action! "Skipping OAuth #{outh_callback.inspect} callback because code was not sent."
|
# * If the URL being access is a valid provider for the given scope;
|
||||||
|
# * If code or error was streamed back from the server;
|
||||||
|
# * If the resource class implements the required hook;
|
||||||
|
#
|
||||||
|
def valid_oauth_callback? #:nodoc:
|
||||||
|
unless oauth_config
|
||||||
|
unknown_action! "Skipping #{oauth_callback} OAuth because configuration " <<
|
||||||
|
"could not be found for model #{resource_name}."
|
||||||
end
|
end
|
||||||
|
|
||||||
unless oauth_config
|
unless params[:code] || params[:error] || params[:error_reason]
|
||||||
unknown_action! "Skipping OAuth #{outh_callback.inspect} callback because provider " <<
|
unknown_action! "Skipping #{oauth_callback} OAuth because code nor error were sent."
|
||||||
"could not be found in model #{resource_name.inspect}."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unless resource_class.respond_to?(oauth_model_callback)
|
unless resource_class.respond_to?(oauth_model_callback)
|
||||||
raise "#{resource_class.name} does not respond to to OAuth callback #{oauth_model_callback.inspect}. " <<
|
raise "#{resource_class.name} does not respond to #{oauth_model_callback}. " <<
|
||||||
"Check the OAuth section in the README for more information."
|
"Check the OAuth section in the README for more information."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def oauth_model_callback
|
# Check if an error was sent by the authorizer.
|
||||||
|
#
|
||||||
|
# TODO: Currently, Facebook is returning error_reason=user_defined when
|
||||||
|
# the user denies, but the specification defines error=access_denied instead.
|
||||||
|
def error_happened? #:nodoc:
|
||||||
|
if error = params[:error] || params[:error_reason]
|
||||||
|
logger.warn "#{oauth_callback} OAuth failed: #{error.inspect}."
|
||||||
|
|
||||||
|
# Some providers returns access-denied instead of access_denied.
|
||||||
|
error = error.to_s.gsub("-", "_")
|
||||||
|
set_flash_message :alert, error[0,25], :default => :failure, :reason => error.titleize
|
||||||
|
redirect_to after_oauth_failure_path_for(resource_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# The model method used as hook.
|
||||||
|
def oauth_model_callback #:nodoc:
|
||||||
"find_for_#{oauth_callback}_oauth"
|
"find_for_#{oauth_callback}_oauth"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The session key to store the token.
|
||||||
|
def oauth_session_key #:nodoc:
|
||||||
|
"#{resource_name}_#{oauth_callback}_token"
|
||||||
|
end
|
||||||
|
|
||||||
|
# The callback redirect uri. Used to request the access token.
|
||||||
|
def oauth_redirect_uri #:nodoc:
|
||||||
|
oauth_callback_url(resource_name, oauth_callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is the implementation for all actions in this controller.
|
||||||
def callback_action
|
def callback_action
|
||||||
access_token = oauth_config.access_token_by_code(params[:code])
|
access_token = oauth_config.access_token_by_code(params[:code], oauth_redirect_uri)
|
||||||
self.resource = resource_class.send(oauth_model_callback, access_token, signed_in_resource)
|
self.resource = resource_class.send(oauth_model_callback, access_token, signed_in_resource)
|
||||||
|
|
||||||
if resource.persisted?
|
if resource.persisted?
|
||||||
set_flash_message :notice, oauth_callback, :default => :default, :kind => oauth_callback.to_s.titleize
|
set_flash_message :notice, oauth_callback, :default => :success
|
||||||
sign_in_and_redirect resource_name, resource, :event => :authentication
|
sign_in_and_redirect resource_name, resource, :event => :authentication
|
||||||
else
|
else
|
||||||
session[oauth_session_scope] = access_token.token
|
session[oauth_session_key] = access_token.token
|
||||||
render_for_oauth
|
render_for_oauth
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def oauth_session_scope
|
# Overwrite to automatically add kind to messages.
|
||||||
"#{resource_name}_#{oauth_callback}_token"
|
def set_flash_message(key, kind, options={}) #:nodoc:
|
||||||
|
options[:kind] = oauth_callback.to_s.titleize
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
# Choose which template to render when a not persisted resource is
|
||||||
|
# returned in the find_for_x_oauth. By default, it renders registrations/new.
|
||||||
|
def render_for_oauth
|
||||||
|
render_with_scope :new, devise_mapping.controllers[:registrations]
|
||||||
|
end
|
||||||
|
|
||||||
|
# The default hook used by oauth to specify the redirect url.
|
||||||
|
def after_oauth_sign_in_path_for(resource_or_scope)
|
||||||
|
after_sign_in_path_for(resource_or_scope)
|
||||||
|
end
|
||||||
|
|
||||||
|
# A callback to redirect your user to the proper location after create.
|
||||||
|
def after_oauth_failure_path_for(scope)
|
||||||
|
root_path
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overwrite redirect_for_sign_in so it takes uses after_oauth_sign_in_path_for.
|
# Overwrite redirect_for_sign_in so it takes uses after_oauth_sign_in_path_for.
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Devise
|
||||||
|
|
||||||
def oauth_callback_url(resource_or_scope, *args)
|
def oauth_callback_url(resource_or_scope, *args)
|
||||||
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
||||||
send("#{scope}_oauth_callback_path", *args)
|
send("#{scope}_oauth_callback_url", *args)
|
||||||
end
|
end
|
||||||
|
|
||||||
def oauth_callback_path(resource_or_scope, *args)
|
def oauth_callback_path(resource_or_scope, *args)
|
||||||
|
|
Loading…
Reference in a new issue