viernes, 14 de diciembre de 2012

iPhone y Rails con devise

Cuando queremos conectar una aplicación de iPhone con una aplicación de Ruby on Rails que tenga la gestión de usuarios con Devise, esto es lo que debemos hacer:

Primero, debemos tener el modelo User de devise con la extensión token_authenticatable. Si no lo tenemos, le añadimos el módulo :token_authenticatable en la clase User.

Realizamos una migración sobre User para los campos requeridos y la corremos sobre la tabla de usuarios:


rails g migration AddTokenToUser authentication_token:string:uniq

class AddTokenToUser < ActiveRecord::Migration
  def change
    add_column :users, :authentication_token, :string
    add_index :users, :authentication_token, :unique => true
  end
end

Ahora configuramos las rutas de la aplicación, para tener apartado el acceso  para aplicaciones móviles (formato JSON) del resto de aplicación Web.
....routes.draw do

devise_for :users
[...]

namespace :api do
  resources :tokens,:only => [:create, :destroy]
end

Necesitamos crear un controlador para manejar los token de acceso:

 rails g controller api::token create destroy

Que quedará más o menos así:

class Api::TokensController  < ApplicationController 
  skip_before_filter :verify_authenticity_token
  respond_to :json
  
  def create
    identificador = params[:login] || params[:email]
    password = params[:password]

    if request.format != :json
      render :status=>406, :json=>{:message=>I18n.t('errors.invalid_nojson')}
      return
    end

    if identificador.blank? or password.blank?
      render :status=>400, :json=>{:message=>I18n.t('errors.invalid_nouserpwd')}
      return
    end

    @user=User.find_by_email(identificador.downcase) || User.find_by_username(identificador.downcase)

    if @user.blank?
      render :status=>401, :json=>{:message=>I18n.t('errors.invalid_user')}
      return
    end

    @user.ensure_authentication_token
    @user.save!

    if @user.valid_password?(password)
      render :status=>200, :json=>{:token=>@user.authentication_token}
    else
      render :status=>401, :json=>{:message=>I18n.t('errors.invalid_user')}
    end

  end
  
  def destroy
    @user=User.find_by_authentication_token(params[:id])
    if @user.blank?
      render :status=>404, :json=>{:message=>I18n.t('errors.invalid_token')}
    else
      @user.reset_authentication_token!
      render :status=>200, :json=>{:token=>params[:id]}
    end
  end  

end  



Si ahora accedemos a http://localhost:3000/api/token.json vía POST con los parámetros adecuados, tendremos el token de sesión para el usuario