
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.

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.

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.

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.

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.

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".

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).

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.

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.

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.

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.

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

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

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.

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.

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.

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.

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.

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.

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.

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.

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).