Assign a password to new users when they sign up through 3rd party account
In my Rails app, there are two ways to sign up a new user in my app: the traditional one that you provide password and email, and the one that you use the third party account, which is Google account here in my app. In the building of the second route, I came across a validation issue, and the way I fixed it is by assigning a password to the user when it is created in the database. Here’s how I did it.
Ominauth
So to implement the second feature, I use Ominauth. Omniauth is a Gem in rails that offers another way to authenticate a user’s identity. Here in my app on the Login page, you see a button that says “Login with Google”. Once you click it, Google would usually ask if you want to authorize this operation of the website accessing your information to create a user in their database( or if you haven’t already logged in your Google account, you will be asked to log in first). Once you agree, a user is created in the database with the personal information sent by Google and you will usually be redirected to the page when you are logged in.
Problem
To make sure a user provides a password and an email when signing up, I put validations for User
model. Therefore, if an attempt to sign up a user without providing both of those two items, it will fail.
It is not too difficult when a form is provided to capture a password and an email. However, it is hard when I use Omniauth.
The good thing of Omniauth is, it is just one “agree” button away before you can sign up. It is very fast because it saves you a lot of trouble filling out a form. On the other side, the bad thing is, how can a user tell the database what the password is if there is not even a form?
Solution
At first, I was looking for a way to validate two ways of signing up differently. For example, ask for a password ONLY in the traditional route to sign up. But after I talk to one of my instructors, he told me better not to do that because a password is very important, and therefore, we should never give a roundabout route to skip setting up a password.
He gave me a suggestion on changing the corresponding action to create a user through third party accounts, which is, new_from_google
action in Sessions Controller
here.
It looks like this before:
def new_from_google
auth = request.env['omniauth.auth']['info']
@user = User.find_or_create_by(email: auth['email'])
if @user.persisted?
session[:user_id] = @user.id
redirect_to user_path(@user)
else
@user.name = auth['name']
end
end
The key lies in the assignment statement: @user = User.find_or_create_by(email: auth['email'])
. The method find_or_create_by
accepts a block and would pass the params to create
if a user is not found.
I change it in the following way. When a user with that specific email cannot be found, the block including a password will be passed to create a new user and the problem is solved! And next time, when the same user wants to log in, a google account’s presence is enough.
def new_from_google
auth = request.env['omniauth.auth']['info']
@user = User.find_or_create_by(email: auth['email']) do |u|
u.name = auth['name']
u.password = "superhardpassword"
end
if @user.persisted?
session[:user_id] = @user.id
redirect_to user_path(@user)
else
@user.name = auth['name']
end
end
Of course, you can also provide a way for the user to change their password later, just in case they want to log in using the email/password combination. Thanks for reading!