API Rails: ¿la mejor forma de implementar la autenticación?

78

Estoy escribiendo una aplicación Rails 4 que expondrá una API para una aplicación móvil que aún no se ha desarrollado. Los usuarios se autenticarán mediante un correo electrónico y una contraseña de la aplicación móvil.

Si bien he encontrado bastante información sobre el tema. Es difícil discernir qué está anticuado o qué no es óptimo. He leído sobre la autenticación básica HTTP, que no parece demasiado segura, y la autenticación basada en token HTTP, pero no estoy seguro de cómo combinar eso con la autenticación normal de correo electrónico y contraseña (estoy usando Devise por la manera).

Solo me gustaría saber cuál es la mejor práctica actual sobre cómo implementar esto, así que estaré seguro de ir por el camino correcto.

Roma149
fuente
1
Sin conocer los detalles, mi pensamiento es OAuth2. Usa el portero o las gemas oauth2.
Joe Essey

Respuestas:

49

El punto importante, desde una perspectiva de seguridad, es intercambiar el correo electrónico y la contraseña del usuario por un token una vez y luego usar ese token para solicitudes posteriores. Esto es porque:

  1. No desea que la aplicación cliente sea responsable de retener la contraseña del usuario, donde un error o un ataque podrían hacer que se filtre; y
  2. Un token emitido por el servidor le brinda a usted (y a sus usuarios) la capacidad de caducar un token si es necesario, por ejemplo, para bloquear un dispositivo robado o bloquear un cliente API que se comporte mal.

Hay muchas formas de lograr esto con diferentes niveles de complejidad.

Aquí hay un tutorial que es muy reciente y tiene un tutorial completo para crear una API en Rails con autenticación basada en token (sin usar Devise, pero sigue siendo relevante para comprender los conceptos): https://labs.kollegorna.se/blog/ 2015/04 / build-an-api-now /

Matt Brictson
fuente
¡Gracias! Esto es lo que necesitaba entender.
Roma 149
Además del token de dispositivo, también puede utilizar el token de dispositivo. El token de dispositivo de esta respuesta puede ayudarlo a identificar el dispositivo correctamente, puede ayudar en caso de que otro dispositivo acceda a su sitio web.
duykhoa
5

Otra opción es incluir el módulo a continuación en su modelo de dispositivo y agregar el auth_token a su tabla.

app / modelos / preocupaciones / token_authenticable.rb

module TokenAuthenticatable
  extend ActiveSupport::Concern

  included do
    before_save :ensure_auth_token
  end

  module ClassMethods
    def find_by_token(token)
      find_by(auth_token: token)
    end
  end

  def ensure_auth_token
    self.auth_token = generate_auth_token if auth_token.blank?
  end

  private

  def generate_auth_token
    loop do
      token = Devise.friendly_token
      break token unless self.class.exists?(auth_token: token)
    end
  end
end

app / controllers / api / v1 / login_controller.rb

...
 def login_user(params)
    if params[:authentication]
      @user = User.find_by(auth_token: params[:authentication])
      if @user.nil?
        render json: err('login user by token failed', ERR_USER_NOT_FOUND), status: :not_found
        event('login_user_by_auth_failed', 'token', params[:authentication])
        return
      else
        render status: :ok, json: @user
        return
      end
    else
      user = user.find_by(email: params[:email])
      if user.nil?
        event('login_user_failed_not_found', 'user_email', params[:email])
        render json: err("login user not found #{params[:email]}", ERR_USER_NOT_FOUND), status: :not_found
        return
      end
      if user.access_locked?
        event('login_user_blocked', 'user_id', user.id)
        render json: err("login user account is locked : #{user.id}", ERR_USER_LOCKED), status: :unauthorized
        return
      end
      unless user.try(:valid_password?, params[:password])
        event("login_user_password_does_not_match #{user.id}", 'user_id',  user.id)
        render json: err('login user password does not match', ERR_PASSWORD_NOT_MATCH), status: :unauthorized
        return
      end
      event('login_user_succeeded', 'user_id', user.id)
      @user= user
      if @user.save
        response.headers['authentication'] = @user.auth_token
        render status: :ok, json: @user
        return
      else
        render json: @user.errors, status: :unprocessable_entity
        return
      end
    end
  end
...

Editar: error tipográfico de descifrado de código corregido

Antarr Byrd
fuente
además, eche un vistazo a la función has_secured_password de rieles
ilusionista
Creo que token_authenticable ha quedado obsoleto.
Kelsey Hannan
4

@ Roma149 esto es más una preferencia personal, pero la mayoría de las personas que recién están comenzando usan Devise, ya que es la OMI más fácil. OAuth2 también es una buena opción. Como nota más importante, siempre puede ir a The Ruby Toolbox

Allí hay mucha buena información sobre las gemas e incluso te dicen la edad y la popularidad de la gema. Esto también te permitirá diferenciar entre las gemas con las que la comunidad se está volviendo loca ahora o las que se han quedado obsoletas.

Recuerde que en Ruby y Ruby On Rails no siempre es mejor en cuanto a gemas, ¡sino lo que mejor se adapta a su proyecto!

El Gugaru
fuente
3

Tiddle gem proporciona una estrategia de Devise para la autenticación de tokens en aplicaciones Ruby on Rails solo API. Su característica principal es el soporte para múltiples tokens por usuario .

https://github.com/adamniedzielski/tiddle

Zakaria
fuente