Autenticación en Rails 3: Sorcery [QuickTut]



Prefacio

Una parte vital de las aplicaciones Rails es la autenticación. Esta palabra se refiere a la capacidad de una aplicación web para registrar usuarios y permitirles accesar a la misma (o a cierto contenido especial por ejemplo) gracias a ese registro en la base de datos. La autenticación no asigna roles ni da privilegios, simplemente nos permite registrar y reconocer usuarios. Hay diversos tipos de autenticación, tales como podrían ser por OpenID, Facebook Connect o bien, en base a un registro que asigna datos tales como nombre de usuario y contraseña, pero, (como ya expliqué arriba) el simple hecho de autenticar usuarios no significa darles permisos, o establecer niveles (como administrador, usuario normal etc). Esto quiere decir que por ejemplo, podemos restringir contenido en base a autenticación, pero entonces cualquiera que se registre y/o accese con sus datos de login a nuestra aplicación tendría acceso a dicho contenido. Si quisiéramos asignar roles, necesitaríamos hablar de Autorización (misma que no puede existir sin autenticación primero) y eso lo haremos en otra ocasión... De momento aprendamos a autenticar usuarios:

Como ya dijimos, hay muchos métodos de autenticación, aunque la que aprenderemos hoy es la clásica (registro de usuarios y login con Usuario y Contraseña). Así mismo, como hay varios métodos de autenticación, en Rails tenemos distintas maneras de integrar dichos métodos en nuestras aplicaciones. Si hablamos de autenticación clásica, entonces a mi en lo personal lo que me gusta más es la gema Sorcery.

NOTA: Si no sabes Rails checha las instrucciones de nuestro tutorial express para dicho framework antes de hacer esto.

Teoría

Los usuarios más avanzados casi siempre prefieren crear su mecanismo de autenticación desde cero, escribiendo todo el código y la lógica detrás del mismo. Los usuarios principiantes (y también los que son más prácticos), gustan por lo general de usar generadores para sus mecanismos de autenticación y por otro lado, aquellos que necesitan algo completamente ya hecho y con muchas funcionalidades extra (por ejemplo cuando se arma un proyecto con poco tiempo pero se tienen suficientes conocimientos del framework) optan por opciones como Devise.

Todos estos métodos tienen problemas y ventajas, aunque están muy localizados, es decir creados para un público y neceidades específicos. Si tuviera que elegir o recomendar un punto medio entre todos estos, sin duda elegiría sorcery, ya que es una gema que nos permitirá autogenerar un modelo y otros archivos que detallarán la lógica de la autenticación, pero todo de una manera muy minimalista de manera que nosotros podremos moldear completamente dichas cuestiones en la aplicación además de que la cuestión de los controladores y las vistas queda completamente en nuestras manos para que los escribamos según nuestras necesidades.

Práctica!

Muy bien, veamos como funciona todo esto vale? lo primero que haremos será crear una nueva aplicación Rails para nuestro tutorial (Yo la llamaré "AuthApp"), Una aplicación muy simple donde manejaremos 2 cosas: Usuarios y otra cosa, la que quieran. En mi caso serán Productos. Para crear nuestra aplicación y los entes que manejaremos correremos éste código en la consola:

1. rails new AuthApp
2. cd AuthApp
3. rails g controller Users index show new create edit update destroy
4. rails g controller Sessions new create destroy
5. rails g controller Productos index show new create edit update destroy

Por convención y comodidad, es necesario que el controlador de Usuarios se llame Users y el de Sesiones tenga por nombre Sessions, los demás pueden tener cualquier nombre... 

Seguido a esto, eliminamos las vistas que no necesitaremos, tales vistas son:

# De Usuarios
app/views/users/create.html.erb
app/views/users/destroy.html.erb
app/views/users/update.html.erb

# De Sesiones
app/views/sessions/create.html.erb
app/views/sessions/destroy.html.erb

# De Productos
app/views/productos/create.html.erb 
app/views/productos/update.html.erb
app/views/productos/destroy.html.erb

Recuerda que se eliminan porque esas son acciones que representan sucesos, y como tal no requieren vista prácticamente nunca.

Luego, Instalaremos sorcery en nuestra aplicación añadiendo a nuestro Gemfile estas 2 gemas:

gem 'sorcery'
gem "bcrypt-ruby", :require => "bcrypt"

Bien, una vez añadidas, hacemos bundle install en consola para que las instale en el proyecto y proseguimos corriendo el generador para sorcery, donde le pasaremos los módulos que queremos utilizar en la aplicación:

 rails generate sorcery:install remember_me

Para conocer los distintos módulos que puedes usar, visita la página GitHub de Sorcery.

En este caso, yo solo estoy añadiendo a sorcery con su módulo de remember_me que permitirá guardar la sesión del usuario para que se le recuerde... Sin embargo, además del sorcery core y remember_me, podemos añadir otros módulos como el de reset_password y demás para tener otras funcionalidades según ocupemos (obvio que cada módulo o módulos agregados requieren configuraciones distintas) y pues bueno, una vez hecho esto de elegir e instalar módulos, tenemos que configurar nuestro sorcery según nuestras necesidades:

Para esto abrimos el archivo config/initializers/sorcery.rb y ahí escogemos la configuración requerida, hay muchas opciones y todas están bien explicadas en la página del proyecto y en el mismo archivo con comentarios... Lo que haremos en ese archivo para nuestra aplicación de ejemplo del tutorial será simplemente especificar que queremos que el usuario acceda con su nombre de usuario solamente en el login independientemente de que se le haya pedido el e-mail en el registro. Para lograr esto, buscaremos username_attribute y cambiaremos la línea correspondiente de la siguiente manera:

