So far I have:
authenticate_or_request_with_http_digest do |username,password|
end
but I dont know how to pass these values to devise to return / signin a user.
the following works for me.
Hope it helps someone else::::
before_filter :check_auth
def check_auth
authenticate_or_request_with_http_basic do |username,password|
resource = User.find_by_email(username)
if resource.valid_password?(password)
sign_in :user, resource
end
end
end
Related
I am implementing api via rails.
I want to implement following feature but unable to figure out how?
I tried this sample app
I have user model with email, password and access_token
class UsersController < ApplicationController
def signin
c_user = User.find_by_email(params[:email])
pass = params[:password]
if c_user.password == pass
render json: c_user.access_token
end
end
private
def users_params
params.require(:user).permit(:email, :password)
end
end
If user request via api for http://localhost:3000/signin?email=t1#t.com&password=password
then it will check email and password and return access_token that user can use for future request.
I want to implement same with devise or any other gem please help me to understand it.
Thanks in advance
This is how I emplement such mechanism in my apps:
Generate an access_token whenever a user is created.
Respond with that access_token whenever the user signs in.
Require an access_token authentication for every request needed.
user.rb
class User < ActiveRecord::Base
# Use this before callback to set up User access_token.
before_save :ensure_authentication_token
# If the user has no access_token, generate one.
def ensure_authentication_token
if access_token.blank?
self.access_token = generate_access_token
end
end
private
def generate_access_token
loop do
token = Devise.friendly_token
break token unless User.where(access_token: token).first
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
private
# To make authentication mechanism more safe,
# require an access_token and a user_email.
def authenticate_user_from_token!
user_email = params[:user_email].presence
user = user_email && User.find_by_email(user_email)
# Use Devise.secure_compare to compare the access_token
# in the database with the access_token given in the params.
if user && Devise.secure_compare(user.access_token, params[:access_token])
# Passing store false, will not store the user in the session,
# so an access_token is needed for every request.
# If you want the access_token to work as a sign in token,
# you can simply remove store: false.
sign_in user, store: false
end
end
end
Then you can use this before_filter in any controller you want to protect with access_token authentication:
before_filter :authenticate_user_from_token!
You also needs to override Devise sessions controller, so it responds with a JSON holding the access_token.
users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
# Disable CSRF protection
skip_before_action :verify_authenticity_token
# Be sure to enable JSON.
respond_to :html, :json
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource) do |format|
format.json { render json: {user_email: resource.email, access_token: resource.access_token} }
end
end
end
And make sure to specify it in your routes:
routes.rb
devise_for :users, controllers: { sessions: 'users/sessions' }
I think the reason you can't do that is because there is no password field as you were expected. There is only encrypted_password in the table.
And you must not save any user's password explicitly in a field like password for security reasons.
The way you can make your api work is by using valid_password? method provided by devise to authenticate the user.
if c_user.valid_password?(params[:password])
... ...
end
Since Doorkeeper is an isolated Engine we have no access to whatever you did in ApplicationController. But what if you need a current_user? What could be a workaround here?
The first idea is to monkey-patch ActionController::Base. Any better thoughts?
My doorkeeper implementation was inside of the base app so this wont help if you are using a separate engine but will if you use the same rails app so I will share here:
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
if doorkeeper_token
return current_resource_owner
end
# fallback to auth with warden if no doorkeeper token
warden.authenticate(:scope => :user)
end
# Needed for doorkeeper find the user that owns the access token
def current_resource_owner
User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
end
end
Unless there are no answers, may be my own dirty monkey patch will be useful to someone.
in initializers/action_controller_patch.rb:
module ActionController
Base.class_eval do
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
end
I think you can find it on this page.
https://github.com/applicake/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow
I am using credential auth pattern, so in my case this works.
Doorkeeper.configure do
resource_owner_from_credentials do |routes|
request.params[:user] = {:email => request.params[:username], :password => request.params[:password]}
request.env["devise.allow_params_authentication"] = true
request.env["warden"].authenticate!(:scope => :user)
end
end
I have the following configuration:
devise :database_authenticatable
config.http_authenticatable = true
on request:
http://user:password#localhost:3000/
Devise ignores the http auth login and redirects to login page
any thoughts?
Regards
What http_authenticatable give you is the ability to use your HTTP Basic Authentication credentials to log into its own authentication system. You still need to code the http_auth block by yourself, like this:
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "foo" && password == "bar"
end
warden.custom_failure! if performed?
end
This code should go into your application controller. Make sure you are using warden.custom_failure!, otherwise the devise will enter an infinite loop of redirects.
This worked for me
before_filter :check_auth
def check_auth
authenticate_or_request_with_http_basic do |username,password|
resource = User.find(username)
if resource.valid_password?(password)
sign_in :user, resource
end
end
warden.custom_failure! if performed?
end
I am building an application in Rails 2.3.14 using Ruby 1.8.7.
My client has requested a very simple authentication on a webinars page.
I thought using http_auth would be very fitting, as it just needs a very basic username and password.
Now, she has requested that if they hit cancel or use the wrong information, they get redirected to a page that basically says "if you forget login information, contact us."
How do I make it so that when we get the "HTTP Basic: Access denied." error, I can instead redirect to a page? Or, instead, just customize this page with our custom styles/content?
Thanks in advance.
Here is the code from my webinars controller:
class WebinarsController < ApplicationController
before_filter :authenticate, :only => [:bpr]
def bpr
render :action => :bpr
end
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
username == "abc" && password == "123"
end
end
end
If you look at the authenticate_or_request_with_http_basic source, you'll see this:
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
end
def authenticate_with_http_basic(&login_procedure)
HttpAuthentication::Basic.authenticate(request, &login_procedure)
end
#...
def authenticate(request, &login_procedure)
unless request.authorization.blank?
login_procedure.call(*user_name_and_password(request))
end
end
So your login_procedure block can do pretty much anything it wants as long as it returns true for a successful login. In particular, it can call redirect_to:
def authenticate
authenticate_or_request_with_http_basic do |username, password|
if(username == "abc" && password == "123")
true
else
redirect_to '/somewhere/else/with/instructions'
end
end
end
I am using Devise for rails 3 application. For page caching, I need to set cookies for log in/out info.
What's the simplest way to set cookies when log in/out occurrs with Devise? I read 'how to customize controller' part but it seems to be a lot of work.
Since Devise is based on Warden, another solution is to use Warden's callbacks, e.g in your devise.rb:
Warden::Manager.after_set_user do |user,auth,opts|
auth.cookies[:signed_in] = 1
end
Warden::Manager.before_logout do |user,auth,opts|
auth.cookies.delete :signed_in
end
It actually wouldn't be too hard to extend the devise SessionsController to add cookies on log in and log out, you could create a controller similar to this:
# app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
# POST /resource/sign_in
def create
cookies[:sign_in] = "Sign in info you want to store"
super
end
# GET /resource/sign_out
def destroy
cookies[:sign_out] = "Sign out info you want to store"
super
end
end
Then you would have to add the following to your routes.rb:
devise_for :users, :controllers => { :sessions => "sessions" }
That should get you most of the way there.
Adapted from #karl-rosaen answer this solution create a new initializer or add to the end of your devise.rb initializer.
This will add the cookie to remember the email if remember me options is set, if not it will delete the cookie
Warden::Manager.after_authentication do |user, auth, opts|
if user.remember_me
auth.cookies[:email] = {value: user.email, expires: 2.weeks.from_now}
else
auth.cookies.delete :email
end
end