class Devise::FailureApp
redirects to the default_url.
in page based on current scope and mapping. If no scope is given, it
any strategy or hook. It is responsible for redirecting the user to the sign
Failure application that will be called every time :warden is thrown from
def self.call(env)
def self.call(env) @respond ||= action(:respond) @respond.call(env) end
def self.default_url_options(*args)
ApplicationController). Instance methods are not supported at the moment,
Try retrieving the URL options from the parent controller (usually
def self.default_url_options(*args) if defined?(Devise.parent_controller.constantize) Devise.parent_controller.constantize.try(:default_url_options) || {} else {} end end
def attempted_path
def attempted_path warden_options[:attempted_path] end
def http_auth
def http_auth self.status = 401 self.headers["WWW-Authenticate"] = %(Basic realm=#{Devise.http_authentication_realm.inspect}) if http_auth_header? self.content_type = request.format.to_s self.response_body = http_auth_body end
def http_auth?
is the same as your public API and uses a format like JSON (so you
handling the errors on their own. This is useful in case your AJAX API
on AJAX requests in case they want to redirect on failures instead of
This method allows the user to explicitly disable HTTP authentication
including 401 and optional headers.
Choose whether we should respond in an HTTP authentication fashion,
def http_auth? if request.xhr? Devise.http_authenticatable_on_xhr else !(request_format && is_navigational_format?) end end
def http_auth_body
def http_auth_body return i18n_message unless request_format method = "to_#{request_format}" if method == "to_xml" { error: i18n_message }.to_xml(root: "errors") elsif {}.respond_to?(method) { error: i18n_message }.send(method) else i18n_message end end
def http_auth_header?
It doesn't make sense to send authenticate headers in AJAX requests
def http_auth_header? scope_class.http_authenticatable && !request.xhr? end
def i18n_locale
def i18n_locale warden_options[:locale] end
def i18n_message(default = nil)
def i18n_message(default = nil) message = warden_message || default || :unauthenticated if message.is_a?(Symbol) options = {} options[:resource_name] = scope options[:scope] = "devise.failure" options[:default] = [message] auth_keys = scope_class.authentication_keys keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key| scope_class.human_attribute_name(key) } options[:authentication_keys] = keys.join(I18n.t(:"support.array.words_connector")) options = i18n_options(options) I18n.t(:"#{scope}.#{message}", **options) else message.to_s end end
def i18n_options(options)
def i18n_options(options) options end
def is_flashing_format?
Check if flash messages should be emitted. Default is to do it on
def is_flashing_format? request.respond_to?(:flash) && is_navigational_format? end
def is_navigational_format?
def is_navigational_format? Devise.navigational_formats.include?(request_format) end
def rails_51_and_up?
def rails_51_and_up? Rails.gem_version >= Gem::Version.new("5.1") end
def recall
def recall header_info = if relative_url_root? base_path = Pathname.new(relative_url_root) full_path = Pathname.new(attempted_path) { "SCRIPT_NAME" => relative_url_root, "PATH_INFO" => '/' + full_path.relative_path_from(base_path).to_s } else { "PATH_INFO" => attempted_path } end header_info.each do | var, value| if request.respond_to?(:set_header) request.set_header(var, value) else request.env[var] = value end end flash.now[:alert] = i18n_message(:invalid) if is_flashing_format? self.response = recall_app(warden_options[:recall]).call(request.env).tap { |response| response[0] = Rack::Utils.status_code( response[0].in?(300..399) ? Devise.responder.redirect_status : Devise.responder.error_status ) } end
def recall_app(app)
def recall_app(app) controller, action = app.split("#") controller_name = ActiveSupport::Inflector.camelize(controller) controller_klass = ActiveSupport::Inflector.constantize("#{controller_name}Controller") controller_klass.action(action) end
def redirect
def redirect store_location! if is_flashing_format? if flash[:timedout] && flash[:alert] flash.keep(:timedout) flash.keep(:alert) else flash[:alert] = i18n_message end end redirect_to redirect_url end
def redirect_url
def redirect_url if warden_message == :timeout flash[:timedout] = true if is_flashing_format? path = if request.get? attempted_path else request.referrer end path || scope_url else scope_url end end
def relative_url_root
def relative_url_root @relative_url_root ||= begin config = Rails.application.config config.try(:relative_url_root) || config.action_controller.try(:relative_url_root) end end
def relative_url_root?
def relative_url_root? relative_url_root.present? end
def request_format
def request_format @request_format ||= request.format.try(:ref) end
def respond
def respond if http_auth? http_auth elsif warden_options[:recall] recall else redirect end end
def root_path_defined?(context)
def root_path_defined?(context) defined?(context.routes) && context.routes.url_helpers.respond_to?(:root_path) end
def route(scope)
def route(scope) :"new_#{scope}_session_url" end
def scope
def scope @scope ||= warden_options[:scope] || Devise.default_scope end
def scope_class
def scope_class @scope_class ||= Devise.mappings[scope].to end
def scope_url
def scope_url opts = {} # Initialize script_name with nil to prevent infinite loops in # authenticated mounted engines in rails 4.2 and 5.0 opts[:script_name] = nil route = route(scope) opts[:format] = request_format unless skip_format? router_name = Devise.mappings[scope].router_name || Devise.available_router_name context = send(router_name) if relative_url_root? opts[:script_name] = relative_url_root # We need to add the rootpath to `script_name` manually for applications that use a Rails # version lower than 5.1. Otherwise, it is going to generate a wrong path for Engines # that use Devise. Remove it when the support of Rails 5.0 is dropped. elsif root_path_defined?(context) && !rails_51_and_up? rootpath = context.routes.url_helpers.root_path opts[:script_name] = rootpath.chomp('/') if rootpath.length > 1 end if context.respond_to?(route) context.send(route, opts) elsif respond_to?(:root_url) root_url(opts) else "/" end end
def skip_format?
def skip_format? %w(html */* turbo_stream).include? request_format.to_s end
def store_location!
authenticated yet, but we still need to store the URI based on scope, so
the scoped session provided by warden here, since the user is not
Stores requested URI to redirect the user after signing in. We can't use
def store_location! store_location_for(scope, attempted_path) if request.get? && !http_auth? end
def warden
def warden request.respond_to?(:get_header) ? request.get_header("warden") : request.env["warden"] end
def warden_message
def warden_message @message ||= warden.message || warden_options[:message] end
def warden_options
def warden_options request.respond_to?(:get_header) ? request.get_header("warden.options") : request.env["warden.options"] end