Devise Authentication Strategies

Despite the rather grandiose title, this bit of extendable functionality that the popular Authentication library Devise exposes is really rather simple in concept. It’s also proved to be extremely useful, even though coming across it’s existence required a bit of detective work.

Here’s the elevator pitch:

Conceptually an authentication strategy is where you put the logic for authenticating a request. Background Devise is built on top of a library called Warden. Warden is a low-level authentication library for Rack and works as a piece of middleware in the stack. Warden provides the basis for Strategies by implementing a Base strategy from which others must inherit. If you are looking for web design for your company check Vivid Designs

Devise actually declares 2 strategies Authenticatable and DatabaseAuthenticatable of which you can choose :database_authenticatable. Authenticatable inherits from Wardens Base Strategy and DatabaseAuthenticatable inherits from Authenticatable.

Implementation A Strategy is just a class that follows a few rules. Warden specifies that a Strategy must declare an authenticate! method and can optionally declare a valid? method.

Now I’ll add the valid? method. valid? acts as a guard for the Strategy. It should return true or false depending on if the strategy is valid for the request. If it returns true, the Strategy will be run otherwise it is skipped. You can have many Strategies you see, and they cascade. So in the code below, it will only run the Strategy if the correct params are available. You can imagine a Strategy that only runs if certain request headers are available.

You can see the fail! and success! methods used in the authenticate! method. There are a bunch of these public utility methods Warden makes available. Devise also exposes some if your Strategy inherits from Authenticatable. I’ll run through the ones you’ll likely use more often. You can see the rest in the Warden source declaration.

#success! This is called when you want to provide a resource as ‘authenticated’. This will halt the strategy, and set the resource in the appropriate scope. This is effectively the ‘login’ method.

#fail Calling fail causes the strategy to fail, but not halt. The request will cascade to the next strategy (if available) passing along any failure message. The last strategy to fail will have it’s message displayed. If you are looking for website design in Hyderabad for your company visit Vivid Designs  

#fail! This also causes the strategy to fail. It also Halts the strategies so that this is the last strategy checked.

#headers Provides access to the headers hash for getting and setting custom headers.

Adding to Rails Once you have your new Strategy, it’s time to add it to your application. I add mine as an initializer, so for this example you’d add it to:

/config/initializers/password_strategy.rb I tend to prefix my initializers with numbers so that I know the order they will be loaded in. The important thing is that the:

/config/initializers/devise.rb initializer is loaded before any custom strategies.

Next you need to tell devise to load the strategy, and to stick it on top of the pile so it is used first.

config.warden do |manager| manager.strategies.add(:password, Devise::Strategies::Password) manager.default_strategies(:scope => :user).unshift :password end Restart your app and you’re done. The new strategy will now be used.

Real World This post came of the back of a real world example. Below is a version (with added pseudo code) of the strategy we needed to use for a client that was changing authentication solutions and needed it to be transparent to the user. We wanted this migration to be elegant and not cause headaches when the old one needed to be removed.

The beauty of doing this in a Strategy was any remnants of the old authentication system is now in the strategy and when we are happy everyone has been migrated, we just removed the Strategy and Devise will revert to it’s own. Simple.


Leave a Reply

Your email address will not be published. Required fields are marked *