This is actually a pretty simple question but I can't seem to find the answer. In the Omniauth Overview on Github, there is actually an explanation but I don't get it:
We pass the :event => :authentication to the sign_in_and_redirect method
to force all authentication callbacks to be called.
I already have authentication working using an action similar to this one:
def facebook
authenticator = UserAuthenticator.new(request.env["omniauth.auth"], current_user)
if authenticator.user_authenticated?
sign_in_and_redirect authenticator.user, :event => :authentication
else
session["devise.oauth_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
All I really want to know is what good is :event => :authentication
for?
I just would like to help to figure out the answer. I tracked the source code myself, that help me understand how the :event => :authentication
parameter works. I hope it also helps you.
So your question is why
pass the :event => :authentication to the sign_in_and_redirect method to force all authentication callbacks to be called.
then, we can track the definition.
# Sign in a user and tries to redirect first to the stored location and
# then to the url specified by after_sign_in_path_for. It accepts the same
# parameters as the sign_in method.
def sign_in_and_redirect(resource_or_scope, *args)
options = args.extract_options!
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource = args.last || resource_or_scope
sign_in(scope, resource, options)
redirect_to after_sign_in_path_for(resource)
end
and then sign_in define in devise:
# All options given to sign_in is passed forward to the set_user method in warden.
# The only exception is the :bypass option, which bypass warden callbacks and stores
# the user straight in session. This option is useful in cases the user is already
# signed in, but we want to refresh the credentials in session.
#
# Examples:
#
# sign_in :user, @user # sign_in(scope, resource)
# sign_in @user # sign_in(resource)
# sign_in @user, :event => :authentication # sign_in(resource, options)
# sign_in @user, :bypass => true # sign_in(resource, options)
#
def sign_in(resource_or_scope, *args)
options = args.extract_options!
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource = args.last || resource_or_scope
expire_session_data_after_sign_in!
if options[:bypass]
warden.session_serializer.store(resource, scope)
elsif warden.user(scope) == resource && !options.delete(:force)
# Do nothing. User already signed in and we are not forcing it.
true
else
warden.set_user(resource, options.merge!(:scope => scope))
end
end
Okay, so :event => :authentication
now is passed to warden#set_user
, Then your question become why
pass the :event => :authentication to the sign_in_and_redirect method to force all authentication callbacks to be called.
# Manually set the user into the session and auth proxy
#
# Parameters:
# user - An object that has been setup to serialize into and out of the session.
# opts - An options hash. Use the :scope option to set the scope of the user, set the :store option to false to skip serializing into the session, set the :run_callbacks to false to skip running the callbacks (the default is true).
#
# :api: public
def set_user(user, opts = {})
scope = (opts[:scope] ||= @config.default_scope)
# Get the default options from the master configuration for the given scope
opts = (@config[:scope_defaults][scope] || {}).merge(opts)
opts[:event] ||= :set_user
@users[scope] = user
if opts[:store] != false && opts[:event] != :fetch
options = env[ENV_SESSION_OPTIONS]
options[:renew] = true if options
session_serializer.store(user, scope)
end
run_callbacks = opts.fetch(:run_callbacks, true)
manager._run_callbacks(:after_set_user, user, self, opts) if run_callbacks
@users[scope]
end
opts[:event] can be [:set_user, :fetch, :authentication]
# Hook to _run_callbacks asserting for conditions.
def _run_callbacks(kind, *args) #:nodoc:
options = args.last # Last callback arg MUST be a Hash
send("_#{kind}").each do |callback, conditions|
invalid = conditions.find do |key, value|
value.is_a?(Array) ? !value.include?(options[key]) : (value != options[key])
end
callback.call(*args) unless invalid
end
end
# A callback hook set to run every time after a user is set.
# This callback is triggered the first time one of those three events happens
# during a request: :authentication, :fetch (from session) and :set_user (when manually set).
# You can supply as many hooks as you like, and they will be run in order of decleration.
#
# If you want to run the callbacks for a given scope and/or event, you can specify them as options.
# See parameters and example below.
#
# Parameters:
# <options> Some options which specify when the callback should be executed
# scope - Executes the callback only if it maches the scope(s) given
# only - Executes the callback only if it matches the event(s) given
# except - Executes the callback except if it matches the event(s) given
# <block> A block where you can set arbitrary logic to run every time a user is set
# Block Parameters: |user, auth, opts|
# user - The user object that is being set
# auth - The raw authentication proxy object.
# opts - any options passed into the set_user call includeing :scope
#
# Example:
# Warden::Manager.after_set_user do |user,auth,opts|
# scope = opts[:scope]
# if auth.session["#{scope}.last_access"].to_i > (Time.now - 5.minutes)
# auth.logout(scope)
# throw(:warden, :scope => scope, :reason => "Times Up")
# end
# auth.session["#{scope}.last_access"] = Time.now
# end
#
# Warden::Manager.after_set_user :except => :fetch do |user,auth,opts|
# user.login_count += 1
# end
#
# :api: public
def after_set_user(options = {}, method = :push, &block)
raise BlockNotGiven unless block_given?
if options.key?(:only)
options[:event] = options.delete(:only)
elsif options.key?(:except)
options[:event] = [:set_user, :authentication, :fetch] - Array(options.delete(:except))
end
_after_set_user.send(method, [block, options])
end
so,
# after_authentication is just a wrapper to after_set_user, which is only invoked
# when the user is set through the authentication path. The options and yielded arguments
# are the same as in after_set_user.
#
# :api: public
def after_authentication(options = {}, method = :push, &block)
after_set_user(options.merge(:event => :authentication), method, &block)
end
Passing :event => :authentication
causes Warden (underlying Devise) to trigger any callbacks defined with:
Warden::Manager.after_authentication do |user,auth,opts|
# Code triggered by authorization here, for example:
user.last_login = Time.now
end
If you're not using any after_authentication
callbacks, and you're confident your libraries aren't either, then it's not of any immediate use to you. Since it is an authentication event, I'd leave it in, now that you know what it's potentially useful for.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With