API with NestJS #42. Authenticating users with Google

JavaScript NestJS TypeScript

This entry is part 42 of 180 in the API with NestJS

Authenticating users with emails and passwords is a valid and common approach. However, a convenient alternative is to shift this responsibility to a third party. In this article, we look into adding Google authentication to a NestJS project.

We can achieve the above because Google uses the OAuth (Open Authorization) framework. With it, Google users can grant access to some of their data to an application – in this case, us. In this series, we’ve already implemented a way to register users. Therefore, this article aims to implement an additional way of registering users with Google that can work besides authenticating with a password and an email. Because of that, in this article, we won’t be using the popular passport-google-oauth20 library.

Registering an application

The first step in implementing authentication with Google is registering an application in the Google Cloud Platform dashboard.

When we have a project, we need to set up the OAuth consent screen.

When configuring it, we need to set up some basic information about our application. For example, if we would like to deploy our application and use it outside of localhost, we would need to register our domain.

For testing purposes, we also need to specify a list of users that are allowed to use our application. If we would like our application to be available to any user, we need to submit our app for verification.

After sorting out the above, we need to generate OAuth Client ID credentials for our application in the credentials dashboard.

We could also set the redirect URL, but in this article we use a different approach that doesn’t use it.

Above, in the authorized JavaScript origins, we put the URL of our frontend application. We also need to specify the redirect URL. Google will redirect the user to it after successful authentication.

Finalizing the process of creating the credentials gives us the client ID and the client secret. We need to save them in the environment variables.

app.module.ts

.env

The frontend side

To implement authentication with Google in our application, we need to allow the users to type in their Google credentials. A straightforward way to do that in React is to use the react-google-login library.

In this simple example we use Create React App.

First, we need to add some environment variables.

.env

react-app-env.d.ts

Once we have the above, we can use the react-google-login library.

GoogleButton.tsx

Clicking on the above button causes a popup to show.

To change the above behavior, we could pass to the component. This would cause a full redirect instead of opening a popup.

When the user successfully authenticates, the callback is invoked.

useGoogleAuthentication.tsx

Above, we take the from the response and send it to our NestJS API.

Implementing Google authentication with NestJS

The last step in authenticating our users is receiving the from Google and logging the user into our system.

googleAuthentication.controller.ts

Above, we expect the frontend to call the endpoint with the access token.

tokenVerificationDto.ts

The final part of the implementation is the method.

googleAuthentication.service.ts

Registering new users

The crucial part is that the users can be signed up into our system at this point, but they don’t have to be. Therefore, we need to handle both cases. Let’s start with registering the user.

googleAuthentication.service.ts

In our API, we require the user to provide a name. We can get this data from the Google API. Unfortunately, the googleapis library requires us to do that in a way that is a bit odd.

googleAuthentication.service.ts

When the users are not registered yet, we add them to our database with the method.

googleAuthentication.service.ts

Above, you can see that we add the user to Stripe. If you want to know more, check out API with NestJS #36. Introduction to Stripe with React

To handle it properly, let’s make some changes to the :

user.entity.ts

Please notice that the password is now nullable, because we don’t need it for users authenticated with Google. It would be a good idea to modify the existing code usef for verifying the user’s password to account for this change.

Returning the data of the user

When the users are registered, we need to generate cookies for them.

If you want to know more about this approach, check out API with NestJS #3. Authenticating users with bcrypt, Passport, JWT, and cookies

googleAuthentication.service.ts

Above you can see that we generate refresh tokens. To read more about it, check out API with NestJS #13. Implementing refresh tokens using JWT

Thanks to implementing all of the above, the handles both new and returning users.

To see the whole picture more accurately, check out the full code for the .

Summary

In this article, we’ve implemented a way for our users to authenticate with Google. Moreover, we’ve done it in a way that integrates with our existing system. To do that, we register users into our own database instead of relying upon Google. Thanks to doing so, we don’t have to make significant changes to the existing code that takes care of registering users with the email and the password.

Series Navigation<< API with NestJS #41. Verifying phone numbers and sending SMS messages with TwilioAPI with NestJS #43. Introduction to MongoDB >>
Subscribe
Notify of
guest
9 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Nathanael Cunningham
Nathanael Cunningham
3 years ago

What is the workflow for checking if the user is authenticated from a request on the frontend after the user logs in?

Twelve
Twelve
2 years ago

According to this article: https://stackoverflow.com/questions/33860262/how-to-interact-with-back-end-after-successful-auth-with-oauth-on-front-end

Here’s how the flow works, in this system:

ELSE:

NOW, the user can directly use your API:

EDIT

Based on discussion – adding that the backend can authenticate a user by verifying the access token with the identity provider:

For example, Google exposes this endpoint to check a token 

XYZ123

Twelve
Twelve
2 years ago

Other helpful answer:

Your FE receives OAuth token from Service Provider after user gives authorization. FE passes OAuth token to BE. BE sends OAuth token to Service Provider to validate the OAuth token. Service Provider responds to BE with username/email information. You can then use the username/email to create an account.

Then after your BE creates the account, your BE should generate its own implementation of an OAuth token. Then you send your FE this OAuth token, and on every request, your FE would send this token in the header to your BE. Since only your BE has the secret key to validate this token, your application will be very safe. You could even refresh your BE’s OAuth token on every request, giving your FE a new key each time. In case someone steals the OAuth token from your FE, that token would be quickly invalidated, since your BE would have already created a new OAuth token for your FE.

ICode Rule
ICode Rule
3 years ago

Hi
I followed the above steps, but when I deploy using docker image, getting an error in the AWS server. but it’s working local env.

axselll
axselll
2 years ago

Hi, great content. I just wanna ask can i ignore the stripe thingy will it run properly. thanks

Ngoc Anh
Ngoc Anh
1 year ago

Here frontend I try Next Auth library will automatically return cookies when login is successful, I save user information in database for management, do I have to return cookies again because Next Auth has set cookies already

Mark
1 year ago

Faces such error on frontend react app – how to solve it?
Failed to log in
cb=gapi.loaded_0?le=scs:176 Cross-Origin-Opener-Policy policy would block the window.closed call.
Lv @ cb=gapi.loaded_0?le=scs:176
(anonymous) @ cb=gapi.loaded_0?le=scs:176

Mark
1 year ago
Reply to  Mark

May be that error not important but I am getting “onFailure” log, can’t have “onSuccess” for some reason:

return (
<GoogleLogin
clientId={clientId}
buttonText=‘Log in’
onSuccess={handleSuccess}
onFailure={() => {
console.log(‘failure’);
}}
/>
);

Mark
1 year ago
Reply to  Mark

OOOHHH I fugered that out, just put in into index.html, run with live server on 127.0.0.1:5500, use cloudflare to expose it to internet, whitelist in authorized origins on google console portal

https://developers.google.com/identity/gsi/web/guides/display-button#javascript