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