I am attempting to get a user registration endpoint setup for my rails application so that I can access the app's functionality in an iOS rendition. I've gone ahead and namespaced my API, and so far have managed to get user authentication working using Devise and JWT's.
This is great, however, I also need to ability to register a user via the API. To be frank, I have no idea how to correctly implement this. Several Google searches either bring up outdated articles, use the deprecated token authenticatable, or have never been answered.
Below is the code that I believe pertains most to this question:
namespace :api do
namespace :v1 do
devise_for :users, controllers: { registrations: 'api/v1/registrations' }
resources :classrooms
resources :notifications
end
end
end
class Api::V1::RegistrationsController < Devise::RegistrationsController
respond_to :json
def create
if params[:email].nil?
render :status => 400,
:json => {:message => 'User request must contain the user email.'}
return
elsif params[:password].nil?
render :status => 400,
:json => {:message => 'User request must contain the user password.'}
return
end
if params[:email]
duplicate_user = User.find_by_email(params[:email])
unless duplicate_user.nil?
render :status => 409,
:json => {:message => 'Duplicate email. A user already exists with that email address.'}
return
end
end
@user = User.create(user_params)
if @user.save!
render :json => {:user => @user}
else
render :status => 400,
:json => {:message => @user.errors.full_messages}
end
end
private
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute, :first_name, :last_name, :access_code])
end
end
http://localhost:3000/api/v1/users
{
"message": [
"Email can't be blank",
"Password can't be blank",
"Access code is invalid [Beta]."
]
}
Any help would greatly be appreciated, as I am keen on learning more (and getting this to work!).
Here is what I get on the server after making a post request to generate a user...
Started POST "/api/v1/users" for 127.0.0.1 at 2017-02-22 09:22:11 -0800
Processing by Api::V1::RegistrationsController#create as */*
Parameters: {"user"=>{"email"=>"[email protected]", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "access_code"=>"uiux"}}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."email" IS NULL LIMIT $1 [["LIMIT", 1]]
Completed 400 Bad Request in 2ms (Views: 0.2ms | ActiveRecord: 0.4ms)
class Api::V1::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
respond_to :json
def create
@user = build_resource(sign_up_params)
if @user.persisted?
# We know that the user has been persisted to the database, so now we can create our empty profile
if resource.active_for_authentication?
sign_up(:user, @user)
render :json => {:user => @user}
else
expire_data_after_sign_in!
render :json => {:message => 'signed_up_but_#{@user.inactive_message}'}
end
else
if params[:user][:email].nil?
render :status => 400,
:json => {:message => 'User request must contain the user email.'}
return
elsif params[:user][:password].nil?
render :status => 400,
:json => {:message => 'User request must contain the user password.'}
return
end
if params[:user][:email]
duplicate_user = User.find_by_email(params[:email])
unless duplicate_user.nil?
render :status => 409,
:json => {:message => 'Duplicate email. A user already exists with that email address.'}
return
end
end
render :status => 400,
:json => {:message => resource.errors.full_messages}
end
end
protected
# If you have extra params to permit, append them to the sanitizer.
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute, :first_name, :last_name, :access_code])
end
end
I'm pretty sure my main issue at this point is the format of my params, so any push in the right direction for this would be great. I did find this post but am finding it a little difficult to follow in terms of what got their API to work...
The simplest way to handle authentication is through the use of HTTP, where the username and password are sent alongside every API call. You can use an HTTP header and encode the username and password.
Devise is the cornerstone gem for Ruby on Rails authentication. With Devise, creating a User that can log in and out of your application is so simple because Devise takes care of all the controllers necessary for user creation ( users_controller ) and for user sessions ( users_sessions_controller ).
Both authentication and authorization are core to the security of APIs. They play different roles but together they ensure that the right legitimate consumer has the right permissions to access an API.
Here is 2 solution, choose one you like.
Override devise_parameter_sanitizer:
class ApplicationController < ActionController::Base
protected
def devise_parameter_sanitizer
if resource_class == User
User::ParameterSanitizer.new(User, :user, params)
else
super # Use the default one
end
end
end
Override sign_up_params:
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
If you go deeping to Devise ParameterSanitizer, the resource_name will be :api_v1_user
, not just :user
because of your routes:
namespace :api do
namespace :v1 do
devise_for :users, controllers: { registrations: 'api/v1/registrations' }
end
end
Error resource_name will cause sign_up_params
always return empty hash {}
Why don't you try something like this:
user = User.create(sign_up_params)
if user.save
render status: 200, json: @controller_blablabla.to_json
else
render :status => 400,
:json => {:message => @user.errors.full_messages}
end
or even better. You might use something like tiddle gem to make session more secure:
respond_to :json
def create
user = User.create(sign_up_params)
if user.save
token = Tiddle.create_and_return_token(user, request)
render json: user.as_json(authentication_token: token, email:
user.email), status: :created
return
else
warden.custom_failure!
render json: user.errors, status: :unprocessable_entity
end
end
You might use httpie --form
to make the request:
http --form POST :3000/users/sign_up Accept:'application/vnd.sign_up.v1+json' user[email]='[email protected]' user[username]='hello' user[password]='123456789' user[password_confirmation]='123456789'
do not forget:
def sign_up_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
I don't know what i'm missing, let me know if i'm wrong or something is wrong and i did't realize!
Regards!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With