I have a rails app hosted on Heroku that am restricting access to by using a proxy service. The external server acts as intermediary for all requests and handles user authentication. Once a user has authenticated, the server (I think LDAP) adds the user name to the request header and redirects them to my app.
I would like to use the username from the request header to authenticate users in my app. Basically if the user doesn't exist I would create a user with that username (no password required) and if not I would just log them in. I will be storing the users in my app's database.
How should I do this? Is it possible to use Devise for this purpose?
Edit: I got it working with Devise/custom Warden strategy like this:
# config/initializers/my_strategy.rb
Warden::Strategies.add(:my_strategy) do
def valid?
true
end
def authenticate!
if !request.headers["my_key"]
fail!("You are not authorized to view this site.")
redirect!("proxy_url")
else
username = request.headers["my_key"]
user = User.find_by_username(username)
if user.nil?
user = User.create(:username => username)
end
success!(user)
end
end
end
#config/initializers/devise.rb
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :my_strategy
end
I need to make this as bullet proof as possible. Are there other security measures can I take to make sure someone can't spoof the request header and access my site?
I think using devise can be a little more overkill, but you can. You just need define a warden strategie. in devise or use only warden in this purpose.
Related
I am using Devise to authenticate users to my Ruby on Rails application. Up to this point, I have been using the standard Cookie-based session to authenticate users, but now I have requirements to allow a token-based authentication, and I implemented this through a custom Warden strategy.
For the sake of this example, my custom strategy code is:
module Devise
module Strategies
class CustomAuthenticatable < Base
def valid?
params.has_key? :email
end
def authenticate!
success!(User.find_by(email: params[:email]))
#fail
end
end
end
end
So this works as expected for the first request: when I GET /api/my_controller/url?email=user#example.com the user is authenticated, and I get the expected response.
But wait: when I then make a second request: GET /api/my_controller/url, the user is still authenticated.
Upon further inspection, I see that a Set-Cookie is being sent, with a Devise session.
So here's my question:
How do I disable the Set-Cookie when using a custom strategy?
You can prevent the creation of a session, like described in here.
Prevent session creation on rails 3.2.2 for RESTful api
resource = warden.authenticate!(:scope => resource_name, :store => !(request.format.xml? || request.format.json?))
For some other options, please consider Rails 3 disabling session cookies.
I have a main app, this app has Users and Admins (different model and table)using devise.
The main app use doorkeeper to allow users to authenticate on another service. The client app use Omniauth to consume OAuth2.
I am now reaching a point where the client app need a backend and using the admin users from the main app make sense.
Here is my current strategy :
module OmniAuth
module Strategies
class ResaNetwork < OmniAuth::Strategies::OAuth2
option :name, :resa_network
option :client_options, {
#site: "http://dylog.ubuntu-sylario.net:3000",
site: ENV["CLIENT_OPTION_SITE"],
authorize_path: "/oauth/authorize"
}
uid do
raw_info["resource_owner_id"]
end
def raw_info
#raw_info ||= access_token.get('/api/me').parsed
end
end
end
end
For doorkeeper I will will use warden scope (I know it has nothing to do with oauth scope) user or admin depending on the oauth2 Scope in resource_owner_authenticator.
Is it possible to have a specific scope entered has a param with the signin path of omniauth? How?
Is my use of scope correct or is it a really ugly hack? For info, admin will access users data while users will only see their data in the API.
with omniauth in my app, to have a user use Google oAuth2 to authenticate I redirect the user to:
/users/auth/google_oauth2
If the users approves the request, then the AuthenticationsController#create is called.
With AuthenticationsController#create - I can add event tracking to record the # of users who approve google auth. What I don't have is the number that I sent to approve meaning I don't have a conversion rate.
How can I track the # of people who hit the URL around making requests to connect.
A nasty solution would be to build a filter around the method Strategy#request_call and do the tracking there.
Inside an initializer:
OmniAuth::Strategy.class_eval do
def request_call_with_tracking
log :info, "Im running before the actual request_call"
Tracker.hit(name) #name will return the provider
request_call_without_tracking
end
alias_method_chain :request_call, :tracking
end
You can achieve this by using the OmniAuth setup phase. You can pass a :setup option to an OmniAuth provider, with a proc which will be executed before the authentication is performed. You can add event tracking inside this proc.
So if you have some tracker class, you can do this:
use OmniAuth::Builder do
provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'],
:setup => lambda { |env|
Tracker.track
}
end
For more information check out Avdi Grimm's great blog post about the subject.
I am using authlogic and omniauth for authentication in my Rails 3.0.10 app.
However when I get the callback from the omniauth provider, I am not able to create a new user session so as to sign in the user.
In other responses (and even in the authlogic docs), it says that using UserSession.create(#user, true) should be able to create and persist a new session.
However, this does not work for me. It only works if the #user has a password in the database (by inserting a password directly in the db).
But there is no password when using third-party authentication providers, hence I cannot sign in users.
Any ideas how to sign in a user without a password in authlogic?
Thanks.
You can do something like this in your User model:
acts_as_authentic do |config|
external = Proc.new { |r| r.externally_authenticated? }
config.merge_validates_confirmation_of_password_field_options(:unless => external)
config.merge_validates_length_of_password_confirmation_field_options(:unless => external)
config.merge_validates_length_of_password_field_options(:unless => external)
end
externally_authenticated? is just a method on the user that checks what is providing the user information, and if it's one of the omniauth providers, returns true.
I want to use "authenticate_ with_ http_ basic" but I just can not get it
working.
In my RoR app Authlogic is working fine and I'm using User Sessions for that. While keeping that method as it is now i need to use authenticate_with_http_basic.I have a iPhone SDK app and now I need to fetch some products from my webapp and display as list. So I'm assuming that i need to send the request to my webapp like this; http://username:password#192.168.1.9/products/
So my question is to validate this username and password and what I need to do to my UserSession Controller?
You don't need to do anything with UserSessionController, since that controller would only handle login form submit and logout.
Authlogic and authenticate_with_http_basic is irrelevant to each other. If you want to authenticate via HTTP basic, you just need to create a method to authenticate using method provided by Rails, and put that method on the before_filter. By logging in via HTTP authentication, I assume that the username and password should be mandatory for every request.
So finally, your ProductsController would be something like this
class ProductsController < ApplicationController
before_filter :authenticate_via_http_basic
# In case you have some method to redirect user to login page, skip it
skip_before_filter :require_authentication
...
protected
def authenticate_via_http_basic
unless current_user
authenticate_with_http_basic do |username, password|
if user = User.find_by_username(username)
user.valid_password?(password)
else
false
end
end
end
end
Authentication via HTTP Auth is now integrated into AuthLogic, and it is enabled by default.