2016-02-05 7 views
0

Я работаю над проектом, который разделен на два приложения: - один рельс JSON API, который имеет дело с базой данных и передает данные как JSON - один «фронт» -end "rails, которое отправляет запросы API каждый раз, когда это необходимо, и отображать json-данные в приятном ключе.Несанкционированный запрос с использованием методов проверки подлинности как API для сброса пароля

Аутентификация для API - это токен, основанный на использовании gem'simple_token_authentication', что означает, что для большинства запросов, отправляемых в API, вы должны отправить токен пользователя & свой адрес электронной почты в заголовке, чтобы запрос был авторизирован.

Тот, кто работал над проектом передо мной, также установил систему аутентификации Devise на стороне API, чтобы разрешить прямой доступ к API-методам из навигатора после успешного входа с помощью пароля электронной почты &.

Я только начал кодирование в «интерфейсном приложении», которое должно запрашивать API, и у меня возникают проблемы, особенно с системой аутентификации.

Поскольку Devise уже установлен на API, я подумал, что было бы неплохо сделать логин пользователя в интерфейсном приложении, которое затем запросит методы разработки, представленные в API, для создания пароля пользователя, auth, реселлера ...

Проблема заключается в том, что методы devise представляют собой рендеринг html, а не JSON, поэтому я фактически должен был переопределить большинство контроллеров разработчика. Чтобы дать вам краткое представление о том, как это работает: Вы заполняете форму для регистрации в интерфейсном приложении, после чего параметры отправляются в интерфейсный контроллер приложений, который затем запрашивает у пользователя метод регистрации пользователя в API:

1) передний конец контроллер приложения:

def create 
    # Post on API to create USER 
    @response = HTTParty.post(ENV['API_ADDRESS']+'users', 
     :body => { :password => params[:user][:password], 
        :password_confirmation => params[:user][:password_confirmation], 
        :email => params[:user][:email] 
       }.to_json, 
     :headers => { 'Content-Type' => 'application/json' }) 
    # si le User est bien crée je récupère son email et son token, je les store en session et je redirige vers Account#new 
    if user_id = @response["id"] 
     session[:user_email] = @response["email"] 
     session[:user_token] = @response["authentication_token"] 
     redirect_to new_account_path 
    else 
     puts @response 
     @errors = @response["errors"] 
     puts @errors 
     render :new 
    end 
    end 

2) API-перекрытая разработать контроллер:

class RegistrationsController < Devise::RegistrationsController 
    def new 
    super 
    end 

    def create 
    @user = User.new(user_params) 
    if @user.save 
     render :json => @user 
    else 
     render_error 
    end 
    end 

    def update 
    super 
    end 

    private 

    def user_params 
    params.require(:registration).permit(:password, :email) 
    end 

    def render_error 
    render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity 
    end 
end 

Это работает нормально. Здесь я отправляю обратно пользователя, который был только что создан на API, как JSON, и я храню это токен аутентификации и его адрес электронной почты в сеансе hash.

Моя проблема связана с методом reset_password, для которого я пытаюсь повторно использовать некоторые из кода разработки. Во-первых, я прошу сбросить пароль, который генерирует токен для сброса пароля для пользователя, который запросил изменение. Это также генерирует электронное письмо пользователю со ссылкой (с токеном внутри), указывающим на форму пароля сброса для конкретного пользователя. Это хорошо работает.Я получаю ссылку в электронном письме, затем собирается в виде edit_password на моем фронтального приложение:

Изменить пароль

<form action="https://stackoverflow.com/users/password" method='post'> 
    <input name="authenticity_token" value="<%= form_authenticity_token %>" type="hidden"> 
    <%= hidden_field_tag "[user][reset_password_token]", params[:reset_password_token] %> 
    <%=label_tag "Password" %> 
    <input type="text" name="[user][password"> 
    <%=label_tag "Password Confirmation" %> 
    <input type="text" name="[user][password_confirmation]"> 
    <input type="Submit" value="change my password"> 
</form> 

Когда форма отправлена ​​она проходит через мой передний конец контроллера приложения:

def update_password 
     @response = HTTParty.patch(ENV['API_ADDRESS']+'users/password', 
     :body => { 
      :user => { 
      :password => params[:user][:password], 
      :password_confirmation => params[:user][:password_confirmation], 
      :reset_password_token => params[:user][:reset_password_token] 
      } 
       }.to_json, 
     :headers => { 'Content-Type' => 'application/json' }) 
    end 

, который затем вызывает мой перекрытая Завещание :: PasswordController (метод обновления):

