Reputation: 113
I am trying to make an RoR backend for an iOS app. I have decided to try and secure the API using Devise (3.2.4). However, I am unable to register a user via http://localhost:3000/users
as generated by devise.
I have tried using both the default devise Registrations controller as well as overriding it and creating my own.
Using restkit, my iOS app is sending this JSON:
{"user":{"email":"[email protected]", "password":"Test123", "password_confirmation":"Test123"}}
Response is a 401 Unauthorized - HTTP Token: Access denied.
Started POST "/users.json" for 127.0.0.1 at 2014-08-24 18:19:34 -0400
Processing by Devise::RegistrationsController#create as JSON
Parameters: {"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "email"=>"[email protected]"}, "registration"=>{"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "email"=>"[email protected]"}}}
Can't verify CSRF token authenticity
Rendered text template (0.1ms)
Filter chain halted as :restrict_access_by_token rendered or redirected
Completed 401 Unauthorized in 5ms (Views: 2.5ms | ActiveRecord: 0.0ms)
Here, it seems that :restrict_access_by_token
is being called. However, I'm not sure why as this is a post to register a new user, so I have no token to send, but am expecting one back. Also, I don't know why it doubles the parameters sent and adds a duplicate registration
JSON array here.
After the above error, I tried ignoring ':restrict_access_by_token' for the 'create' method and updated routes accordingly
routes.rb
devise_for :users, :controllers => {:registrations => "api/registrations"}
app/controllers/api/registrations_controller.rb
module API
class RegistrationsController < Devise::RegistrationsController
prepend_before_filter :allow_params_authentication!, :only => :create
skip_before_filter :restrict_access_by_token, :only => :create
respond_to :json
def create
user = User.new(sign_up_params)
if user.save
render :json=> user, :status=>201
return
else
warden.custom_failure!
render :json=> user.errors, :status=>422
end
end
end
end
Response here is a 422 Unprocessable Entity
and the response is :
{"email":["can't be blank"],"password":["can't be blank"]}
Server log:
Started POST "/users.json" for 127.0.0.1 at 2014-08-24 18:30:08 -0400
Processing by API::RegistrationsController#create as JSON
Parameters: {"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "email"=>"[email protected]"}, "registration"=>{"user"=>{"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "email"=>"[email protected]"}}}
Can't verify CSRF token authenticity
(0.1ms) begin transaction
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = '[email protected]' LIMIT 1
(0.1ms) rollback transaction
Completed 422 Unprocessable Entity in 93ms (Views: 1.0ms | ActiveRecord: 0.3ms)
Thus it seems that somehow, Devise is getting params and then not processing them correctly. Perhaps to do with the second registration
JSON array.
Any ideas on how to fix these problems? should I ditch the custom controller? How do I get rid of the registration
JSON array that it seems Devise is making?
Thanks
Upvotes: 1
Views: 1030
Reputation: 113
I figured out a way to get around my problem. For Method 2 above (using my custom Registrations controller) I was getting the error response:
{"email":["can't be blank"],"password":["can't be blank"]}
This was because I was sending the password_confirmation
attribute as well, which is not needed for user registrations (only account updates I believe). After removing that, the JSON was parsed correctly by Devise.
For anyone having similar trouble with a Devise API, I found this gist helpful. There's a simple project here that I also used to model my JSON API to get around the deprecation of :token_authenticatable
that is posted in this repo by brianweiner.
The only change I had to make was in the user.rb
model. The last line of the loop generate_authentication_token
should read as follows to avoid a noMethodError (based on the gist mentioned above):
break token unless User.where(authentication_token: token).first
Upvotes: 1