The world’s leading publication for data science, AI, and ML professionals.

Tutorial: MongoDB User Authentication with Google Sign-In

A step-by-step guide to simplify your application's access control and personalize user experience.

Photo by Franck on Unsplash
Photo by Franck on Unsplash

MongoDB has quickly become my nonrelational database platform of choice for its high performance, broad developer support, and generous free tier. As is the case with many database engines, user management and access control can get quite complex, especially when the software stack draws on other resources, like microservices or cloud storage. Fortunately, we can leverage so-called "federated identity providers" like Google, Facebook, and Amazon to decouple user authentication from the rest of the application.

In this tutorial, we’ll walk through all the steps necessary to build a secure MongoDB-powered application from scratch. Users will need to sign in with their Google account to read from the database.

By the end of this tutorial, we will have a simple web app that allows users to view a Fruit Marketplace by logging in with their Google account (image by author).
By the end of this tutorial, we will have a simple web app that allows users to view a Fruit Marketplace by logging in with their Google account (image by author).

Before we can write a line of code, we have to setup our cloud-based authentication flow, which involves coordination between Google Identity, AWS IAM, and of course, our MongoDB cluster.

Registering a Google Application

The first thing we need to do is register our application with Google. In addition to piggybacking off existing security infrastructure (and legitimizing the login page with a familiar interface), this will enable us to personalize the user experience by viewing customers’ profile information.

First, login to the Google Cloud Console, create a new project, and give it a descriptive name.

Creating a new project in the Google Cloud Console (image by author).
Creating a new project in the Google Cloud Console (image by author).

Once your new project is initialized, search for "APIs & Services", navigate to the "OAuth consent screen" tab in the sidebar, and click "Create an External User". On the first page of the setup wizard, you’ll need to provide an application name and an email address for user support.

Configuring the OAuth consent screen, which will notify users what permissions our application is requesting (image by author).
Configuring the OAuth consent screen, which will notify users what permissions our application is requesting (image by author).

The next page is where we will define our application scope, which dictates exactly what aspects of a user’s Google account we will have permission to read or modify. This is one of the most consequential steps of the entire process, as we could request users’ contact information, calendars, location, or other personal content. In this demo, our app will be able to read a user’s email address and profile, which includes their name and avatar.

Enabled scopes for the application - in this case, read access to users' email and profile (image by author).
Enabled scopes for the application – in this case, read access to users’ email and profile (image by author).

Important note: In a production environment, you will need to provide a Privacy Policy and other legal documentation to define exactly how this personal information will be used.

Next, assign at least one test user. Until our app is officially approved for public access by Google, login access will be restricted to these accounts.

Assigning test users is necessary until our app has been officially published by Google (image by author).
Assigning test users is necessary until our app has been officially published by Google (image by author).

Once we’ve established the OAuth consent screen, we need to generate a Client ID for our Google app. Still within APIs & Services, navigate to the "Credentials" tab in the sidebar. Under "Create Credentials", select "OAuth client ID".

Creating an OAuth client ID on the Credentials tab (image by author).
Creating an OAuth client ID on the Credentials tab (image by author).

Initialize the client as a web application and add http://localhost:8000 as an authorized JavaScript origin, since the login request will be made from our local development server (more on this later).

Configuring the Client ID credentials (image by author).
Configuring the Client ID credentials (image by author).

This will generate your app’s client ID (something like 1234567890.apps.googleusercontent.com). We will need this in a couple different places, so paste it somewhere for easy access.

Copy the Client ID (image by author).
Copy the Client ID (image by author).

This completes the Google Console part of the process. Now onto AWS.

Create an IAM Role in AWS

Once your application has been registered with Google, log into your AWS Console Account. Search for "IAM", select "Roles" in the sidebar, and click "Create Role". For the type of trusted entity, select "Web Identity" and choose Google as the identity provider (you’ll notice that we can use a similar flow for Facebook, Amazon, and other federated identity providers). Paste your Google Client ID under Audience. Accept the defaults for the rest of the Role setup.

In AWS IAM, create a new role with Google as the identity provider (image by author).
In AWS IAM, create a new role with Google as the identity provider (image by author).

Once your Role has been created, take note of the Role ARN (e.g. arn:aws:iam::AWS_ACCOUNT:role/roleName), which we’ll need in the next step.

One place to find the Role ARN (image by author).
One place to find the Role ARN (image by author).

This completes the AWS setup required for this demo, but I will make the note that by adjusting the Policy Document attached to this Role, we can connect our application to other AWS resources, like S3 buckets and Lambda functions.

Configuring the MongoDB Database

Next, we need to launch and configure our cloud-based MongoDB cluster. Log into MongoDB Atlas and create an organization.

