Search code examples
ruby-on-railsrubyhttpsdevisecredentials

Proper way to send user credentials to rails API


I've setup devise token authentication on my Rails API And it's working properly, however I'm unsure of the most proper secure way to send user credentials when logging in/registering... To give you some context, the API will be used as the backend for a mobile application when it's completed.

Here's what I have for my create method inside of my users_controller.rb:

def create
    user = User.new(user_params)
    if user.save
      render json: user, status: 201
    else
      render json: { errors: user.errors}, status: 422
    end
end

As of now, I'm sending them as parameters through the URL (email, password, password confirmation).

My user_params method:

def user_params
  # Seems to expect a user object as the param, unsure of how to replicate this on the app side
  # params.require(:user).permit(:email, :password, :password_confirmation)

  # Expects to see params[:user] as a string representation of JSON containing fields
  # email, password, and password_confirmation.
  ActionController::Parameters.new(JSON.parse(params.require(:user))).permit(:email, :password, :password_confirmation)
end

As you can see from my comments, I'm quite unsure how to proceed and I'd like to create as secure a process as possible with the setup that I have.

These are some questions that I have about sending user credentials to my API from a mobile application:

Is it adequate to use HTTPS and send user credentials through parameters?

How can I do this using the first method defined inside of my user_params? It seems that sending a JSON object and parsing it with my second user_param definition (the one not commented out) is the only way to accomplish this. I was originally sending email, password, password_confirmation as their own params, but then I found out that params.require can only take one argument at a time, and then my errors would only show one field that is missing when there could be multiple, hence the reason why I'm sending it as one object.

Am i going about this all wrong? or headed in the right direction?

Thanks for all of your help ahead of time!


Solution

  • Yeah, what you have is a good start.

    Is it adequate to use HTTPS and send user credentials through parameters?

    It's not only adequate, it's socially responsible of you to send them via HTTPS. At the top of your controller you can force your controller to only respond to ssl requests like so:

    force_ssl unless Rails.env.test?
    

    How can I do this using the first method defined inside of my user_params? It seems that sending a JSON object and parsing it with my second user_param definition (the one not commented out) is the only way to accomplish this. I was originally sending email, password, password_confirmation as their own params, but then I found out that params.require can only take one argument at a time, and then my errors would only show one field that is missing when there could be multiple, hence the reason why I'm sending it as one object.

    You can reduce this all down to simply:

    def user_params
      params.permit(:email, :password, :password_confirmation)
    end
    

    Rails, by default, will parse JSON into the params hash for you. Using the code above you can also avoid having to put your email and password inside a "user" key on the json object.

    { email: '...', password: '...' } VS. { user: { email: '...', password: '...' } }
    

    Am i going about this all wrong? or headed in the right direction?

    You're on the right track. Another minor suggestion, which is totally developer preference is to use keywords for the response types instead of the integers themselves. I just think it reads nicely that way. Here's what it would look like:

    def create
        user = User.new(user_params)
        if user.save
          render json: user, status: :created
        else
          render json: { errors: user.errors }, status: :unprocessable_entity
        end
    end