Whenever you build a web application, you'll often need to provision accounts for your users. By users, I'm specifically referring to customers who interact with your services via your JavaScript/TypeScript frontend.
Managing these user accounts comes with plenty of challenges. Not only will you need to persist user profiles, you'll need to figure out how users can be organized into groups, how to store credentials, what kind of auth mechanisms would be suitable, and so on.
If you're a front-end developer who mainly cares about building enjoyable web experiences, these concerns might seem tedious to deal with. Fortunately, AWS Cognito handles them for you.
In my previous blog post, I covered some basic concepts around Identity and Access Management (IAM). Now, I'd like to build on these concepts by explaining how front-end developers can easily use AWS' Cognito and IAM services to set up authentication and authorization for customer-facing applications.
AWS Cognito
As mentioned, AWS Cognito handles all the nasty user management work. When using Cognito, you really just need to be familiar with two offerings: User pools and Identity pools.
To pull a quote from the Cognito console:
User pools are user directories that provide sign-up and sign-in options for your app users. Identity pools provide AWS credentials to grant your users access to other AWS services.
User Pools
The term "user pool" is an intuitive one, as it refers to a collection of users (which can be organized into groups).
Cognito provides plenty of features for customizing how this collections of users can be managed. Amongst other things, you can:
- Enable multi-factor authentication
- Set up federation with identity providers such as Google or Facebook
- Run customized logic as users progress through their auth-related user journeys (e.g. signing up, signing in).
When a user from your user pool logs into your application successfully, they'll have access to an ID (JWT) token from Cognito. This token can be used to obtain a set of AWS credentials corresponding to a specific IAM role. At this point, you might be wondering: How do we relate Cognito users to IAM identities at all?
Identity Pools
This is where identity pools come in.
Identity pools are a bit trickier to understand. The identities we're dealing with are IAM identities, which means their settings (e.g. permission policies) are defined in IAM, not Cognito.
I prefer to think of an identity pool as a collection of associations between IAM roles and Cognito user pools. For example, given a user pool MyUserPool
, we may want to define an identity pool which assigns one IAM role (e.g. MyAuthenticatedRole
) for users who are authenticated and another IAM role (e.g. MyUnauthenticatedRole
) for users who are not.
In other words, these identity pools specify the relationships between user pools and IAM roles. As such, Cognito users are able to exchange Cognito tokens for AWS credentials. Front-end clients receive these credentials from STS, which allows Cognito users to interact with AWS services.
In Practice
The steps for getting started with Cognito and IAM are relatively straightforward. On your JavaScript/TypeScript frontend, I recommend using the Amplify SDK, which supports many common authentication and authorization use cases.
AWS Console TODOs
On the AWS Console, you'll have to:
- Create a Cognito user pool with an app client representing your front-end web application.
- Create a Cognito identity pool with roles for authenticated and unauthenticated users. As you create the identity pool, be sure to link it to your user pool and app client by listing Cognito as an Authentication provider. Also, ensure your authenticated and unauthenticated roles have the necessary IAM policies in place for them to interact with your AWS services, such as API Gateway and S3.
Front-End Application Code TODOs
In your front-end application code, you'll need to:
- Store the region, user pool, identity pool id, and app client id in a configuration object (e.g.
config
). - Initialize Amplify with this config object (e.g.
Amplify.configure(config)
). - Use Amplify's
Auth
module for your auth-related operations. For example, you can simply invokeAmplify.Auth.signUp(...)
to register a user and add them to your user pool. - Use Amplify's other modules for interacting with other services. For example, you can use
Amplify.API
to speak with your API Gateway endpoints.
One thing worth calling out with Amplify's API
module is that it handles a lot of the IAM-based authorization work for you.
Under the hood, when you invoke a method such as Amplify.API.post(...)
, the module derives temporary AWS credentials corresponding to the appropriate role (e.g. MyAuthenticatedRole
). These credentials are used to sign the frontend request payload before sending it to API Gateway. See this article on SigV4 for details on the signing process.
For an overview of what the architecture I've described looks like, check out this section from an AWS Identity Workshop.
Wrapping Up
Note that using Cognito with IAM is not strictly necessary. For example, you can set up your endpoints on API Gateway to directly rely on Cognito user pools for authorization. Personally, I prefer the IAM-based approach, because it aligns "how Cognito users interact with APIs" with "how AWS services generally interact with each other".
I'm constantly impressed by how much AWS is able to abstract out these software building blocks to help developers scaffold their applications quickly. For anyone looking to get their hands dirty with the tools covered, here are some useful references:
Top comments (0)