Alex N.
Alex N.

Reputation: 173

401 unauthorized for PUT/PATCH/DELETE with Devise and Rails-api

I have a RAILS API that integrates Devise and Doorkeeper. My POST request to registrations#create works but PUT/PATCH/DELETE results in "401 unauthorized" error. I suspect it might be some issue with authentication on Devise but that's where I'm stuck. Perhaps I am missing how to deal with current_user or skip_before_filters? I have tried a number of things like adding

skip_before_filter :verify_authenticity_token
skip_before_filter :authenticate_user!

Thank you!

routes.rb

require 'api_constraints'

Rails.application.routes.draw do

    use_doorkeeper
    devise_for :users, only: [:registrations, :passwords, :confirmations], controllers: {registrations: "api/registrations"}, defaults: { format: :json }

    namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
        scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
            get 'users/me', to: 'users#me'
        end
    end
end

rake routes

Prefix Verb   URI Pattern                                  Controller#Action
                          GET    /oauth/authorize/:code(.:format)             doorkeeper/authorizations#show
      oauth_authorization GET    /oauth/authorize(.:format)                   doorkeeper/authorizations#new
                          POST   /oauth/authorize(.:format)                   doorkeeper/authorizations#create
                          DELETE /oauth/authorize(.:format)                   doorkeeper/authorizations#destroy
              oauth_token POST   /oauth/token(.:format)                       doorkeeper/tokens#create
             oauth_revoke POST   /oauth/revoke(.:format)                      doorkeeper/tokens#revoke
       oauth_applications GET    /oauth/applications(.:format)                doorkeeper/applications#index
                          POST   /oauth/applications(.:format)                doorkeeper/applications#create
    new_oauth_application GET    /oauth/applications/new(.:format)            doorkeeper/applications#new
   edit_oauth_application GET    /oauth/applications/:id/edit(.:format)       doorkeeper/applications#edit
        oauth_application GET    /oauth/applications/:id(.:format)            doorkeeper/applications#show
                          PATCH  /oauth/applications/:id(.:format)            doorkeeper/applications#update
                          PUT    /oauth/applications/:id(.:format)            doorkeeper/applications#update
                          DELETE /oauth/applications/:id(.:format)            doorkeeper/applications#destroy
oauth_authorized_applications GET    /oauth/authorized_applications(.:format)     doorkeeper/authorized_applications#index
oauth_authorized_application DELETE /oauth/authorized_applications/:id(.:format) doorkeeper/authorized_applications#destroy
         oauth_token_info GET    /oauth/token/info(.:format)                  doorkeeper/token_info#show
            user_password POST   /users/password(.:format)                    devise/passwords#create {:format=>:json}
        new_user_password GET    /users/password/new(.:format)                devise/passwords#new {:format=>:json}
       edit_user_password GET    /users/password/edit(.:format)               devise/passwords#edit {:format=>:json}
                          PATCH  /users/password(.:format)                    devise/passwords#update {:format=>:json}
                          PUT    /users/password(.:format)                    devise/passwords#update {:format=>:json}
 cancel_user_registration GET    /users/cancel(.:format)                      api/registrations#cancel {:format=>:json}
        user_registration POST   /users(.:format)                             api/registrations#create {:format=>:json}
    new_user_registration GET    /users/sign_up(.:format)                     api/registrations#new {:format=>:json}
   edit_user_registration GET    /users/edit(.:format)                        api/registrations#edit {:format=>:json}
                          PATCH  /users(.:format)                             api/registrations#update {:format=>:json}
                          PUT    /users(.:format)                             api/registrations#update {:format=>:json}
                          DELETE /users(.:format)                             api/registrations#destroy {:format=>:json}
        user_confirmation POST   /users/confirmation(.:format)                devise/confirmations#create {:format=>:json}
    new_user_confirmation GET    /users/confirmation/new(.:format)            devise/confirmations#new {:format=>:json}
                          GET    /users/confirmation(.:format)                devise/confirmations#show {:format=>:json}
             api_users_me GET    /users/me(.:format)                          api/v1/users#me {:format=>:json, :subdomain=>"api

registrations_controller.rb (that overrides Devise)

include ActionController::ImplicitRender

class Api::RegistrationsController < Devise::RegistrationsController
  clear_respond_to
  respond_to :json
  respond_to :html, only: []
  respond_to :xml, only: []

  skip_before_filter :verify_authenticity_token
  before_filter :not_allowed, only: [:new, :edit, :cancel]

  def not_allowed
    render json: {error: "Method Not Allowed"}, status: 405
  end

  private

  def sign_up_params
    params.require(:user).permit([
      :email,
      :password,
      :password_confirmation,
      :first_name,
      :last_name,
    ])
  end

  def account_update_params
    params.require(:user).permit([
      :email,
      :first_name,
      :last_name,
      :password,
      :password_confirmation,
      :current_password
    ])
  end
end

Application.rb

class ApplicationController < ActionController::API
  respond_to :json

  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers

  def cors_preflight_check
    if request.method == 'OPTIONS'
      headers['Access-Control-Allow-Origin'] = '*'
      headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
      headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version, Token'
      headers['Access-Control-Max-Age'] = '1728000'

      render text: '', content_type: 'text/plain'
    end
  end

  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, GET, PUT, DELETE, OPTIONS'
    headers['Access-Control-Allow-Headers'] = 'Origin, Content-Type, Accept, Authorization, Token'
    headers['Access-Control-Max-Age'] = "1728000"
  end

  def current_resource_owner
    User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
  end

end

Upvotes: 2

Views: 2376

Answers (1)

Anil Maurya
Anil Maurya

Reputation: 2328

You have selectively override Devise::RegistrationsController methods, which are :new, :edit, :cancel.

Rest methods are not defined in your class therefore they will be served by Devise::RegistrationsController.

If you open devise source code, you will see :

class Devise::RegistrationsController < DeviseController
  prepend_before_filter :require_no_authentication, only: [:new, :create, :cancel]
  prepend_before_filter :authenticate_scope!, only: [:edit, :update, :destroy]

As you can see that :create action does not require authentication therefore you don't see 401 for POST request as it matches to create action.

PUT/PATCH matches to update action which requires authentication, similarly DELETE matches to 'destroy' action which also requires authentication therefore you are getting this 401 error.

To solve this issue, Add doorkeeper authorize for protected action after overriding actions in your RegistrationsController.

Upvotes: 1

Related Questions