currently I am using the gem delayed-web in my project. I have multiple user roles, and I don't wanna the users whose roles are sales can access to the page delayed web background interface. I already have a method to check for the authentication in my application controller. However, I don't know how to make it work in the route files. Any suggestion would be appreciated.
Updated: I am not using Devise gem. I roll my own authentication.
application_controller.rb:
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: :exception
before_action :authenticate
before_action :check_pricer_role, except: [:export_for_customer]
helper_method :check_pricer_role
def check_pricer_role
unless current_user && (current_user.pricer? || current_user.admin?)
redirect_to errors_not_found_path
end
end
end
routes.rb:
Rails.application.routes.draw do
# How to apply the defined authentication here?
mount Delayed::Web::Engine, at: '/jobs'
end
Ok, I've solved the problem. It turns out that I can find the current user based on his authentication token which is saved inside of the Request object(again, I don't use any gems for authentication, I roll my own one, but I don't think that is the problem here anyway).
This is the complete solution, in case somebody way run into the same difficulty:
class AuthConstraint
def matches?(request)
current_user ||= User.find_by_auth_token(request.cookie_jar['auth_token']) if request.cookie_jar['auth_token']
current_user.present? && !current_user.sales?
end
end
Rails.application.routes.draw do
mount Delayed::Web::Engine, at: '/jobs', :constraints => AuthConstraint.new
//Other resources ........
end
Related
I'm new to Ruby on rails. I need to maintain a project which is a complete web app. Now I need to introduce APIs in it. I've searched and got many tutorials on API and web app separately. But didn't get any of them showing how these things will work together. I'm confused how that authentication will work for both.
Here is the application_controller.rb:
class ApplicationController < ActionController::Base
helper_method :sort_column, :sort_direction
protect_from_forgery
before_filter :authenticate_user!
# before_filter :authenticate # HTTP AUTH DISABLED
rescue_from CanCan::AccessDenied do |exception|
render :file => "#{Rails.root}/public/403.html", :status => 403, :layout => false
## to avoid deprecation warnings with Rails 3.2.x (and incidentally using Ruby 1.9.3 hash syntax)
## this render call should be:
# render file: "#{Rails.root}/public/403", formats: [:html], status: 403, layout: false
end
def user_for_paper_trail
if user_signed_in?
current_user.full_name
end
end
def info_for_paper_trail
if user_signed_in?
{ :user_id => current_user.id }
end
end
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "admin" && password == "123"
end
end
end
I need to know how to authenticate APIs? If I use JWT then how to override sign_in methods and do all that stuff separately for APIs and that also look overhead to me because authentication is already there.
Moreover it would be helpful if I get to know how to involve API functions in controller? Like I've user controller and all the methods for that for web app. Now I need the same methods for API. So I need to make new controller for API or that controller can be used?
There are many questions here so I'll try to give a big picture answer:
In general, other controllers inherit from ApplicationController which (in your case) runs a before_filter. The filter can redirect or render and therefore prevent the execution of the specific route. Since all controllers inherit from ApplicationController, the filter is run before every action of your app (assuming the most common default case).
Presumably, API authentication is supposed to work in a different way than for the app's html frontend (perhaps an api key in a header). It looks like your app is using https://github.com/plataformatec/devise. I'd have a look at it to see if you can just "switch on" a suitable authentication method for your API with it.
I hope this helps.
The solution worked for me is to use friendly token with devise_token_auth for api. And here is my before filter now:
before_filter do
if check_api_request
authenticate_api_request
else
authenticate_user!
end
end
I am using ng-auth-token and devise_token_auth for authentication which is working fine. I am able to login from front end but when i visit an API url directly in browser it doesnt show any current_user. What i want to do is i want to integrate paypal checkout, so when i come back from paypal to my app after user authorization, current_user is nil and also session variable is empty (even if i set some session variable before going to paypal site).
If i add
before_action :authenticate_user!
it gives me
Filter chain halted as :authenticate_user! rendered or redirected
even if i am logged in.
I don't know how can i handle these callback response from other apps.
I found a workaround to this, but still waiting for a proper solution.
# In ApplicationController
def authenticate_current_user
head :unauthorized if get_current_user.nil?
end
def get_current_user
return nil unless cookies[:auth_headers]
auth_headers = JSON.parse cookies[:auth_headers]
expiration_datetime = DateTime.strptime(auth_headers["expiry"], "%s")
current_user = User.find_by(uid: auth_headers["uid"])
if current_user &&
current_user.tokens.has_key?(auth_headers["client"]) &&
expiration_datetime > DateTime.now
#current_user = current_user
end
#current_user
end
and use this in controllers
# In any controllers
before_action :authenticate_current_user
source: https://github.com/lynndylanhurley/devise_token_auth/issues/74
Thanks.
Addon to Ankit's solution (rep too low to comment):
This was failing for me on post requests because Rails was stripping out the cookies due to protect_from_forgery being set:
class ApplicationController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
include Pundit
protect_from_forgery with: :null_session # <-- this was the culprit
end
Removing protect_from_forgery entirely "solved" the issue, though I'm not happy with it.
The real issue (on my end, at least) is that ng-token-auth is supposed to be including the token in the header, but is only found in the cookies. My current guess is that either 1) ng-token-auth isn't properly setting its HttpInterceptor, or 2) some other interceptor is messing with it after the fact. (I've seen the ng-file-upload can cause issues, but I'm not using that...)
I have ended up with this code in ApplicationController:
before_action :merge_auth_headers
def merge_auth_headers
if auth_headers = cookies[:auth_headers]
request.headers.merge!(JSON.parse(auth_headers))
end
end
I am trying to get devise/omniauth to properly redirect a user after registering (and later I will be trying with signing in). I am using the omniauth Google gem and basically I want the user once registered to be sent to a plans page. I have read several things here and on other sites and tried every suggestion I can find but having no luck. My applications controller is as follows:
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: :exception
private
def stored_location_for(resource)
nil
end
def after_sign_in_path_for(resource)
request.env['omniauth.origin'] || stored_location_for(resource) || root_path
end
def after_sign_up_path_for(resource)
'register#plans' # Or :prefix_to_your_route
end
def after_inactive_sign_up_path_for(resource)
'register#plans' # Or :prefix_to_your_route
end
end
I am not requiring activation but I have tested with inactive sign up as well as sign up just in case. I am not using a custom registration controller or anything, my routes for devise are as follows:
devise_for :users, :controllers => { :omniauth_callbacks => 'users/omniauth_callbacks' }
I also have my plans route registered as such:
get 'register/plans'
I have tested this with rake routes and it is confirmed as:
register_plans GET /register/plans(.:format) register#plans
I am at a loss of what else to try. I am using rails 4.2.4 and Ruby 2.2.1 running on Ubuntu.
Any help would be greatly appreciated.
Many thanks
David
I am using the devise gem for authentication and have the following before_filter in my ApplicationController:
before_filter :require_login
def require_login
unless user_signed_in? || params[:controller] == 'devise/sessions'
flash[:error] = "You must be logged in to access that page."
redirect_to new_user_session_path
end
end
I have recently implemented the ActiveAdmin gem and am trying to get skip_before_filter to work for ActiveAdmin, so that I can access ActiveAdmin. I have attempted the methods outlined in this post, adding the following to config/initializers/active_admin.rb:
config.skip_before_filter :require_login
and also adding the following to one of my admin model files, listing.rb:
ActiveAdmin.register Listing do
controller do
skip_before_filter :require_login
end
end
but it doesn't seem to work, even after restarting the server and browser.
What am I doing wrong?
So two things:
First: I think you're using Devise inefficiently. Instead of a custom login method on application_controller, you should:
class ApplicationController
before_filter :authenticate_user!
end
That's it -- everything else works.
Second: Active Admin uses a different user class -- the "admin_user" (rather than "user"). By default, you'll use the following credentials:
email: admin#example.com
password: password
I'm using Devise in a Rails application I'm writing, and I want to let users go back to where they were after signing in or signing up.
For example, if I have a "comments" Controller that is protected by:
before_filter :authenticate_user!
Then I want users who click a "Comment Now!" button (and are therefore redirected to the new action in CommentsController) to log in and then have Devise redirect them to the new action (or wherever they were) in CommentsController, not to the generic root of the application, or to a generic after_sign_in_path.
Looking through the RDOC for Devise, I found this method that makes it look as if Devise has at least the capability to do something like this on its own, but I can't figure out a way.
OK, so I've done some more experimentation, and working with Kormie's info, I've got a working solution.
From what I can determine, before_filter authenticate_user! does not save the route for returning the user. What I did was this:
First, I added an extra before_filter at the top of my controller
before_filter :store_location
before_filter :authenticate_user!
Then, I wrote the store_location method at the bottom of the controller
private
def store_location
session[:user_return_to] = any_old_route_path
end
I don't claim this is perfect, but it works for me. (The downside for anyone else wanting to use it, is that it only supports one return path per controller. This is all I need for myself, but it is only a slight improvement over the one return path per app that I was using previously.) I would really appreciate anyone else's insights and suggestions.
Devise should do this by itself. The authenticate_user! filter also did not want to work for me when the route to the action was set via PUT method. When I have changed this to GET in routes.rb devise started to work as expected.
The simple way to do this:
# Modified from https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
stored_location_for(resource) || your_defaut_path
end
end
I think by default Devise saves the route but you may be usinging
sign_in #user
this should redirect you
sign_in_and_redirect(#user) #assuming you are sigining in that resource
Have you tried after_sign_in_path_for? If you define that method in your ApplicationController it should override the default implementation on a per controller basis.
Adapted from Devise Wiki how to:
Redirect back to current page after sign in, sign out, sign up, update
Redirecting back to the "current page" involves saving the current url in the session and then retrieving the url from the session after the user is authenticated / signed out. This should only really be done for GET requests as the other http methods (POST, PUT, PATCH, DELETE) are not idempotent and should not be repeated automatically.
To store the location for your whole application use before_action to set a callback (Use before_filter in Rails versions before 4.0).
This example assumes that you have setup devise to authenticate a class named User.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :store_user_location!, if: :storable_location?
# The callback which stores the current location must be added
# before you authenticate the user as `authenticate_user!` (or
# whatever your resource is) will halt the filter chain
# and redirect before the location can be stored.
before_action :authenticate_user!
# To redirect to the stored location after the user signs
# signs in you would override the after_sign_in_path_for method:
def after_sign_in_path_for(resource_or_scope)
# *My note, not wiki*: you may need a fall back as
# stored_location_for can return nil. I've added root_path
stored_location_for(resource_or_scope) || root_path
end
private
# Its important that the location is NOT stored if:
# - The request method is not GET (non idempotent)
# - The request is handled by a Devise controller
# such as Devise::SessionsController as that could
# cause an infinite redirect loop.
# - The request is an Ajax request as this can lead
# to very unexpected behaviour.
def storable_location?
request.get? && is_navigational_format? &&
!devise_controller? && !request.xhr?
end
def store_user_location!
# :user is the scope we are authenticating
store_location_for(:user, request.fullpath)
end
end
Reference
Devise How To: Redirect back to current page after sign in, sign out, sign up, update