Initializing a new organization in MongoDB Atlas (image by author).
Initializing a new organization in MongoDB Atlas (image by author).

Within your Atlas organization, create a new project and give it a name.

Create a new project (image by author).
Create a new project (image by author).

Finally, create a Database. For this demo, I’ll be using a free Shared Cluster.

I opted for the (free) Shared Tier (right-most option) (image by author).
I opted for the (free) Shared Tier (right-most option) (image by author).

Make sure to select AWS as the cloud provider and the M0 Sandbox Tier (also free) for this cluster. You shouldn’t have to connect a credit card unless you opted for upgraded capacity, which isn’t necessary for our simple demo.

Deploy the cluster to the AWS region of your choice and make sure the use the M0 Sandbox Tier to use the database for free (image by author).
Deploy the cluster to the AWS region of your choice and make sure the use the M0 Sandbox Tier to use the database for free (image by author).

Grab a cup of coffee while your cluster is deployed. When it’s ready, click the "Collections" tab. If you’re unfamiliar with Mongodb syntax, "Collections" are akin to SQL tables, and "Documents" can be thought of as JSON-based entries or rows within tables. The first time you create a new collection, you’ll have the option to use a sample dataset or add your own. We’ll opt for the latter.

Creating a new database and collection (image by author).
Creating a new database and collection (image by author).

Let’s say our app is a fruit marketplace that stores the names, prices, and remaining quantities of various items. We’ll insert a few documents into the "marketplace" collection within the "test" database. The quickest way to do this is directly within the web interface, but you can also perform bulk insertions through the MongoDB shell or one of their many supported drivers. Since this tutorial is more focused on authentication, I won’t dwell on schema or usage within MongoDB.

Inserting documents into our MongoDB collection within the web interface. You can also test out queries fom this page (image by author).
Inserting documents into our MongoDB collection within the web interface. You can also test out queries fom this page (image by author).

Finally, let’s connect our database to our IAM Role. On the side tab go to "Database Access" and add a new database user. For the Authentication Method, select "AWS IAM" and choose "IAM Role" in the "AWS IAM Type" dropdown menu. Paste the Role ARN in the required input.

Adding a new database user with AWS IAM credentials (image by author).
Adding a new database user with AWS IAM credentials (image by author).

Adjusting user privileges is another complex topic that warrants its own blog entirely. We’ll give this user read-only access to the "marketplace" collection that we just created.

Restricting this user's privileges to read-only access of the test.marketplace collection (image by author).
Restricting this user’s privileges to read-only access of the test.marketplace collection (image by author).

In addition to user-based authentication, MongoDB can also restrict access to specific IP addresses. In a production environment, we would only want to allow requests that come from our application’s servers, to help thwart threats like DDoS attacks. Since we’ll be running the demo application locally, under "Network Access", add only your current IP address, following the principle of least privilege.

Restrict network access to only the IP addresses that absolutely need it (image by author).
Restrict network access to only the IP addresses that absolutely need it (image by author).

Putting it all together in a demo application

With the setup complete, we are finally ready to write some code. If all goes well, signing in with our Google account will return an ID token that can be used to retrieve credentials for the AWS IAM Role. Our application’s server will then use those credentials to read from the database.

The 30,000-foot view of our application flow (image by author).
The 30,000-foot view of our application flow (image by author).

Client-side, we sign in, retrieve the credentials, send them to our backend, and use the response to update the webpage (the following snippet is taken from our index.html file).

Notice that within the signinCallback function, we’re sending a POST request containing the AWS credentials to the /fruits endpoint (again, we’re simplifying things for this demo by using an HTTP server – in production you would want to encrypt this connection in transit with HTTPS). That request will be processed by our FastAPI-based webserver like so:

As mentioned previously, while I opted for a Pythonic implementation, MongoDB supports drivers in dozens of Programming languages, so you may prefer to build a scalable server in Go, Node.js, or Rust.

The "fruits" of our labor: a simple, but personalized and secure application (image by author).
The "fruits" of our labor: a simple, but personalized and secure application (image by author).

And there you have it! While a simple demonstration, this infrastructure can easily be extended to support other identity providers or additional AWS resources.

Full sample code is available on my Github page.

Note from Towards Data Science’s editors: While we allow independent authors to publish articles in accordance with our rules and guidelines, we do not endorse each author’s contribution. You should not rely on an author’s works without seeking professional advice. See our Reader Terms for details.


References

[1] "Welcome to the MongoDB Documentation – MongoDB Documentation." https://docs.mongodb.com/ (accessed Jul. 28, 2021).

[2] "Integrating Google Sign-In into your web app." https://developers.google.com/identity/sign-in/web/sign-in (accessed Jul. 28, 2021).


Related Articles