Является ли этот API-интерфейс аутентификации Rails JSON (с использованием Devise) безопасным?

Приложение My Rails использует Devise для аутентификации. У него есть родственное приложение iOS, и пользователи могут входить в приложение iOS, используя те же учетные данные, которые они используют для веб-приложения. Поэтому мне нужен какой-то API для аутентификации.

Много подобных вопросов здесь указывают наэтот урок, но, похоже, устарела, так какtoken_authenticatable С тех пор модуль был удален из Devise, и некоторые строки выдают ошибки. (Я использую Devise 3.2.2.) Я попытался свернуть свой собственный, основываясь на этом уроке (иэтот), но я не уверен на 100% - я чувствую, что может быть что-то, что я неправильно понял или пропустил.

Во-первых, следуя советуэтот смыслЯ добавилauthentication_token атрибут текста к моемуusers таблица, и следующие кuser.rb:

before_save :ensure_authentication_token

def ensure_authentication_token
  if authentication_token.blank?
    self.authentication_token = generate_authentication_token
  end
end

private

  def generate_authentication_token
    loop do
      token = Devise.friendly_token
      break token unless User.find_by(authentication_token: token)
    end
  end

Тогда у меня есть следующие контроллеры:

api_controller.rb

class ApiController < ApplicationController
  respond_to :json
  skip_before_filter :authenticate_user!

  protected

  def user_params
    params[:user].permit(:email, :password, :password_confirmation)
  end
end

(Обратите внимание, что мойapplication_controller имеет линиюbefore_filter :authenticate_user!.)

апи / sessions_controller.rb

class Api::SessionsController < Devise::RegistrationsController
  prepend_before_filter :require_no_authentication, :only => [:create ]

  before_filter :ensure_params_exist

  respond_to :json

  skip_before_filter :verify_authenticity_token

  def create
    build_resource
    resource = User.find_for_database_authentication(
      email: params[:user][:email]
    )
    return invalid_login_attempt unless resource

    if resource.valid_password?(params[:user][:password])
      sign_in("user", resource)
      render json: {
        success: true,
        auth_token: resource.authentication_token,
        email: resource.email
      }
      return
    end
    invalid_login_attempt
  end

  def destroy
    sign_out(resource_name)
  end

  protected

    def ensure_params_exist
      return unless params[:user].blank?
      render json: {
        success: false,
        message: "missing user parameter"
      }, status: 422
    end

    def invalid_login_attempt
      warden.custom_failure!
      render json: {
        success: false,
        message: "Error with your login or password"
      }, status: 401
    end
end

апи / registrations_controller.rb

class Api::RegistrationsController < ApiController
  skip_before_filter :verify_authenticity_token

  def create
    user = User.new(user_params)
    if user.save
      render(
        json: Jbuilder.encode do |j|
          j.success true
          j.email user.email
          j.auth_token user.authentication_token
        end,
        status: 201
      )
      return
    else
      warden.custom_failure!
      render json: user.errors, status: 422
    end
  end
end

И вконфиг / routes.rb:

  namespace :api, defaults: { format: "json" } do
    devise_for :users
  end

Я немного не в себе, и я уверен, что здесь есть кое-что, на что мое будущее я оглянется и съежится (обычно это так). Некоторые сомнительные части:

во-первыхвы заметите, чтоApi::SessionsController наследует отDevise::RegistrationsController в то время какApi::RegistrationsController наследует отApiController (У меня также есть некоторые другие контроллеры, такие какApi::EventsController < ApiController которые имеют дело с более стандартным REST-материалом для других моих моделей и не имеют большого контакта с Devise.) Это довольно уродливое соглашение, но я не мог найти другой способ получить доступ к методам, которые мне нужны вApi::RegistrationsController, Учебник, на который я ссылался выше, содержит строкуinclude Devise::Controllers::InternalHelpers, но этот модуль, кажется, был удален в более поздних версиях Devise.

во-вторыхЯ отключил защиту CSRF линиейskip_before_filter :verify_authentication_token, У меня есть сомнения в том, что это хорошая идея - я вижу многопротиворечивый или жесложно понять совет о том, уязвимы ли API-интерфейсы JSON для CSRF-атак, но добавление этой строки было единственным способом, которым я мог заставить эту чертову работу работать

в-третьихЯ хочу убедиться, что я понимаю, как работает аутентификация, как только пользователь вошел в систему. Скажем, у меня есть вызов APIGET /api/friends который возвращает список друзей текущего пользователя. Как я понимаю, приложение для iOS должно было бы получить пользователяauthentication_token из базы данных (которая является фиксированным значением для каждого пользователя, который никогда не изменяется ??), затем отправьте его как параметр вместе с каждым запросом, например,GET /api/friends?authentication_token=abcdefgh1234тогда мойApi::FriendsController мог сделать что-то вродеUser.find_by(authentication_token: params[:authentication_token]) чтобы получить current_user. Это действительно так просто, или я что-то упустил?

Так что для тех, кто сумел прочитать весь этот гигантский вопрос до конца, спасибо за ваше время! Чтобы обобщить:

Безопасна ли эта система входа? Или я что-то упустил из виду или неправильно понял, например, когда дело доходит до атак CSRF?Правильно ли я понимаю, как проверять подлинность запросов после входа пользователей? (См. «В-третьих ...» выше.)Есть ли способ, которым этот код можно очистить или сделать лучше? Особенно уродливый дизайн, когда один контроллер наследуется отDevise::RegistrationsController а остальные изApiController.

Спасибо!

Ответы на вопрос(3)

Ваш ответ на вопрос