1

This is my user:

class User < ActiveRecord::Base
  has_secure_password
end

This is how someone can authenticate via API:

module Api
  class SessionsController < ApiController
    def create
      user = User.find_by_email(params[:user][:email])
      if user && user.authenticate(params[:user][:password])
        # some login logic
      else
        render json: {messages: user.errors.full_messages}, status: :unauthorized
      end
    end
  end
end

If I pass an incorrect password, I get 401 Unauthorized as expected. However, user.errors is blank. How can I access has_secure_password authentication errors?

1
  • 1
    Can you not manually add messages: "Invalid email or password"? - You are not validating any attributes, only that the user can be authenticated. AFAIK has_secure_password provides validations on create of object but just returns false when user cannot be authenticated. Commented Feb 19, 2014 at 16:01

2 Answers 2

3

only validation errors get populated in active record errors. Incorrect password is not a validation error. Why can't you set the message explicitly since the only possible error is email/password invalid

module Api
  class SessionsController < ApiController
    def create
      user = User.find_by_email(params[:user][:email])
      if user && user.authenticate(params[:user][:password])
        # some login logic
      else
        render json: {messages: ["Invalid Email or Password"]}, status: :unauthorized
      end
    end
  end
end
Sign up to request clarification or add additional context in comments.

Comments

1

It's pretty easy to do. In short, put a method (called password_verified in this example) on the User model that adds an error if authenticate fails.

# app/models/user.rb
def password_verified(password)
  verified = authenticate(password)
  errors.add(:password, 'is invalid') unless verified
  verified
end

Now, instead of calling authenticate call password_verified. Your example it would look like this:

module Api
  class SessionsController < ApiController
    def create
      user = User.find_by_email(params[:user][:email])
      # Here is where we call our wrapper method instead of authenticate
      if user && user.password_verified(params[:user][:password])
        # some login logic
      else
        render json: {messages: user.errors.full_messages}, status: :unauthorized
      end
    end
  end
end

This is using ActiveModel::Errors just like custom validators are meant to do, so it's well documented (i.e. Rails Guides, ActiveRecord Validations, errors.add). Although, I put password_verified on the user model here, it can go anywhere like a service or concern.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.