Jared
Jared

Reputation: 661

Devise Token Auth Will Not Authenticate for PUT Route

Hi thanks for viewing my question. I'm building out a Rails API which works with a React front end on a different server. I'm using the Devise Token Auth gem, and am able to successfully log in, log out, and make get requests for multiple resources without any problems. To handle the changing tokens I am updating the headers on each request.

The problem I am running into is when I try to make a PUT request to update a resource, in which case I get a 401. Full error message:

Started PUT "/api/stores/3/orders/1" for 127.0.0.1 at 2017-07-12 16:00:05 -0400
Processing by Api::OrdersController#update as HTML
  Parameters: {"order"=>{"id"=>1, "provider_notes"=>"tailor notes"}, "headers"=>{"client"=>"YVa0NIlxAdm6BLQXk0xeJw", "access-token"=>"bNc9BB0TgICIJzGfM4H_6A", "uid"=>"joe@joestailor.com"}, "store_id"=>"3", "id"=>"1"}
Can't verify CSRF token authenticity.
Filter chain halted as :authenticate_user! rendered or redirected
Completed 401 Unauthorized in 1ms (Views: 0.1ms | ActiveRecord: 0.0ms)

After checking, the access-token printed in the error message is in fact the token i got from the last request, so it should be good. Here's the controller I am working with. I'm not able to get passed the authenticate_user! before action.

class Api::OrdersController < ApplicationController
  before_action :authenticate_user!
  before_action :set_order, only: [:show, :update]

  def index
    render :json => current_user.store.open_orders.as_json(include: [:customer], methods: [:alterations_count])
  end

  def show
    render :json => @order.as_json(include: [:customer, :items => {include: :item_type}])
  end

  def update
    if @order.update(order_params)
      render :json => @order
      .as_json(include: [:customer, :items => {include: :item_type}])
    else
      byebug
    end
  end

  private

  def set_order
    @order = Order.find(params[:id])
  end

  def order_params
    if current_user.tailor?
      params.require(order).permit(:requester_notes, :arrived, :fulfilled)
    end
  end
end

Any reason why a put request might work differently than the get requests I've been using (for this same controller)? Any advice to get around this with the Devise Auth Token would be awesome. Thanks.

Upvotes: 0

Views: 1358

Answers (2)

SMAG
SMAG

Reputation: 798

If you want to use :authenticate_user! or similar, then make sure to pass in all the necessary parameters:

  • access-token
  • expiry
  • token-type
  • uid
  • client

It looks like you were missing expiry and token-type.

This will result in the user being authenticated and the current_user being set as expected, assuming the token and credentials are valid.

Note: these headers my need to be exposed in your CORS configuration:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource "*",
         expose: %w[access-token expiry token-type uid client],
         headers: :any,
         methods: :any
  end
end

Another Note: If you mounted your devise endpoint inside the api, then you will need to call a different :authenticate_user! method.

example:

namespace :api, defaults: { format: 'json' } do
  namespace :v1 do
    mount_devise_token_auth_for 'User', at: 'auth'

    # API routing and resources
  end
end

Then you would need to call :authenticate_api_v1_user! instead.

example:

 class Api::V1::ApiController < ApplicationController
   before_action :authenticate_api_v1_user!
 end

Upvotes: 0

buncis
buncis

Reputation: 2513

are you using :authenticate_user! from devise?

if yes, it can't work for api you need create your own helper method

you can put it in your api application_controller/model_controller

or cretae a module and include it wherever you need

then change the before action into authenticate_with_token!

  def current_user_api
    @current_user ||= User.find_by(auth_token: request.headers['Authorization'])
  end

  def user_signed_in_api?
    current_user_api.present?
  end

  def authenticate_with_token!
    render json: { errors: "Not authenticated" },
      status: :unathorized unless user_signed_in_api? 
  end

this book chapter 5 will help you http://apionrails.icalialabs.com/book/chapter_five

and for Can't verify CSRF token authenticity.

you can put this in your application controller

  protect_from_forgery with: :null_session
  skip_before_action :verify_authenticity_token, if: :json_request?

  protected
    def json_request?
      request.format.json?
    end

Upvotes: 1

Related Questions