简体   繁体   中英

Access authenticated routes in rails api devise

Can someone help me figure out how to properly access authenticated routes with devise in rails? I have figured out how I can authenticate a session and get a token, but I get an error about needing to sign in when I pass that token in authenticated routes. This is likely a curl formatting problem but I am new to rails and I am not quite sure how to do it.

I can pass the curl command and get back a proper token:

curl -X POST 'http://localhost:3000/users/sign_in.json' -d 'user[email]=user@name.com&user[password]=password'

I get back:

{"token":"at6aig30jzulnnyomrm4c5","user_email":"user@name.com"}

The issue is that when I then try to access the user's route I get a permission denied error:

curl -L 'http://localhost:3000/users/me.json?token=at6aig30jzulnnyomrm4c5'
{"error":"You need to sign in or sign up before continuing."}

I have my routes which look like this:

  devise_for :users, controllers: { sessions: 'sessions' }, :skip => [:passwords]
  resources :users, only: [:create, :update] do 
    get 'me' => 'users#me', on: :collection
  end

The corresponding routes look like this:

      new_user_session GET        /users/sign_in(.:format)                  sessions#new
          user_session POST       /users/sign_in(.:format)                  sessions#create
  destroy_user_session DELETE     /users/sign_out(.:format)                 sessions#destroy
              me_users GET        /users/me(.:format)                       users#me
                 users POST       /users(.:format)                          users#create
                  user PATCH      /users/:id(.:format)                      users#update
                       PUT        /users/:id(.:format)                      users#update

My users controller looks like this:

class UsersController < ApplicationController
  before_action :authenticate_user!, except: [:create, :start_password_reset, :finish_password_reset]
  before_action :assert_reset_token_passed, only: [:finish_password_reset]

  def create
    user = User.new(register_params)

    if user.save
      render json: user, status: :created
    else
      render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def update
    if current_user.update(user_params)
      render json: current_user
    else
      render json: { errors: current_user.errors.full_messages }, status: :unprocessable_entity
    end
  end

  def me
    if current_user
      render json: current_user, status: 200
    else
      render json: {}, status: 400
    end
  end

  def start_password_reset
    user = User.where(email: params[:email]).first
    user.send_reset_password_instructions if user
    render json: {}
  end

  def finish_password_reset
    user = User.with_reset_password_token(params[:reset_password_token])
    if user
      if user.reset_password!(reset_params[:password], reset_params[:password_confirmation])
        render json: {}
      else
        render json: { errors: user.errors.full_messages }, status: 400
      end
    else
      render json: { errors: ['Invalid password reset request.'] }, status: 403
    end
  end

  private

  def register_params
    params.require(:user).permit(:email, :password, :time_zone)
  end

  def user_params
    params.require(:user).permit(:include_email_memory, :time_zone, email_times: [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday])
  end

  def reset_params
    params.permit(:password, :password_confirmation)
  end

  # Check if a reset_password_token is provided in the request
  def assert_reset_token_passed
    render(json: { errors: ['Invalid password reset request.'] }, status: 403) if params[:reset_password_token].blank?
  end

end

For what it is worth, I am trying to get this working with Ember simple-auth so I can properly load a user's profile. Thanks for the help!

EDIT: As requested, here is my application controller:

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :null_session
  skip_before_filter :verify_authenticity_token

  before_filter :authenticate_user_from_token!
  #before_filter :authenticate_user!

  around_action :user_time_zone, if: :current_user

  def index
    render file: 'public/index.html'
  end

  protected

  # def authenticate_user!
  #   render(json: {}, status: 401) unless current_user
  # end

  private

  def authenticate_user_from_token!
    authenticate_with_http_token do |token, options|
        user_email = options[:user_email].presence
        user       = user_email && User.find_by_email(user_email)

        if user && Devise.secure_compare(user.authentication_token, token)
          sign_in user, store: false
        end
      end
  end

  def user_time_zone(&block)
    Time.use_zone(current_user.time_zone, &block)
  end

  # If this is a get request for HTML, just render the ember app.
  def handle_html
    render 'public/index.html' if request.method == 'GET' && request.headers['Accept'].match(/html/)
  end
end

You're only including the token in the request

curl -L 'http://localhost:3000/users/me.json?token=at6aig30jzulnnyomrm4c5'

but your server also expects the email to be passed as well:

authenticate_with_http_token do |token, options|
  user_email = options[:user_email].presence
  user       = user_email && User.find_by_email(user_email)
  …

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM