В приложении My Rails используется функция "Усовершенствование" для аутентификации. У него есть приложение для сестры 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
И в config/routes.rb:
  namespace :api, defaults: { format: "json" } do
    devise_for :users
  end
Я немного из глубины, и я уверен, что здесь есть что-то, что мое будущее будет оглядываться назад и сжиматься (обычно есть). Некоторые части iffy:
  Во-первых,, вы заметите, что 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, но добавив, что эта линия была единственным способом, которым я мог бы получить эту чертову работу.
  В-третьих,, я хочу убедиться, что я понимаю, как работает аутентификация после входа пользователя. Скажем, у меня есть API-вызов GET /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. 
Спасибо!