Social login (OAuth2 google) in Rails 7
what is social login?
Social login is a form of single sign-on using existing information from a social networking service such as Facebook, Twitter, or Google, to sign into a third-party website instead of creating a new login account specifically for that website.
There are two ways to apply this notion in Rails because it is a full-stack framework. Now, these options differ based on how the view will be handled. So, the first method is to handle view on the rails side and use gems like omniauth in combination with a service provider’s gem like ‘omniauth-google-oauth2’, ‘omniauth-facebook,’ and so on. In the second approach the view is handled on the front-end side(for example React) and rails just do the necessary to log in the user.
There are tons of articles on the internet on the first approach, but for the second one, there are few, or approximately none, since I struggled a lot while doing my work with the second approach, and I don't want to see others be in the complete dark while implementing this, so let's begin with the second approach. By the way, if you are looking for the first approach, there is a superb article on that here.
So, for the second approach, we’ll use google as an example. I’m assuming you’re familiar with the flow of Oauth2 and have read about the first approach; if not, please go to any article on the first approach or the article listed above to learn more about the idea, or read the official docs of Oauth2.
I’ll walk you through the concept first, and then we’ll go into the implementation. So, with this approach, all you need to do is create an endpoint where the front end can supply you with a token. Now, how will they obtain the token? They simply run the OAuth2 procedure on their side, obtain a token from the service provider (in our case, Google), and then hit the API endpoint supplied by our rails (backend). let’s assume the endpoint is
https://yourdomain.com/auth/google_oauth2/callback
and routes defined are
post '/auth/google_oauth2', to: 'sessions#google_oauth2'
Now the front-end side will hit the URL above along with the access token in the body, and after getting the token, we will hit the Google endpoint to get the information of the user as per token permissions. The Google endpoint that we will hit depends on the scopes and the information we are trying to get. In our case, we will get the user's basic info, like email, first name, and family name. So for that, you have to hit this endpoint.
https://www.googleapis.com/oauth2/v2/userinfo?access_token=your_token
For more information on scopes, you can read the official docs here.
Implementation
let's say you have a sessions controller where you have defined the callback and so the routes for that will look like this
post '/auth/google_oauth2', to: 'sessions#google_oauth2'
And your session controller’s callback method will look like this.
def google_oauth2
url = 'https://www.googleapis.com/oauth2/v2/userinfo?access_token=@token@'
url.sub! '@token@', your_token_here
url = URI(url)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request_body = Net::HTTP::Get.new(url)
response = http.request(request_body)
end
Now, you could ask, “Okay, we can use the code above to access the Google OAuth 2 API, but how will we acquire the token if there isn’t a front end yet?” The point is that you are correct, and thus we must obtain that token from someplace in order to serve this function. Google offers us Google Playground, through which we can obtain a token, develop the feature, and test it. Let’s look at how to obtain that token.
1- Go to this link https://developers.google.com/oauthplayground/
2- Now go to Oauth2 API v2 (by pressing ctrl+f and type oauth2) and click authorize API.
3- A consent screen like this will appear so choose any account you like (but the account has to be a developer account if not make it one)
4- A screen like this will appear
5- Now click on the blue button (exchange token), and a screen like this will appear.
Now this will contain the token that I just blurred in the green text and you can copy the token contained in the key “access_token”
Now that we have obtained a token, we can make a request using the code I just mentioned in the omniauth callback method (API) of the session controller. In response, you will get user information like below
{
"id": "123746581267304687183",
"email": "yourmail@gmail.com",
"verified_email": true,
"name": "john",
"given_name": "jonh",
"family_name": "doe",
"picture": "https://url_of_picture",
"locale": "en",
"hd": "sometext"
}
Now you can use it to sign in or sign up the user, and you can send the response accordingly to the front end. You can actually make a function like this (here I am using the user model as an example)
class User < ApplicationRecord
has_secure_password
def self.from_omniauth(response)
User.find_or_create_by (uid: response [:uid], provider: response [:provider]) do |u|
u.username response[:info][:name]
u.email = response[:info][:email]
u.password = SecureRandom.hex(15)
end
end
And that's it; you just implemented a social login flow successfully.