Rails render auth_token to API response header - ruby-on-rails

I have custom JSON authentication API (without devise, etc). How do I render #user.authentication_token to response header instead of body?
in sessions_controller.rb
def create
if #user && #user.authenticate(params[:user][:password])
#user.sign_in_count += 1
#user.save!
render status: 200,
json: { success: true,
info: "Logged in sucessfully.",
data: { auth_token: #user.authentication_token } }
else
render status: :unprocessable_entity,
json: { success: false,
info: "Login failed." }
end
end

Try the response.header["auth_token"]
def create
if #user && #user.authenticate(params[:user][:password])
#user.sign_in_count += 1
#user.save!
render status: 200,
json: { success: true,
info: "Logged in sucessfully."}
response.headers["auth_token"] = #user.authentication_token
else
render status: :unprocessable_entity,
json: { success: false,
info: "Login failed." }
end
end

There is a headers object available in your controller action. So you can do the following:
headers['X-User-Token'] = #user.authentication_token

Related

Manual Update password Rails Devise

I have an API based Rails app and I need to add a changing password section for clients after login. this is y codes so far:
# routes.rb
resources :passwords, only: %i[index]
post '/passwords/update_password', to: 'passwords#update_password'
passwords_controller.rb
class Api::PasswordsController < ApplicationController
respond_to :json
before_action :auth_check
def auth_check
if !user_signed_in?
render json: {:status => false, :msg => 'Access denied!'}
end
end
def update_password
user = User.find(current_user['_id'])
password = params["password"]
if password && !password.blank?
user.password = user.password_confirmation = password
end
if user.save
render json: {company: user}, status: 200
else
render json: {message: "Problem updating company"}, status: 500
end
end
end
And this is XHR request from client-side
axios({
url: '/api/passwords/update_password',
method: 'POST',
body: {
password: password,
password_confirmation: password_confirmation
}
})
.then(response => {
console.log(response);
})
.catch(err => {
console.log(err);
});
Its not working!
You should be able to use current_user. I edited the code. If it doesn't work, can you write the error here? Make sure the post request goes to update_password action.
class Api::PasswordsController < ApplicationController
respond_to :json
before_action :auth_check
def update_password
password = params.dig(:password)
password_confirmation = params.dig(:password_confirmation)
if password.present? && password == password_confirmation
if current_user.update(password: password, pasword_confirmation: password_confirmation)
render json: { company: user }, status: 200
else
render json: { message: 'Problem updating company' }, status: 500
end
end
end
private
def auth_check
render json: { status: false, msg: 'Access denied!' } unless user_signed_in?
end
end

Returning Devise errors based on conditions with Rails

When I post a session to rails/devise, I either get response with the user data, or i get a bland error message. How can I send back custom messages based on what goes wrong with creating if the session?
current code:
class V1::SessionsController < ApiBaseController
skip_before_action :verify_authenticity_token
def create
email = params[:user] && params[:user][:email]
username = params[:user] && params[:user][:username]
#user = User.where(email: email).first if email
#user = User.where(username: username).first if username
if #user&.valid_password?(params[:user] && params[:user][:password])
#user.update_user_login_timestamp
respond_to do |format|
format.json { render '/users/show', status: :ok }
end
else
render json: {success: false, message: "no good"}, status: 500
end
end
private
def user_params
params.require(:user).permit(:email, :password, :username)
end
end
What I'd like to do:
if username_found_but_password_doesnt_match
render json: {error: "bad password"} status: 500
elsif username_not_found
render json: {error: "doesnt exist"} status: 500
end
You can write the create action as:
def create
email = params[:user] && params[:user][:email]
username = params[:user] && params[:user][:username]
#user = User.where(email: email).first if email
#user = User.where(username: username).first if username
respond_to do |format|
if #user.blank?
format.json { render json: {error: "doesnt exist"}, status: 404 }
elsif #user.valid_password?(params[:user] && params[:user][:password])
#user.update_user_login_timestamp
format.json { render '/users/show', status: :ok }
else
format.json { render json: {error: "bad password"}, status: 422 }
end
end
end

Trouble testing for an error thrown from AASM gem

I'm having trouble testing for a thrown AASM error.
Here is my controller method:
# controllers/jobs_controller.rb
def change_state
respond_to do |format|
if #job.close && #job.save
format.html { redirect_to #job, notice: 'Job has been closed.' }
format.json { render :show, status: :ok, location: #job }
else
format.html { render :show, notice: 'Job could not be closed.' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
My spec looks like this:
# spec/controllers/jobs_controller_spec.rb
describe "POST #change_state" do
it "cannot transition job from closed" do
job.aasm_state = "closed"
job.save!
post :change_state, params: {id: job.hash_id, user_id: user.id}
expect { response }.to raise_error(AASM::InvalidTransition)
end
end
The test fails (with the expected/desired failure):
Failure/Error: if #job.close && #job.save
AASM::InvalidTransition:Event 'close' cannot transition from 'closed'.
I'm just unable to figure out the right syntax to pass the test. I've tried a few variations of the expect line but can't seem to piece it together.
Any guidance is appreciated.
The exception is happening before the expect statement. Try it:
expect {
post(:change_state, params: { id: job.hash_id, user_id: user.id })
}.to(
raise_error(AASM::InvalidTransition)
)

Rails rendering json on destroy not working

I'm trying to render some specific json based on whether the destroy worked or not. However, the code gets past the #attachment.destroy... then throws an exception when it tries to render the json. Not sure what's going on here.
def delete
#attachment = Attachment.find(params[:attachment_id])
if #attachment.destroy
render json: {
status: 200,
message: MESSAGES_SUCCESS
}
else
render json: {
status: 422,
message: MESSAGES_FAILED
}
end
end
Destroy attachment and then check if there are any errors.
def delete
#attachment = Attachment.find(params[:attachment_id])
#attachment.destroy
if #attachment.errors.any?
render json: {
status: :unprocessable_entity, # 422
message: MESSAGES_FAILED
}
else
render json: {
status: :ok, # 200
message: MESSAGES_SUCCESS
}
end
end

Rails responds with status code 406

I have been working on a test for my function inside Ruby on Rails. However, the test (which expects a status code of :success) fails after receiving a status code 406. Here's the exact failure log:
Failure: Expected response to be a <:success>, but was <406>.
test_should_post_comment_through_token_successfully(CommentControllerTest)
test/functional/comment_controller_test.rb:271:in `block in <class:CommentControllerTest>'
I read a little about the 406 response, and found out that it stands of "Not Acceptable". so I tried setting the Accept, Content-Type, Accept-Language and Accept-Charset headers but I have had no luck.
Here's the code for my test:
test 'should post comment through token successfully' do
#params = {
id: 1,
body: "Test Comment",
username: "Bob"
}
#headers = {
"Accept" => "application/json",
"Accept-Language" => "en-US",
"Accept-Charset" => "utf-8",
"Content-Type" => "application/json",
"Token" => "abcdefg12345"
}
get :create_by_token, #params, #headers
assert_response :success
end
The create_by_token function inside the controller:
def create_by_token
#node = Node.find params[:id]
#user = User.find_by_username params[:username]
#body = params[:body]
#token = request.headers['Token']
p request.headers["Accept"]
p request.headers["Content-Type"]
p request.headers["Token"]
if #user && #user.token == #token
begin
#comment = create_comment(#node, #user, #body)
msg = {
status: :created,
message: "Created"
}
respond_to do |format|
format.xml { render xml: msg.to_xml }
format.json { render json: msg.to_json }
end
rescue CommentError
msg = {
status: :bad_request,
message: "Bad Request"
}
respond_to do |format|
format.xml { render xml: msg.to_xml }
format.json { render json: msg.to_json }
end
end
else
msg = {
status: :unauthorized,
message: "Unauthorized"
}
respond_to do |format|
format.xml { render xml: msg.to_xml }
format.json { render json: msg.to_json }
end
end
end
My route:
post '/bot/comment.:format', to: 'comment#create_by_token'
Am I missing something crucial? How do I go about solving this issue?
I would be happy to provide any other information you would need.
Seems this might be an error with respond_to do block. Kindly check with the routes whether you have configured as resources or resource.
Do update to resources than singular which will help with respond_to do block.
You can also try update your routes as/;
resources :samples, defaults: {format: :json}
Oh, stupid me. I realized that among all the params I was passing, the format was also being passed inside the URL. However, as in the test I was not mentioning a URL which I could pass in the format as the suffix (.xml or .json), I would have to mention the format inside the params explicitly. Here's the updated test code:
test 'should post comment through token successfully' do
#params = {
id: 1,
body: "Test Comment",
username: "Bob",
format: 'json'
}
#headers = {
"token" => "abcdefg12345"
}
post :create_by_token, #params, #headers
assert_response :success
end
Kudos #Sowmiya for leading me to this conclusion. Your answer was not exactly the solution I needed, but it provoked me to think.

Resources