Is there any chance to access authenticate Doorkeeper method from a rails action controller? I would like to skip authentication just for one of my actions('show') but if a specific condition aapplies I want to call the apiauthenticate method to do its job. So in the action 'show', first of all I check a condition, and if does not apply, then I need to activate the api_authenticate. I'm launching a test that should call api_authenticate and stop it there. But for some reason it keeps going on and it does not stop.
This is the code of my controller
skip_before_action :api_authenticate, only: :show
def show
param! :id, String, required: true
post = Services::Posts.find(params[:id])
if post.public
#post = post
#user_id = nil
else
api_authenticate
ap "it shouldnt get here if user is not logged in"
user = current_resource_owner
#post = Services::Posts.show(params[:id], user)
#user_id = user.identity rescue nil
end
end
#more actions....
And this is the api_controller.rb where I have the authenticate method
class ApiController < ApplicationController
protect_from_forgery with: :null_session
# Check the user is authenticated
before_action :api_authenticate
rescue_from ActionController::RoutingError, :with => :route_error
rescue_from ::AbstractController::ActionNotFound, :with => :action_error
rescue_from Exception, :with => :base_error if Rails.env.production?
def api_authenticate
doorkeeper_authorize!()
end
end
I have implemented something similar. Haven't tested the below code, but it should work.
skip_before_filter :doorkeeper_authorize! , only: :show
def show
param! :id, String, required: true
post = Services::Posts.find(params[:id])
if post.public
#post = post
#user_id = nil
else
doorkeeper_authorize!
ap "it shouldnt get here if user is not logged in"
user = current_resource_owner
#post = Services::Posts.show(params[:id], user)
#user_id = user.identity rescue nil
end
end
The Api controller,
class ApiController < ApplicationController
protect_from_forgery with: :null_session
# Check the user is authenticated
before_action :doorkeeper_authorize!
rescue_from ActionController::RoutingError, :with => :route_error
rescue_from ::AbstractController::ActionNotFound, :with => :action_error
rescue_from Exception, :with => :base_error if Rails.env.production?
def doorkeeper_unauthorized_render_options(error: nil)
response_hash = { status: false, description: error.description, expired: false }
response_hash[:expired] = true if error.description == "The access token expired"
{ json: response_hash }
end
end
If the problem persists, please add the params passed to the show action.
Related
I recently created an errors_controller.rb file which basically will route all non-existing page even those unathorized page by non-admin users to a 404 page:
class ErrorsController < ApplicationController
def page_not_found
show_404
end
end
And then on my routes.rb file:
match '*path', via: :all, to: 'errors#page_not_found'
I am using Pundit gem for my authorization and here's my users policy:
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.all
end
end
def index?
user.admin?
end
end
Now the problem is whenever I visits a page that is non existing it shows me this:
But if a non-admin user visit a page that is unathorized to access (for instance localhost:3000/users-list), it's showing this:
How do I make sure to show the 404 page or a specific page on my public folder whenever an unathorized user visits a page that is can only access by an admin?
UPDATE: Here's the content of my application_controller.rb file:
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
rescue_from ActiveRecord::RecordNotFound, with: :show_404
def after_sign_in_path_for(resource)
stored_location_for(resource) || contacts_path
end
def after_sign_up_path_for(resource)
after_sign_in_path_for(resource)
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:user_avatar, :name, :email, :password, :password_confirmation)}
devise_parameter_sanitizer.permit(:account_update) { |u| u.permit(:user_avatar, :name, :email, :password, :current_password)}
end
private
def user_not_authorized
flash[:danger] = "You are not authorized to perform this action."
redirect_to action: :index
end
def show_404
render template: "errors/404", status: 404
end
end
You should rewrite user_not_authorized on controller, that you call when a non-admin user visit a page that is unathorized to access
def user_not_authorized
show_404
end
I have this code in my ApplicationController
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def authenticate!
# unless current_user
if current_user
current_user
else
render json: { 'error' => {'message' => 'Invalid access token', 'code' => 301 } }
return
end
end
def current_user
return #current_user if #current_user.present?
user = User.find_by(access_token: params.delete(:token))
if user.present?
#current_user = user
else
false
end
end
end
and I authenticate a user with
class Api::V1::RegisterController < ApplicationController
layout nil
skip_before_action :verify_authenticity_token
def get_user
authenticate!
render json: {'hello' => 'hi'}
end
end
it throws me an error of Double Render.
how can I render an invalid access token message if user's access token is not present in my database and return user details if present?
EDIT1: I tried the code provided by #andrew21
class ApplicationController < ActionController::Base
class UnauthorizedAccess < StandardError; end
rescue_from UnauthroizedAccess, with: render_invalid_access
protect_from_forgery with: :exception
def authenticate!
raise UnauthorizedAccess, 'invalid access token' unless current_user
end
def render_invalid_access
render json: { 'error' => {'message' => 'Invalid access token', 'code' => 301 } }
end
end
but I get an error.
undefined local variable or method `render_invalid_access' for ApplicationController:Class
why don't you raise an error on invalid access, then rescue the error and render the appropriate response. e.g.:
class ApplicationController < ActionController::Base
class UnauthorizedAccess < StandardError; end
protect_from_forgery with: :exception
rescue_from UnauthorizedAccess, with: :render_invalid_access
def authenticate!
raise UnauthorizedAccess, 'invalid access token' unless current_user
end
def render_invalid_access
render json: { 'error' => {'message' => 'Invalid access token', 'code' => 301 } }
end
end
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user!, :except => [:index]
before_action :authenticate, :except => [:index]
def authenticate
authenticate_or_request_with_http_basic do |username, password|
basic_auth=User.find_by_email(username)
if basic_auth.valid_password?(password)
sign_in :user, basic_auth
render :json => {:email => current_user.email, :id => current_user.id, :message => "hai ruby"}
else
render plain: "Unauthorized access"
end
end
end
end
I am trying to implement basic authorization in my sample application.When i give correct credentials values as in the user database. It gives json response.when the username and password goes wrong it throws error as "undefined method `valid_password?' for nil:NilClass"
Just change if condition to this:
if basic_auth && basic_auth.valid_password?(password)
This will work because before checking for valid password, you must check whether a user even exists with given email id or not. Otherwise, basic_auth object will be nil and valid_password method on nil object will throw error.
I have a custom method outside the generic CRUD in my friendships controller called request. My problem is that I have before_filter :require_auth set to run before all methods in my FriendshipsController.
It was working fine except for the request method.
(This makes me think it has something to do with it being out of normal CRUD?)
When I call the request method now it skips the :require_auth and goes straight to the request method which is giving me errors as I define some variables in :require_auth that I need inside the request method.
Here is my FriendshipsController:
class FriendshipsController < ApplicationController
skip_before_filter :verify_authenticity_token, :only => [:create]
before_filter :require_auth
def create
#friendship = Friendship.new(user_id: params[:user_id], friend_id: params[:friend_id], status: params[:status])
if #friendship.save
render :status => 200, :json => {:message => "Friendship Created"}
else
render :status => 500, :json => { :message => "Problem creating friendship" }
end
end
def request
# friendID = params[:friend_id]
# userID = #currentuser.id
binding.pry
#userid = #currentuser.id
#friendid = params[:friend_id]
unless (#userid == #friendid || Friendship.exists?(user_id: #userid,friend_id: #friendid))
create(:user_id => userID, :friend_id => friendID, :status => 'pending')
create(:user_id => friendID, :friend_id => userID, :status => 'requested')
end
end
end
Here is my ApplicationController where I define require_auth:
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
def require_auth
binding.pry
auth_token = request.headers["HTTP_AUTH_TOKEN"]
#user = User.find_by_auth_token(auth_token)
if #user.auth_token
#currentuser = #user
else
render :status => 401, :json => {:error => "Requires authorization"}
return false
end
end
end
Chris Peters was right in the comments. My problem was that rails already has request defined. I simple changed the method name to something else and it works.
Thanks.
Having problems figuring this out.
trying to do a
rescue_from NoMethodError, :with => :try_some_options
But its not working.
EDITED:
For testing I'm doing a simple redirect
def try_some_options
redirect_to root_url
end
EDITED 2:
Sample of my controller. Added (exception) as recommended below.
I know the reason I'm getting the error. Using Authlogic and authlogic_facebook_connect plugin. When user is created from the facebook plugin the "MyCar" model, which is associated with a user is not created like it normally is created if a user registers locally. Since I do call on the user model and reference the users car throughout different parts of the site, I would like to do something like what you see below and eventually put it in my application_controller.
class UsersController < ApplicationController
before_filter :login_required, :except => [:new, :create]
rescue_from NoMethodError, :with => :try_some_options
...
def show
store_target_location
#user = current_user
end
def create
#user = User.new(params[:user])
if #user.save
MyCar.create!(:user => #user)
flash[:notice] = "Successfully created profile."
redirect_to profile_path
else
render :action => 'new'
end
end
...
protected
def try_some_options(exception)
if logged_in? && current_user.my_car.blank?
MyCar.create!(:user => current_user)
redirect_to_target_or_default profile_path
end
end
...
end
EDITED 3: Hacked it for now since I know why the error is showing up, but would like to figure out how to rescue_from NoMethodError
class UsersController < ApplicationController
before_filter :login_required, :except => [:new, :create]
before_filter :add_car_if_missing
def add_car_if_missing
if logged_in? && current_user.my_car.blank?
MyCar.create!(:user => current_user)
end
end
end
I just read your post when trying to come up with a solution to the same problem. In the end I did the following:
class ExampleController < ApplicationController
rescue_from Exception, :with => :render_404
...
private
def render_404(exception = nil)
logger.info "Exception, redirecting: #{exception.message}" if exception
render(:action => :index)
end
end
This worked well for me. It is a catch all situation yet it just may help you. All the best.