OmniAuth and Devise, how to set optional passwords - ruby-on-rails

I am using OmniAuth and Devise to authenticate users. I would like users that have signed up using OmniAuth providers to be able to set an optional password (needed for API authentication) but I'm running into a wall.
If a user creates an account via OmniAuth and tries to set a password they get the following error:
BCrypt::Errors::InvalidHash in RegistrationsController#update
I believe this is because the password is blank. What's a good way around this? I've thought about generating a random password but the problem with that approach is the user needs to know the current password in order to edit settings.
Edit:
I looked at allowing the user to change settings without requiring a current password and that's what I would like to do only if the user didn't have a password initially.

An alternative is to add the following into your 'user' model class to bypass password verification if there is no password to verify, where provider is some field that is set when using external authentication.
def valid_password?(password)
!provider.nil? || super(password)
end

I assume you don't want the easy way out which would be to simply reset the password if they wanted to set it?
user.send_reset_password_instructions

This comes a bit late but it might help someone else, with Andrew's answer you can in create a password and store it in the database, but you can't login using your email and your new password, solved this by setting:
def valid_password
!provider.nil? && !encrypted_password.present? || super
end

Another alternative. You don't have to include a new field. Just catch the exception raised and return false. Here is the code.
def valid_password?(password)
begin
super(password)
rescue BCrypt::Errors::InvalidHash
return false
end
end
This should do the job.

Related

Find_for_database_authentication vs Find_by in Rails Devise app?

So, I'm trying to set up a React frontend and Rails backend with devise, and the Rails side is supposed to be an internal API. It's the first time I've ever done this, so I'm struggling with authentication. Specifically, in my SessionsController, I have this code:
def create
resource = User.find_for_database_authentication(email: params[:email])
return invalid_login_attempt unless resource
if resource.valid_password?(params[:password])
sign_in :user, resource
return render nothing: true
end
invalid_login_attempt
end
This always returns 401 Unauthorized. I check the result of calling valid_password? and it is always false.
However, if I replace find_for_database_authentication with find_by, the valid_password? works with no problems. Why is this? It's okay if for now the user can only enter his email and not his password, but this really confuses me. It also bugs me that this doesn't use any token checking (different issue).
On the side, I'm also wondering about whether or not CSRF tokens are okay for internal APIs (should I use a different token-auth?), and how I'm supposed to include a CSRF token with a login form if the user isn't logged in yet, but I guess those are questions for another post. Thanks for any help.

Devise allows blank password during password reset

I have a Rails 3.2 app setup with Devise. Everything works great except for when I reset a password ( via the built in routes and methods ), Devise is allowing a blank password. It will validate the length and if it matches the confirmation if there is at least one character. I do have it setup where in a users account they can update their profile without entering the password, but I don't think that has anything to do with resetting the password.
Any help is appreciated.
devise.rb -> http://pastie.org/3911178
user.rb -> http://pastie.org/3911187
Thanks for pointing me in the right direction. The problem was caused by what you described. However, if I let devise handle the validation or use the same code they do, the user must provide a password when updating their account even after they are logged in. To fix this, I just checked for the rest_password_token in my validation:
def password_required?
# If resetting the password
return true if reset_password_token.present? && reset_password_period_valid?
# If the person already has a pass, only validate if they are updating pass
if !encrypted_password.blank?
password.present? || password_confirmation.present?
end
end
*UPDATE
I just updated this to ensure the password token is not expired.
You should let devise handler password validations: https://github.com/plataformatec/devise/blob/master/lib/devise/models/validatable.rb or use the code devise is using for validations.
The issue with your code is that you're doing validations only if the user doesn't has a password set (!encrypted_password.blank?) and other conditions. When recovering the password the user already has a password set so you don't run validations on password updates ...

How do I get AuthLogic to skip Password validation?

I think I'm just missing something obvious. I send a user a perishable token embedded in a link. They click on it, and they come back to the site. I want to log them in automatically --- authenticated by their perishable token, not the password. (I'm not building a banking app).
This seems like this should be simple, but all the examples I've found require a password. How do I skip this completely? When I try to get UserSession.create to work, it reports a validation error and will not create the user session. What is the way around this?
#user = User.find_by_perishable_token(params[:token])
if #user
if !current_user
# skip sign-in
UserSession.create!(#user.email)
# => error "You did not provide any details for authentication."
...
I have googled extensively but haven't found the answer.
Doesn't UserSession.create take a user object as it's first argument? If so, couldn't you just do:
UserSession.create(User.find_by_perishable_token(params[:token]))
#current_user_session = UserSession.find
Or is that where you're running into problems?

Rails: Accessing the username/password used for HTTP Basic Auth?

I'm building a basic API where user information can be retrieved after that user's login and password are correctly sent.
Right now I'm using something like this:
http://foo:bar#example.com/api/user.xml
So, what I need to do is access the user/password sent in the request (the foo and bar) but am not sure how to access that info in a Rails controller.
Then I'd check those variables via a quick User.find and then set those as the username and password variables for authenticate_or_request_with_http_basic.
It's possible I'm looking at this at the completely wrong way, but that's where I'm at right now. :)
The answer to your question of how to get the credentials from the request is this:
user, pass = ActionController::HttpAuthentication::Basic::user_name_and_password(request)
However authenticate_or_request_with_http_basic is all you need to do basic auth:
class BlahController < ApplicationController
before_filter :authenticate
protected
def authenticate
authenticate_or_request_with_http_basic do |username, password|
# you probably want to guard against a wrong username, and encrypt the
# password but this is the idea.
User.find_by_name(username).password == password
end
end
end
authenticate_or_request_with_http_basic will return a 401 status if credentials are not supplied, which will pop up the username/password dialog in a browser. If details are given then those are passed to the block provided. If the block returns true the request goes through. Otherwise the request processing is aborted and a 403 status is returned to the client.
You can also check out Railscast 82 (thats were the code above is from):
http://railscasts.com/episodes/82-http-basic-authentication
The rails plugin Authlogic supports this functionality (as well as much more) out of the box. You could root around in the source for it, or simply integrate it into your existing application.
Edit:
After digging around the source code for Authlogic, I found this file which uses the following piece of code to grab the username and password:
def authenticate_with_http_basic(&block)
#auth = Rack::Auth::Basic::Request.new(controller.request.env)
if #auth.provided? and #auth.basic?
block.call(*#auth.credentials)
else
false
end
end
I'd look a bit further into where it all goes, but I've got to get to bed. Hope I was of some help.

Authlogic-oid with ONLY OpenID

I am implementing an internal site, for which I want our company's OpenID server to be the only means of registering and logging in. To be more specific, I don't even want a normal email and password/salt to be stored for the users in this site.
I am using authlogic with the authlogic-oid plugin, but I am getting these errors whenever I try to make a new user:
undefined local variable or method `crypted_password_field' for #<User:0xb68b7c00>
I take this to mean that authlogic is trying to generate a password for this user even though there are no password fields in my database. Is there a workaround for this, or config options I can pass to acts_as_authentic to make this work?
Figured it out. In your User model, you must specify this config in the acts_as_authentic block:
class User < ActiveRecord::Base
acts_as_authentic do |c|
c.crypted_password_field = false
end
end
Looks like maybe you're trying access the crypted_password_field property somehow. If you look at the Authlogic example the documentation lists the optional fields (#3). I was able to get Authlogic and RPX up and running without password fields so I know it's possible.

Resources