-Antes-

# user.username_attribute_names =

-Despues-

user.username_attribute_names = :username

Una vez cambiada, abrimos nuestro modelo de usuarios que sorcery ya generó para nosotros al correr su comando generador (el archivo está en app/models/user.rb) y ahí configuramos los atributos accesibles y las validaciones correspondientes para nuestra autenticación; Para efectos del ejemplo usaremos este código en dicho archivo:

Aquí damos acceso de escritura al usuario para los datos de username, password, email y la confirmación de la contraseña, de manera que puedan escribirlos en el formulario que les presentaremos en alguna vista después. Luego corremos las validaciones pertinentes en los datos (presencia, originalidad etc según sea el caso) y también hacemos que el modelo funcione con la autenticación de sorcery.

Terminado esto, tenemos que crear las acciones y vistas de nuestros controladores que tienen que ver con la autenticación (como el de usuarios y el de sesiones), El controlador de usuarios (con sólamente las acciones necesarias para autenticación definidas) queda así:

Para la vista de New (que sería lo que nos permitiría crear usuarios) podríamos estar cómodos con:

Por otro lado, el controlador de sesiones con todas las acciones que requiere definidas quedaría así:

Y la vista de Login (Sessions/New) sería más o menos como esto:

Luego tendríamos que configurar nuestras rutas, de manera que añadiríamos los recursos y matches adecuados (además de otras configuraciones) para que todo funcione como se debe; Eliminaríamos las rutas auto-generadas y añadiríamos las siguientes líneas entonces:

get "logout" => "sessions#destroy", :as => "logout"
 get "login" => "sessions#new", :as => "login"
 get "signup" => "users#new", :as => "signup"
 resources :users
 resources :sessions
 resources :productos
 root :to => "users#index"

Nuestro archivo quedaría más o menos así:

Click para Agrandar

Y entonces, justo después, crearíamos la vista index para users, Añadiríamos los enlaces de Log in y Log Out a nuestra aplicación en el layout, y casi terminamos... En la vista index de users podemos tener lo que queramos... Para fines prácticos de este tutorial, tendremos una página cualquiera que tendrá un enlace a una sección que deberá estar protegida por el Login (Usuario que no se registre y acceda, no podrá verla) La vista Index de usuarios entonces quedaría así:

Como verán, la página que estamos protegiendo en ese link es "productos_path" es decir, la página principal de productos que está representada por su vista index. Hecho esto, tenemos que modificar nuestra plantilla de manera que muestre los enlaces de login y también permita que la aplicación emita flash messages (pues los configuramos en los controladores) El layout final quedaría así:

Y ahora, lo que tendríamos que hacer, es forzar al usuario a loguearse para ver la página/sección protegida, de otra manera, cualquiera independientemente de registrado o no, sigue siendo capaz de ver ese link protegido... Para realmente limitar el acceso a los usuarios que han hecho login, tendríamos que irnos al controlador de la sección o vista (página) que queremos proteger y añadir un before_filter que especifique lo que queremos (en este caso obligar a loguearse), esto se logra con la siguiente línea:

before_filter :require_login

Dentro del controlador de (en este caso) productos, lo que limitaría el acceso a las vistas (páginas) de productos únicamente a los usuarios que se registraron y han iniciado sesión. El controlador quedaría así más o menos (sin ninguna acción definida, sólo con el before_filter adecuado añadido):

Click para Agrandar
Una vez que hicimos esto, ya protegimos esa sección. Lo que faltaría, sería definir un comportamiento a tomar en caso de que un usuario no autenticado quisiera entrar a dicha sección (Generalmente mostrar un mensaje). Para hacer esto, abrimos nuestro application_controller.rb de app/controllers y definimos un método privado que se llame "not_authenticated" y haga lo que queremos. Para mostrar un mensaje que pida acceder antes de poder visitar la página protegida, el método en cuestión debería tener el siguiente código:

def not_authenticated
  redirect_to login_url, :alert => "First login to access this page."
end

Y el archivo al que tenemos que agregarle dicho método queda así:

Click para agrandar

Una vez que añadimos dicho método, lo único que nos queda es hacer migración con rake db:migrate en terminal, eliminar el archivo public/index.html y si todo lo hicimos bien, al iniciar nuestro servidor con rails s veríamos algo como esto:

Click para Agrandar

Podremos registrarnos, acceder y demás... Por otro lado, Únicamente los usuarios registrados y que hayan accedido podrán ver la sección protegida, ¡de eso se trata esto! jeje.

Como podrán ver, Sorcery es una solución bastante modular y práctica para habilitar la autenticación en nuestra aplicación Rails. No te olvides de visitar la página oficial del proyecto para investigar qué más se puede hacer con Sorcery.

P.D. Este tutorial es una adaptación al español del episodio #285 de Railscasts, Modifiqué el código y las instrucciones de manera que funcionara bien con Rails 3.2.x y con la nueva versión de sorcery (0.7.12 es la más actual al momento que escribo esto); También traté de generar un escenario mucho más viable de autenticación que el que nos muestran en el screencast (usar un nombre de usuario para el login en lugar del email) etc. Sin embargo, te recomiendo checar el video para darte una mejor idea de cómo y porqué todo esto funciona como funciona, la lógica detrás de la aplicación y demás.