I am trying to make an API for a project. I followed instructions from this video - https://www.youtube.com/watch?v=lgdUqtw4weg&t=165s
Basically in the video, I make a token column for the user model. And set up the controller so I can use a post method.
But when i run the POST.
I get the error saying
{"error":"You need to sign in or sign up before continuing."}
I think Devise is interfering with the POST and sees that the user has not logged in when trying to visit non public pages.
How do I get past this and get the token ?
Here is my api controller.
class Api::V1::UserSerializedController < ApplicationController
protect_from_forgery except: :index
respond_to :json
def create
user = User.where(email: params[:email]).first
if user.valid_password?(params[:encrypted_password])
render json: user.as_json(only: [:email, :authentication_token]),status: :created
else
head(:unauthorized)
end
end
def show
end
end
You are inheriting ApplicationController and i'm guessing you have authenticate_user! before action set there.
You'd have to either change the parent class to something like ApiController (i'd prefer this)
OR
skip this action for your Api::V1::UserSerializedController
Related
I have a user model with two roles as enums
enum role: [:'Standard', :'Admin']
I am trying to redirect based on the user role to relevant page after sign-in with Devise, I have used the recommended way of doing it on the docs.
In my sesssions controller...
def create
super
sign_out :user
end
def after_sign_in_path_for(_resource)
if resource.role == "Standard"
redirect_to dashboards_path
else
redirect_to dashboards_admin_index_path
end
end
And in my controller...
before_action :authenticate_salesperson!
before_action :set_project, only: %i[show edit update destroy]
I get this error saying too many renders/redirects (highlights super in create method) when logging in and i'm wondering why?
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return"
How to work around this? Ty.
Would be nice if you paste the whole controllers, but it seems like the after_sign_in_path_for method gets called before each time you visit either page, which creates a loop and hence the error. You can easily verify this by logging some text in each of the if else methods to double check.
What you should do is to add this logic to the controller, which is the root path like so
class DashboardController < ApplicationController
before_action: :after_sign_in_path_for, only: :index
private
def after_sign_in_path_for
if current_user.standard?
redirect_to dashboards_path
else
redirect_to dashboards_admin_index_path
end
end
end
Thanks. This works after I removed the "redirect_to's" in the "after_sign_in" method in my sessions controller
def after_sign_in_path_for(_resource)
if current_salesperson.standard?
dashboards_path
elsif current_salesperson.admin?
dashboards_admin_index_path
end
end
And in my user model...
def admin?
role == "Admin"
end
def standard?
role == "Standard"
end
You are redirecting too many times in the same action, this is why the message: "Render and/or redirect were called multiple times in this action."
Just return the path, delete redirect sentence! You only can redirect once in every action method!
Greetings
I have an API that needs to return results based on whether there is a signed in user or not. I'm using devise_token_auth on the rails side, and ng_token_auth on the client side (Angular).
In my controller, I would like to do something like the following:
Try to authenticate_user, but don't fail the filter chain if authentication failed.
Use current_user when creating the response payload (current_user will either contain nil or the authenticated user).
Take this code for example:
class Api::MyController < Api::ApiController
before_filter :authenticate_user!
def index
if current_user
# Create the result json using current_user
else
# Create the result json without user data
end
render json: result
end
end
Only authenticated users will pass the authenticate_user! call and get to the index method. I'd like all users to get there, both authenticated and non-authenticated.
You could try the following (not tested because I currently don't have Rails installed).
skip_before_filter :authenticate_user!, only: [:index_auth_failed]
before_filter do
authenticate_user! rescue redirect_to index_auth_failed
end
def index
# current_user exists
end
def index_auth_failed
# current_user does not exist
end
So often I have a form in some webpage that the user submits to a POST, PUT or DELETE action in Rails where I want it to redirect to a specified URL if the submission was a success. I typically make a hidden extra parameter called to with a path like /users. So if the form submission failed, it just stays on that form, but if it succeeds then the browser is redirected to /users.
I'd like to automatically look for this parameter and always redirect to it if a form submission succeeded in any controller/action. Do I put this in the ApplicationController within an after_action?
class ApplicationController < ActionController::Base
after_action :redirect_if_success
private
def redirect_if_success
redirect_to params[:to] if params[:to]
end
end
I guess I can check the request object if this was a POST, PUT or DELETE action. How do I know the submission was a success? Will a redirect_to in the after_action override any redirect_tos in the form controller?
I think the solution is define private method redirect_if_success in application controller but call it directly in the action. eg:
class ApplicationController < ActionController::Base
private
def redirect_if_success(default_ur)
redirect_to params[:to] || default_url
# or similar logic
end
end
class UserController < ApplicationController::Base
def create
redirect_if_success("/users") if #user.save
end
end
I would create a helper method
def redirect_to_location
redirect_to params[:to] && params[:to].present?
end
and I would use it explicitly in each action I want this behaviour.
However, you could experiment a bit. To keep this logic in after_action you would need to setup some state that would let you know whether you need to redirect or not.
You could do :
def save
if #user.save
#follow_redirect = true
end
end
and check #follow_redirect flag in after_action filter. Does not look like a very pretty solution but it would work.
You could also try to inspect response variable to see if you have already redirected or rendered an action: (not sure if it would work but it's fun to experiment)
So you could check:
if you need to redirect (action is post/put/delete) and params[:to] is present and
if you have not already redirected/redirected
# this is not a copy-paste code but rather to demonstrate an idea
class ApplicationController < ActionController::Base
after_action :redirect_to_location
protected
def is_redirectable?
%w{post put delete}.include?(request.method) && params[:to].present?
end
def already_redirected?
!response.status.nil? # not sure if it would work at all
end
def redirect_to_location
redirect_to params[:to] if is_redirectable? && !already_redirected?
end
end
I am trying to require an authorization in my controller called purcahses_controller.rb It's just an order form model with Show Edit Destroy.. I can view it in active_admin, the url being localhost/admin/purchases. But the problem is I can also view it at localhost/purchases. It lists all of the orders and allows all functions. Id like to route_to rooturl with a simple unauthorized message if the user isn't logged in.
You can do the following and it will give you option to redirect to any path you see fit.
In the file
config/initializers/active_admin.rb
Make the following change
config.authentication_method = :authenticate_active_admin_user!
Then inside your application controller adding something like this:
class ApplicationController < ActionController::Base
protect_from_forgery
def authenticate_active_admin_user!
authenticate_user!
unless current_user.superadmin?
flash[:alert] = "Unauthorized Access!"
redirect_to "/admin/login"
end
end
end
In your purchases_controller add the following before_filter line:
before_filter :authenticate_active_admin_user!
Hopefully this helps!
~Kevin
authenticate_active_admin_user gives you access to the admin authenticate user which will lead you to the authorization regardless of what you call in admin.
controller do
skip_before_action :authenticate_active_admin_user, only: :action
end
https://www.rubydoc.info/gems/nsm-activeadmin/0.2.2/ActiveAdmin%2FResourceController%3Aauthenticate_active_admin_user
https://github.com/activeadmin/activeadmin/blob/master/lib/active_admin/base_controller.rb#L38
I have devise token_auth working fine in my rails 3.2 application. I'm just building an API and need to override the devise authentication mechanism with another token. Why? Because one user has many locations and I want to provide independent access via this token to each location without compromising the whole account.
When a location is created, a location api_token is automatically created.
In my locations controller that I'm trying to access with the new key, I have tried this:
class Api::V1::LocationsController < ApplicationController
before_filter :restrict_access, :only => :index
def index
#locations = Location.all
#page_title = "Locations"
#page_title_content = "A list of all your locations. Click to customise and view reports"
respond_to do |format|
format.json { render json: #locations }
format.xml { render xml: #locations }
end
end
private
def restrict_access
api_key = Location.find_by_api_token(params[:access_token])
head :unauthorized unless api_key
end
end
Everything routes fine however, even when I'm not logged in and don't pass the key in the url, I am permitted to see all locations.
Any suggestions how I can get this working? Also, how can I restrict the locations seen to those with access? Usually I use cancan but can't see how this might work.
With devise you need to use :token_authenticatable and override find_for_token_authentication. Here's what works in my app.
class User
devise :token_authenticatable
self.token_authentication_key = "access_token"
def self.find_for_token_authentication conditions
grant = AccessGrant.where(access_token: conditions[token_authentication_key]).
any_of({access_token_expires_at: nil}, {:access_token_expires_at.gt => Time.now}).
first
if grant
user = User.where(_id: grant.user_id).first
user.current_grant = grant if user
user
end
end
end
And after that you just call the standard authenticate_user! in a before filter
class Api::BaseController < ApplicationController
respond_to :json
# don't protect from forgery here
skip_before_filter :verify_authenticity_token
before_filter :authenticate_user!