# app/controllers/registrations_controller.rb 
class PasswordsController < Devise::RegistrationsController 
    # POST /resource/password 
    def create 
    if resource_params[:email].blank? 
     render_error_empty_field and return 
    end 
    self.resource = resource_class.send_reset_password_instructions(resource_params) 
    yield resource if block_given? 

    if successfully_sent?(resource) 
     render_success 
    else 
     render_error 
    end 
    end 

    def update 
    self.resource = resource_class.reset_password_by_token(resource_params) 
    yield resource if block_given? 

    if resource.errors.empty? 
     resource.unlock_access! if unlockable?(resource) 
     render_success 
    else 
     render_error 
    end 
    end 


    private 

    # TODO change just one big method render_error with different cases 

    def render_success 
    render json: { success: "You will receive an email with instructions on how to reset your password in a few minutes." } 
    end 

    def render_error 
    render json: { error: "Ce compte n'existe pas." } 
    end 
    def render_error_empty_field 
    render json: { error: "Merci d'entrer un email" } 
    end 
end 

Однако запрос всегда Несанкционированный:

Started PATCH "https://stackoverflow.com/users/password" for ::1 at 2016-02-05 11:28:30 +0100 
Processing by PasswordsController#update as HTML 
    Parameters: {"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "reset_password_token"=>"[FILTERED]"}, "password"=>{"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "reset_password_token"=>"[FILTERED]"}}} 
Completed 401 Unauthorized in 1ms (ActiveRecord: 0.0ms) 

Я не понимаю, почему этот последний запрос несанкционированный?

ответ

0

Ваш предшественник, вероятно, совершил беспорядок на стороне API только для его удобства.

Мы знаем, что использование файлов cookie для API является действительно плохой идеей, поскольку он оставляет двери широко открытыми для атак CSRF/XSRF.

Мы не можем использовать защиту RSR CSRF для API, поскольку она работает только как гарантия того, что запрос возник из нашего собственного сервера. И API, который может использоваться только с вашего собственного сервера, не очень полезен.

Разработчик по умолчанию использует стратегию auth на основе файлов cookie, потому что это то, что работает для веб-приложений и Devise, - это упрощение создания аутентификации в веб-приложениях.

Так что вам нужно либо полностью удалить Devise из приложения API, либо преобразовать Devise для использования стратегии на основе токенов. Вы также должны рассмотреть возможность удаления промежуточного программного обеспечения сеансов из API app. Кроме того, контроллеры Devise настолько сильно наклонены к взаимодействию с клиентом, что попытка избить их в контроллерах API будет очень запутанной.

Обновление пароля в API просто:

class API::V1::Users::PasswordsController 
    before_action :authenticate_user! 
    def create 
    @user = User.find(params[:user_id]) 
    raise AccessDenied unless @user == current_user 
    @user.update(password: params[:password]) 
    respond_with(@user) 
    end 
end 

Это очень упрощенный пример - но дело в том, если вы отбросите все барахло от контроллера, связанного с формами/вспышек и перенаправляет есть не то, что вы действительно собираетесь использовать повторно.

Если ваше front-end приложение является «классическим» клиентом/сервером Rails-приложения, вы можете использовать обычный файл cookie на основе cookie (Devise) и позволить ему совместно использовать базу данных с помощью приложения API. Авторизованный токен не работает с классическими приложениями клиент/сервер из-за своей безгражданности.

Если переднее приложение представляет собой SPA, например Angular или Ember.js, вы можете захотеть вместо этого настроить собственный поставщик OAuth с помощью Doorkeeper.

Outh service diagram

+0

спасибо @max. Я думаю, что я уже использую аутентификацию на основе токенов с помощью gem '' 'simple_token_authentication''': при создании пользователя он автоматически генерирует auth_token, и когда этот пользователь входит в систему, API отображает токен аутентификации в интерфейсном приложении ; Я храню его в '' 'session []' '', и затем, в каждом запросе, который пользователь делает впоследствии, пока он не выйдет из системы, я отправляю auth_token в заголовках.Как вы говорите, вероятно, мало пользы в разработке, если мне нужно переопределить каждого отдельного редактора, но скажете ли вы, что система токенов, которую я описал здесь, является хорошим решением в моем случае? –

+0

Я не уверен - вы используете сеанс в своем приложении API? Если это так - нет, это не очень хорошая идея, поскольку файлы cookie оставляют API открытым для XSRF. – max

+0

no Я не думаю, что я использую сеансы в своем API, API должен быть доступен только из внешнего приложения с использованием токена, аналогичного тому, что я описал в своем предыдущем комментарии. Не будет способа получить доступ к API за счет прямого входа в API для ex, вам всегда придется пройти через это приложение переднего плана и получить свой токен от API. Это яснее? –

 Смежные вопросы

  • Нет связанных вопросов^_^