Logging into your favourite app using your Google, Facebook, or Amazon credentials is now an expectation for modern applications. The benefits of configuring your app to support Social Sign On (SSO) are well documented and include benefits such as streamlined signups, greater app adoption, and less password reliance.
The benefits of SSO using identity providers are clear, but how do we go about adding SSO support such as Google’s to our AWS application?
We’re going to leverage Amazon Cognito – AWS’ generic access control service. It allows administrators to create user pools that govern access to their applications. Using Amazon Cognito’s interface, it’s very easy expand your options for login from a username and password combination, to using Google, Facebook, or Amazon SSO providers. By the way, if you’re looking for a complete beginner guide on Amazon Cognito, check out my comprehensive beginner guide here.
In this article, I’m going to walk you through the steps needed to add Google Social Sign On to your Amazon Cognito User Pool, allowing your application users to sign into your app using a users existing Google credentials.
Although in a production usecase I would suggest using Infrastructure as Code, I typically suggest doing it the first time through the console to help you see the moving parts in motion. In this post, we’re going to be using the AWS Console.
So lets get started.
Prefer a video instead? Check out my YouTube video on this video here:
Prerequisites
You’re not going to need much for tutorial, all you need is
- An AWS Account
- A Google/Gmail Developer Account with Access to Google Cloud Platform (to check, try visiting the GCP dashboard using this link )
- A bit of knowledge of OAuth2.0 – for those out of the loop, Cognito uses OAuth2 protocol to authenticate users as part of the login flow. Learn more about it here.
Step 1 – Creating Your Amazon Cognito User Pool
Background Info on Cognito User Pools
In order for us to add any form of authentication or authorization to our app, we first need a Cognito User Pool. A User Pool is essentially a collection of users that are currently known to your system. You can add users to your pool either directly through a custom Amazon Cognito UI, or by using a Social Sign On provider (such as google) to federate.
Note: the term Federate simply refers to the ability of a user to use an account existing in a different system to login to your user pool.
This also means a human person can have multiple accounts in your user pool. This in itself isn’t a problem, and its certainly a common place to have multiple accounts in many ecosystems such as Google, so don’t get hung up on it. However, if you’re expecting your user pool to represent a unique human per entry, that would require custom “identity linking” which is beyond the scope of this article.
Carrying on, below is a screenshot of a User Pool‘s users in a Demo pool I just created. The top being a user I created with a default username and password, and the bottom being an account created as a result of logging in via Google using Social Sign On.
Creating the Pool, Step by Step
To create our User Pool, head over to the Amazon Cognito section of the AWS console. You should see a screen similar to what I have below. Go ahead and click on Manager User Pools to get started.
On the following screen, you’ll want to go ahead and click on Create a User Pool in the top right of your screen as seen below.
This is going to launch the User Pool creation wizard. Initially we just need to give our application a name and decide whether or not we want to accept the default settings, or step through each one by one and do some customization.
We’re not going to walk through each of the individual settings in this video, but I encourage you to go through them on your own time. They allow for customizations such as password strength, enabling Multi-factor authentication (MFA), and using Lambda to create ‘hooks’ at different stages of the OAuth2.0 login flow.
These extra features are what make Amazon Cognito so popular, but to keep things simple in this article, we won’t mention those details unless necessary for this tutorial.
To move forward, I’m going to call my pool DemoAppPool and click on Review Defaults below. After doing so, you should see a summary screen like the one I have below.
Clicking Create Pool should result in a confirmation message as seen below. Congrats! You just created your first User Pool!
This pool doesn’t do much right now at all. So lets progress by adding an App to our User Pool.
Step 2 – Creating Your Amazon Cognito App
First Things First – What is a Cognito App?
In order to answer what is an App or Application in Cognito’s context, we need to understand a concept called Multi-tenancy. Multi-tenancy is basic idea that we can create a single piece of software + infrastructure that serves multiple different customers.
Cognito User Pools can leverage this concept of multi-tenancy by serving multiple different applications at once using the same User Pool. This can be great for companies that want users to only have to create an account once, and be able to use that account all over the companies ecoystem.
It turns out that in order for a Cognito User Pool to be useful, we need to have atleast one configured application. So lets quit the talk and get one set up.
Creating the Application
On your left hand navigation pane, click the tab called App Clients under the General Settings tab as seen below.
On the next screen, click on Add an app client.
From here, we’re prompted with a bunch of options to configure our application client. Go ahead and pick an App client name and fill it in the first box. I’m calling mine DemoAppClient.
I’m going to leave most of these settings as default here. I’ll let you explore them on your own time. Just note that these settings are app client specific and can be changed even after you create your initial client – so don’t worry too much about the details for now.
Go ahead and click on Create App Client at the bottom of the page. You should now be greeted by a new page presenting you with your App Client Id and Secret Key. This is going to be important later when we set up our google integration.
Step 2 – Creating Your Amazon Cognito Domain Name
In this next step, we need to set up a Cognito Domain Name. This is going to be useful to act as a sign in portal for users that want to access your application. Its at this domain that the user will be presented with the ‘Sign In With Google’ button.
To get started, click the Domain Name option under the App Integration section of the menu bar, like seen below.
On the next page, you have an important decision to make. Do you want to use Amazon’s provided URL for clients to login? Or do you want to set up your own?
Typically, most professional folks that really care about the optics of their application will go ahead and use their own domain. This is not that difficult, but annoying enough that its something you’ll avoid unless absolutely necessary.
Since this tutorial is about simplicity, I’m opting for the first option which is to use an Amazon Provided Domain Name.
Picking a Domain
Picking a domain is pretty straight forward – generally just pick something related to your application. Perhaps a name or functionality. Do remember though that no upper case or special characters are allowed beyond numbers or hyphens.
Note though that whatever name you choose, it needs to be Globally Unique. In other words, two people, even if they have different AWS accounts, cannot share the same domain name. For reasons pretty obvious, URLs are globally unique, so in turn, so must be our domains.
You can click on the Check Availability button (1) to see if your domain is available before committing. A confirmation message (2) will indicate the name is up for grabs.
After reviewing the page, click on Save Changes (3) in the bottom right.
Our domain is now accessible! To check it out, click on the App Integration option on the right hand side of the menu to find our login domain. mine is https://demoappdomainname.auth.us-east-1.amazoncognito.com (Note, this URL will not be accessible by the time this article is posted, so don’t worry if it doesn’t work).
Do note though that if you try to visit your domain initially, you’ll be greeted with a Blank Page. Hmph, that isn’t right. But don’t fret! This is intentional. First, we need to muck with our App Client Settings to tell Cognito to leverage our User Pool for this UI.
Step 3 – Integrating our User Pool and App Client
To begin, click on App Client Settings on the left hand menu under App Integration as seen below.
We’re greeted by a complicated menu with a whole bunch of options. So lets briefly touch on what all of this stuff is and what we need to change.
Firstly, as seen in (1) below, click Cognito User Pool under Enabled Identity Providers. This will tell cognito to use your User Pool that you just created as a source for your application. When we add Google as our Federated Identity Provider later on, we’ll have to come back to this menu to select it as well. But for now, selecting Cognito User Pool will do.
For (2) and (3), we need to enter a Callback URL and Sign Out URL. These requirements are kind of specific to the OAuth2.0 protocol, so I won’t discuss them ad nauseum here. But basically, the callback url is an endpoint that your application will be redirected to after successful authentication. Embedded in the url will be a JWT (Json Web Token) that contains information about the user, also known as claims (which need to be filled out below). These claims can be used by your application to discover attributes about the user including firstname, lastname, and other attributes.
So using the example domain from above in (2) – after a successful login, your user will be redirected to a URL that looks like https://example.com/cb?auth_token=<GENERATED_JWT_TOKEN_HERE>. You can use a library in your language of choice to decode the information and store details in cookies for subsequent login. Pretty cool!
Sign out URL follows the same principles, but is dedicated to Sign Out functionality.
You don’t NEED to fill these values in right now, but this is basically how you’re going to get confirmation on your application end that a user has successfully signed in. You can always come back here later to change the URL in case you’re not sure of what its going to be.
I want to take a quick pause to discuss some more details about OAuth2.0 flows. I feel that this information is relevant for this discussion to understand the basic principles of how tokens are generated and exchanged to authenticate users. Feel free to skip this section if you’re already familiar with OAuth2.0 flow fundamentals. I personally think this is a nice brief refresher, even if you already know how it works.
OAuth2.0 Flows – Authorized Code Grant, Implicit Grant, and Client Credentials
Next, we need to decide on what type of OAuth2.0 flow(s) we want to use. Each type, Authorized Code Grant, Implicit Grant, and Client Credentials are useful under different circumstances. Here’s a brief summary of when to use what:
- Authorized Code Grant – This flow relies on a third party, usually a backend server, to communicate with Cognito to verify the authenticity of a token provided to a client upon successful authentication. This token, provided to the client, is called authorization code. This code is then sent to your backend, where it is combined with a secret key, known only to your backend, to be confirmed with Cognito. Upon successful confirmation, Cognito issues a id_token and access_token which contain details about the user and authorization permissions respectively. This is a preferred solution when a backend is available to confirm the token, since it prevents Man In the Middle (MITM) attacks. However, a backend isn’t always available, which brings us to #2.
- Implicit Grant – When there is no backend available to double-check our token, such as in a staticly hosted react app or mobile application, we must rely on a different protocol called Implicit Grant. Implicit grant is similar to Authorized Code Grant in that an id_token AND access_token is sent back directly to the user after authentication. The client can then immediately take this token and use it to access restricted resources. Do note that since there is no ‘double checking’ going on at the backend, a token can be potentially intercepted during network communications by a bad actor, and used to access secured resources. This pattern is not ideal, but
- Client Credentials Grant – This third case is more straightforward – it is used to provide access credentials for a specific application as opposed to a specific user. Think granting permissions between two services such as CreditCardService which wants to call BankingService. In this flow, CreditCardService calls Cognito to obtain an access token. It then calls BankingService providing the access token as party of the Bearer HTTP header. Banking Service double checks this token is valid by calling Cognito, before finally providing access to its protected resource. Not really applicable in this demo, but good to know overall.
For more information on code grant types, I highly suggest this great resource from AWS which goes through the specifics of each flow. In fact, its where this beautiful image from below is sourced.
Before proceeding, make sure you set Implicit Grant flow from above as seen under (4).
Carrying On… Setting Our Scopes
Scopes (5) are essentially a lists of identifiers that indicate what access priviliges are being requested. There are a couple of well known scopes such as profile, email, address, and phone. Within the profile scope, there are claims, which as a reminder, are key/value pairs describing attribute information about a user.
In general, a scope is a request to a group of resources, and a claim is an entry that consists of a key/value pair for that scope.
For our Demo, we only really want to provide access to a couple scopes: phone, email, and openid. So go ahead and select those three options before clicking on Save Changes below. Be sure to check the reference image at the beginning of this section to make sure your configuration matches mine (except for callback urls, if applicable).
Voila, Our Hosted UI Now Works!
Affter clicking Save Changed from above, you should now see clickable text below that says Launch Hosted UI. If all went well, you should be greeted with a sign in page similar to the following: