@@ -18,6 +18,7 @@ module Devise
1818 autoload :ParameterSanitizer , 'devise/parameter_sanitizer'
1919 autoload :TimeInflector , 'devise/time_inflector'
2020 autoload :TokenGenerator , 'devise/token_generator'
21+ autoload :TwoFactor , 'devise/two_factor'
2122
2223 module Controllers
2324 autoload :Helpers , 'devise/controllers/helpers'
@@ -40,6 +41,7 @@ module Mailers
4041 module Strategies
4142 autoload :Base , 'devise/strategies/base'
4243 autoload :Authenticatable , 'devise/strategies/authenticatable'
44+ autoload :TwoFactor , 'devise/strategies/two_factor'
4345 end
4446
4547 module Test
@@ -312,6 +314,14 @@ def self.mappings
312314 mattr_accessor :sign_in_after_change_password
313315 @@sign_in_after_change_password = true
314316
317+ # Global default for two_factor_methods per-model config.
318+ mattr_accessor :two_factor_methods
319+ @@two_factor_methods = [ ]
320+
321+ # Registry of two-factor method configs set via register_two_factor_method.
322+ mattr_reader :two_factor_method_configs
323+ @@two_factor_method_configs = { }
324+
315325 # Default way to set up Devise. Run rails generate devise_install to create
316326 # a fresh initializer with all configuration values.
317327 def self . setup
@@ -439,6 +449,59 @@ def self.add_module(module_name, options = {})
439449 Devise ::Mapping . add_module module_name
440450 end
441451
452+
453+ # Register available devise two factor methods.
454+ # Third-party modules that intend to add a 2FA method need to be added explicitly using this method.
455+ #
456+ # Note that adding a module using this method does not cause it to be used in the authentication
457+ # process. That requires the `:two_factor_authenticatable` module to be listed in the arguments passed
458+ # to the 'devise' method in the model class definition along with the two factor method name listed under
459+ # the `:two_factor_methods` argument passed to the 'devise' method.
460+ #
461+ # == Options:
462+ #
463+ # +name+ - String representing the name of the 2FA method. This will be used to identify it.
464+ # +model+ - String representing the load path to a custom *model* for this 2FA method (to autoload.)
465+ # +strategy+ - Symbol representing if this module got a custom *strategy*.
466+ # +route+ - Generates extension-specific routes and URL helpers (e.g., credential management
467+ # endpoints). This is separate from the core challenge/create routes that Devise
468+ # generates automatically from +two_factor_methods+. Accepts true (defaults route
469+ # name to the method name), a Symbol, or a Hash. Works the same as the +:route+
470+ # option in +add_module+.
471+ #
472+ # == Examples:
473+ #
474+ # Devise.register_two_factor_method(:my_two_factor_method)
475+ # Devise.register_two_factor_method(:my_two_factor_method, model: 'my_two_factor_method/model')
476+ # Devise.register_two_factor_method(:my_two_factor_method, model: 'my_two_factor_method/model', strategy: :my_two_factor_method, route: true)
477+ #
478+ def self . register_two_factor_method ( name , options = { } )
479+ options . assert_valid_keys ( :model , :strategy , :route )
480+
481+ two_factor_method_configs [ name . to_sym ] = options
482+
483+ STRATEGIES [ name . to_sym ] = options [ :strategy ] if options [ :strategy ]
484+
485+ if route = options [ :route ]
486+ case route
487+ when TrueClass
488+ key , value = name , [ ]
489+ when Symbol
490+ key , value = route , [ ]
491+ when Hash
492+ key , value = route . keys . first , route . values . flatten
493+ else
494+ raise ArgumentError , ":route should be true, a Symbol or a Hash"
495+ end
496+
497+ URL_HELPERS [ key ] ||= [ ]
498+ URL_HELPERS [ key ] . concat ( value )
499+ URL_HELPERS [ key ] . uniq!
500+
501+ ROUTES [ name . to_sym ] = key
502+ end
503+ end
504+
442505 # Sets warden configuration using a block that will be invoked on warden
443506 # initialization.
444507 #
0 commit comments