Skip to content

Devise

SecuritySecurity/AuthenticationRuby

What it is

Devise is a flexible authentication solution for Rails built on Warden, providing registration, sessions, password recovery, confirmation and lockout as composable modules.

Include the modules you need on the model. Devise supplies controllers, routes and views, all of which can be overridden.

Installation

gem 'devise'

Getting started

The smallest useful thing you can do with it, and what each part means.

Model modules and route protection
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :confirmable, :lockable, :trackable

  validates :name, presence: true
end

# config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: { registrations: 'users/registrations' }

  authenticate :user do
    resources :books
  end
end

class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  # current_user, user_signed_in? are available in controllers and views
end
lockable and confirmable are worth enabling deliberately: they add brute-force protection and email verification, both of which are tedious and easy to get wrong by hand.

Advanced usage

Where the library earns its place over a simpler alternative.

Custom controllers and strong parameters
class Users::RegistrationsController < Devise::RegistrationsController
  before_action :configure_permitted_parameters

  protected

  # Extra fields must be permitted explicitly or they are silently dropped.
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: %i[name organisation])
    devise_parameter_sanitizer.permit(:account_update, keys: %i[name])
  end

  def after_sign_up_path_for(resource) = onboarding_path
end

# API mode: use devise-jwt rather than cookie sessions.
# config.jwt do |jwt|
#   jwt.secret = ENV.fetch('DEVISE_JWT_SECRET')
#   jwt.expiration_time = 30.minutes.to_i
# end
Silently dropped attributes are the most common Devise confusion — a custom sign-up field simply will not save until it is added to the parameter sanitiser.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Custom sign-up fields are not saved
They were not permitted. Add them via configure_permitted_parameters.
Devise routes conflict with your own
devise_for generates many routes. Define it before conflicting resources, or scope it under a path.

Best practices

  • Enable lockable and confirmable; brute-force protection and email verification matter.
  • Permit custom fields through devise_parameter_sanitizer or they are dropped without error.
  • Use devise-jwt for API-only applications rather than cookie sessions.
  • Never write your own password hashing or reset flow; this is where security bugs live.

Background

Why it exists, and what it was reacting to.

Devise packages the authentication features every application needs, correctly implemented. Rolling your own authentication is where security mistakes concentrate, which is why Devise remains the default.