Autenticación en Rails 8: el generator nativo que llega sin Devise

Durante más de una década, Devise fue la respuesta automática a «¿cómo añado autenticación a mi app Rails?». Es una gema madura, bien mantenida y con decenas de funcionalidades. Pero también es una caja negra: genera código que muchos desarrolladores no entienden del todo, tiene su propio sistema de vistas, sus propios helpers y una curva de aprendizaje propia. Rails 8 trae una alternativa nativa con el generator de autenticación.

El generator de autenticación de Rails 8

Un solo comando genera el esqueleto básico de autenticación:

rails generate authentication

Esto crea:

  • app/models/user.rb y app/models/session.rb: los modelos de usuario y sesión.
  • app/controllers/sessions_controller.rb: login y logout.
  • app/controllers/passwords_controller.rb: recuperación de contraseña.
  • app/controllers/concerns/authentication.rb: el concern con los helpers.
  • Vistas: formularios de login y recuperación de contraseña.
  • Migraciones: tablas users y sessions.
  • Mailer: PasswordsMailer para enviar el email de recuperación.

Qué genera el modelo User

# app/models/user.rb (generado)
class User < ApplicationRecord
  has_secure_password
  has_many :sessions, dependent: :destroy

  normalizes :email_address, with: -> { _1.strip.downcase }
end

has_secure_password es un método de ActiveRecord que lleva en Rails desde la versión 3.1. Usa BCrypt para hashear la contraseña y añade automáticamente los métodos authenticate, password= y password_confirmation=. Solo necesita que la tabla tenga una columna password_digest.

normalizes :email_address es una funcionalidad de Rails 7.1 que normaliza el email automáticamente antes de guardarlo: elimina espacios y convierte a minúsculas.

El modelo Session

# app/models/session.rb (generado)
class Session < ApplicationRecord
  belongs_to :user
end

# Migración correspondiente
class CreateSessions < ActiveRecord::Migration[8.0]
  def change
    create_table :sessions do |t|
      t.references :user, null: false, foreign_key: true
      t.string :ip_address
      t.string :user_agent
      t.timestamps
    end
  end
end

Cada login crea una fila en sessions. El token de sesión se guarda en una cookie signed. Esto permite, entre otras cosas, ver sesiones activas o invalidarlas individualmente (cerrar sesión en un dispositivo concreto).

El concern Authentication

# app/controllers/concerns/authentication.rb (generado, simplificado)
module Authentication
  extend ActiveSupport::Concern

  included do
    before_action :require_authentication
    helper_method :authenticated?
  end

  private

  def authenticated?
    Current.session.present?
  end

  def require_authentication
    resume_session || request_authentication
  end

  def resume_session
    Current.session ||= find_session_by_cookie
  end

  def find_session_by_cookie
    Session.find_by(id: cookies.signed[:session_id])
  end

  def request_authentication
    session[:return_to_after_authenticating] = request.url
    redirect_to new_session_url
  end
end

# En ApplicationController
class ApplicationController < ActionController::Base
  include Authentication
end

Para las acciones que no requieren autenticación (la página de inicio pública, el formulario de login), se usa allow_unauthenticated_access:

class SessionsController < ApplicationController
  allow_unauthenticated_access only: [:new, :create]

  def new; end

  def create
    if user = User.authenticate_by(email_address: params[:email_address],
                                   password: params[:password])
      start_new_session_for user
      redirect_to after_authentication_url
    else
      redirect_to new_session_url, alert: "Email o contraseña incorrectos"
    end
  end

  def destroy
    terminate_session
    redirect_to new_session_url
  end
end

Lo que no incluye el generator

El generator de Rails 8 es deliberadamente minimalista. No incluye:

  • Registro de usuarios: tienes que crear el formulario de registro y el controlador tú mismo.
  • Confirmación de email: no hay flujo de verificación de email al registrarse.
  • Autenticación social: Google, GitHub, etc. requieren OmniAuth u otra solución.
  • OTP / 2FA: no hay soporte de dos factores.
  • Magic links: no incluido por defecto.
  • Roles y permisos: el generator no toca autorización, solo autenticación.

Para aplicaciones con estos requisitos, Devise sigue siendo una opción sólida. Rodauth es otra alternativa que merece atención: es más explícita en su funcionamiento que Devise y tiene soporte nativo para OTP, magic links y WebAuthn.

Cuándo usar el generator nativo

El generator de Rails 8 va bien para:

  • Aplicaciones internas donde controlas los usuarios manualmente.
  • APIs donde la autenticación es simple (email + password, sin OAuth).
  • Proyectos educativos donde quieres entender cómo funciona la autenticación desde dentro.
  • Proyectos donde Devise era excesivo y solo necesitabas lo básico.

La ventaja principal sobre Devise es que el código generado es tuyo: puedes leerlo, modificarlo y entenderlo sin bucear en las entrañas de una gema. Si algo falla, el stack trace lleva directamente a tu código.

Si te interesa ver la autenticación en otro framework backend, el artículo sobre PHP 8.4 tiene contexto sobre cómo el ecosistema PHP gestiona la seguridad en aplicaciones web modernas.

Imagen: Pexels / Miguel Á. Padriñán

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP