The Itch to Switch
Devise is the 800-pound gorilla of Rails authentication. It’s reliable, secure, and does everything.
But eventually, you hit "The Wall."
Maybe you want a passwordless login flow. Maybe you're tired of overriding 15 different controller methods just to change a redirect. Maybe you just want to understand how your own app logs people in.
So you decide: "I’m going to rip out Devise and use Rails' built-in has_secure_password."
I’ve done this migration. It leads to a cleaner, faster codebase—but the path there is filled with landmines. Here are the biggest challenges you will face.
Challenge 1: The Database Schema Mismatch
Devise is opinionated about database columns.
- Devise uses:
encrypted_password - Rails (
has_secure_password) expects:password_digest
You have two options here, and both have trade-offs.
Option A: The Alias (Quick & Dirty)
You can tell your User model that password_digest is actually encrypted_password.
class User < ApplicationRecord
has_secure_password :password, validations: false
alias_attribute :password_digest, :encrypted_password
end
- The risk: It feels hacky. Any future gems or tools expecting standard Rails conventions might stumble.
Option B: The Migration (Clean & Risky)
You write a migration to rename the column.
rename_column :users, :encrypted_password, :password_digest
- The risk: You better make sure you don't have any downtime during this deployment. Also, if you have to rollback, things get messy fast.
Challenge 2: The "Recoverable" Module
Devise gave you "Forgot Password" for free. Now that you deleted it, no one can reset their password.
Writing a secure password reset flow is harder than it looks. You need to:
- Generate a unique, high-entropy token.
- Store a digest of that token (never store the raw token in the DB!).
- Set an expiration (e.g., 2 hours).
- Handle the email delivery.
- Crucial: Ensure the token is invalidated after use so it can't be replayed.
Devise handled the "timing attack" prevention and token hashing for you. You now own that security liability.
Challenge 3: Cookie Security & "Remember Me"
session[:user_id] is fine for a basic login. But what about "Remember Me"?
Devise's Rememberable module is surprisingly complex. It manages a persistent cookie, token rotation (sometimes), and secure comparison.
If you roll your own "Remember Me":
- You must sign/encrypt the cookie.
- You must worry about Session Hijacking. If an attacker steals that cookie, they are that user forever until the cookie expires.
- You need a way to "Invalidate all sessions" (e.g., if the user changes their password, all those old remember tokens need to die).
Challenge 4: The View & Controller Cleanup
This is the tedious part. Devise helpers are insidious—they get everywhere.
You will have to grep your entire project for:
-
authenticate_user!(Replace with your ownrequire_loginfilter) -
user_signed_in?(Replace withlogged_in?) -
current_user(You have to write this helper in ApplicationController) -
devise_error_messages!(You'll need to write your own error partials)
Pro Tip: Do not delete the Gem yet. Create a "Shim" module that maps the old Devise method names to your new logic so you don't have to change 500 files at once.
# app/controllers/concerns/auth_shim.rb
module AuthShim
def authenticate_user!
redirect_to login_path unless logged_in?
end
def user_signed_in?
logged_in?
end
end
Challenge 5: Legacy Password Compatibility
Devise uses BCrypt by default. Rails has_secure_password also uses BCrypt. Usually, they play nice.
However:
If your Rails app is old (migrated from Rails 3 or 4), Devise might be using an older hashing strategy (like SHA-1 with a salt) for older users. has_secure_password will not be able to authenticate those users.
The Fix:
You might need a custom authentication strategy that checks:
- Is this a BCrypt hash? Use
authenticate. - Is this a legacy hash? specific logic.
- If legacy, upgrade them to BCrypt upon successful login.
The Verdict: Is it worth it?
Don't do it if:
- You are an agency building an MVP for a client (Speed is key).
- You rely heavily on OmniAuth (Devise makes OAuth integration very smooth).
- You aren't comfortable with security concepts like Session Fixation or Timing Attacks.
Do it if:
- You are building a monolithic app intended to last 5+ years.
- You want a specialized flow (e.g., OTP only, magic links, multi-step onboarding).
- You want to reduce memory footprint and dependency bloat.
Removing Devise is one of the most educational things you can do in Rails. Just make sure you bring a flashlight—it’s dark down in the plumbing.
Have you survived a Devise removal? Share your war stories below! 👇
Top comments (0)