How To Add Passkeys To Amazon Cognito
Tags: passwords, auth, login, authentication
Published: 2023-06-07 18:41:10

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufieuwjo9ur2oc6nq424.png)
In this tutorial, we will cover how to integrate passkey authentication, the new standard for web authentication, into an existing Amazon Cognito instance that currently authenticates its users with passwords. As passkeys are quite novel, not many tutorials exist on how to integrate them with existing infrastructure. During the creation of this tutorial, AWS released an own passwordless prototype, which is a heavy construct and not ready for production (as AWS says).

**Overview**

The frontend of the sample application uses Angular, while the backend runs on Node.js / Express — both written in TypeScript. In general, you can use any web tech stack to integrate passkeys.

We use Corbado's passkey-first web components to handle authentication, while keeping Amazon Cognito as core user management system in place. Thereby, we leverage AWS Lambda functions for custom authentication flows in Amazon Cognito.

The structure of the article looks as follows:

1. Setup of Amazon Cognito
2. Current password-based auth with Amazon Cognito
3. Setup AWS Lambda functions with custom auth flows
4. Integrate Corbado into the application
5. Learnings from passwordless auth with Amazon Cognito
6. Opinion on AWS' passwordless sample
7. Summary

See the final repository on [GitHub](https://github.com/corbado/passkeys-amazon-cognito).

_**Disclaimer**__: the main purpose of this tutorial is the integration of passkey authentication in Amazon Cognito and build a working prototype. Session management / API endpoint authorization is only covered on a high-level and needs to be adapted for further usage. Note that some settings need to be made in Amazon Cognito to make the example work out-of-the-box. Take a look at_ [_1. Setup of Amazon Cognito_](https://www.corbado.com/blog/passkeys-amazon-cognito#setup-of-amazon-cognito) _and_ [_3. Setup AWS Lambda functions with custom auth flows_](https://www.corbado.com/blog/passkeys-amazon-cognito#setup-aws-lambda-custom-auth) _to check these settings. These AWS Lambda functions are used by Corbado to hook into the authentication flow, as Amazon Cognito heavily relies on passwords (there's not even a way to export password hashes), so a fully passwordless setup out-of-the-box is otherwise not possible in Amazon Cognito._

**1. Setup of Amazon Cognito**

If you have already setup Amazon Cognito, you can skip this step and go directly to [step 2](https://www.corbado.com/blog/passkeys-amazon-cognito#current-password-based-authentication).

We set up a basic user pool in Amazon Cognito with the following properties (most are default). We use this setup because most of the current Amazon Cognito implementations we've seen are configured this way. We would generally recommend higher security levels.

- Authentication with email and passwords
- No federated identity providers or social logins
- Default password policy
- No MFA
- Self-service account recovery
- Self-service sign-up
- No custom attributes
- Email handling by Cognito / AWS SES
- Confidential client (as authentication will be handled by our backend in Node.js / Express)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yljd1k5sbwze651lh9h.png)


Select "Email" as sign-in option:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rktzxcc43nkxruqhx9qj.png)

Define the password, MFA and recovery settings:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xubraabhlxxyw4jmu0tc.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ceh2kx67eekqj8r3x2h4.png)

Configure the sign-up experience:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bkqleqk27lsqxhjxgkw9.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pdda9vdbx65q5abfnxr.png)

To keep things simple, just use the Amazon Cognito email service:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jtcblvz3dw7aosa9d4vq.png)

Define a user pool name:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gv4bosvt20bx6h3oj9tl.png)

Select "Confidential client" as requests to Amazon Cognito will be made via our Node.js backend:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2fei05r902quxbs4r4cc.png)

Review everything and create the user pool:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hj0393yyl0jsrg0o2qj7.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fhzyk37ji34t5mdnbh8d.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6m56wyv2vkzn80872bz6.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opjkxe1oir2tvgds27g9.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fo7scg2uu2bhztl4cne1.png)


**2. Current password-based auth with Amazon Cognito**

**2.1 Frontend in Angular**

The current frontend is a simple web application that has a login / sign-up view and a logged-in view.

The structure of the frontend code follows a typical Angular project structure (only most important files are described below, see [GitHub repository](https://github.com/corbado/passkeys-amazon-cognito) for full code):


```
.
 ├── src
 | ├── app
 | | ├── auth
 | | | ├── auth.component.html # HTML structure for login/sign-up view with email and password input
 | | | ├── auth.component.ts # Interacts with AuthService to login and sign-up
 | | ├── ...
 | ├── logged-in
 | | ├── logged-in.component.html # HTML structure for logged-in view with main function to logout
 | | ├── logged-in.component.ts # Logout and go back to login/sign-up view
 | ├── app-routing.module.ts # Routing
 | ├── app.component.html # Display the page that is currently routed to
 | ├── auth.service.ts # Interaction with the backend to sign-up, login and logout
 | ├── ...
 | ├── assets
 | ├── index.html
 | ├── main.ts
 | ├── ...
 ├── ...
```


The frontend is generated with Angular CLI version 15.2.7. If not done yet, install it via:

```
npm install -g @angular/cli

```
Install all other packages by running the following command in the ./frontend-angular directory:

```
ng serve

```
You should see the following screen when you go to [http://localhost:4200](http://localhost:4200/) in your browser:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m8mfb10n4rzt97cor9w1.png)

**2.2 Backend in Node.js / Express**

The backend's main purpose is to communicate with Amazon Cognito for logging and signing up users. We use the AWS SDK "[@aws-sdk/client-cognito-identity-provider](https://www.npmjs.com/package/@aws-sdk/client-cognito-identity)" to communicate with Amazon Cognito.

The structure of the backend code follows a typical Node.js / Express project structure (only most important files are described below, see [GitHub repository](https://github.com/corbado/passkeys-amazon-cognito) for full code):

```
.
 ├── controllers
 | ├── authCognitoController.ts # Controller that handles the communication with Amazon Cognito
 ├── app.ts # Our server that handles the routing to the controller
 ├── ...
```

Install all required packages by running the following command in the ./backend-nodejs directory:

```
npm install

```

Please create a .env file in the ./backend-node.js directory and set the values for COGNITO\_REGION, COGNITO\_USER\_POOL\_ID, COGNITO\_CLIENT\_ID, COGNITO\_CLIENT\_SECRET and COGNITO\_JWKS (JWKS stands for JSON Web Key Set and contains keys that are used to verify JSON Web Tokens, JWTs, in the session management). In docker-compose.yml, we added AWS\_ACCESS\_KEY\_ID and AWS\_SECRET\_ACCESS\_KEY, which are optional here and can also be provided in other ways.

They can be obtained in the management console in Amazon Cognito:

COGNITO\_REGION: Amazon Cognito \> Navigation bar
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9aukxvj4xexn1ukzbjk.png)

COGNITO\_USER\_POOL\_ID: Amazon Cognito \> User pools

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kzsapwslzdam1j377bi.png)

COGNITO\_CLIENT\_ID: Amazon Cognito \> User pools \> corbado-user-pool \> App integration (on the bottom)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xhd4o7sa3yttp6v55tr1.png)

COGNITO\_CLIENT\_SECRET: Amazon Cognito \> User pools \> corbado-user-pool \> App client: corbado-backend

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfm9l1pqdzvvwif6bloa.png)

COGNITO\_JWKS: Open the following URL in your browser: https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json and copy the value for "keys" (should be an array):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbjnx3v4ht1hjebjh2yh.png)

Start the local development server that runs on port 3000:

```
npm run dev

```
If everything works fine, you should see the following output in the terminal:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vsn4u2wys8cv3hjy7x8x.png)

**3. Setup AWS Lambda functions with custom auth flows**

For the detailled tutorial on how to setup AWS Lambda functions with custom auth flows, feel free to check out the full-length article on: [https://www.corbado.com/blog/passkeys-amazon-cognito#setup-aws-lambda-custom-auth](https://www.corbado.com/blog/passkeys-amazon-cognito#setup-aws-lambda-custom-auth)

**4. Integrate Corbado into your application**

…same for the Corbado integration: [https://www.corbado.com/blog/passkeys-amazon-cognito#integrate-corbado-into-app](https://www.corbado.com/blog/passkeys-amazon-cognito#integrate-corbado-into-app)

**5. Learnings from passwordless auth with Amazon Cognito**

- We first thought about using the ADMIN\_NO\_SRP\_AUTH flow for creating sessions, as this flow didn't require a password to be set, but the flow was deprecated in [September 2021](https://docs.aws.amazon.com/sdk-for-ruby/v2/api/Aws/CognitoIdentityProvider/Types/CreateUserPoolClientRequest.html). This made using a CUSTOM\_AUTH flow the only viable alternative.
- Another thing that you need to consider when choosing Amazon Cognito as user management system, is that you cannot export password hashes and thus are more or less bound to Amazon Cognito forever. This makes switching to another user management provider nearly impossible (as long as you want to stick to some degree with password-based authentication).
- Currently, the integration of other authentication providers into Amazon Cognito is not as seamless as it could be. Especially, the following things are inferior:
- We only need the AWS Lambda functions to create a session for passwordless users. Therefore, needing 3 AWS Lambda functions is quite an overhead.
- The real authentication happens even before the Lambda functions are triggered.
- The developer experience of Amazon Cognito is often quite bad, e.g. the documentation of Amazon Cognito was sometimes outdated which makes developing quite hard, especially for non-standard cases. Moreover, error logs often do not really provide meaningful messages to debug properly. Besides, it sometimes takes some time until you see the logs in Amazon CloudWatch.
- New users are automatically set to "FORCE\_PASSWORD\_CHANGE". Directly updating the user doesn't work with "AdminUpdateUserAttributes" or "AdminConfirmSignUpCommand", so we had to execute the "AdminSetUserPasswordCommand" and provide a randomly generated password to confirm the new user in Amazon Cognito.
- When executing some admin commands, we sometimes faced error messages that our Cognito user pool did not exist, or we were not authorized to interact with it. Often, the cause of these messages is missing or wrong credentials in your .aws/credentials file. Here, you need to provide aws\_access\_key\_id and aws\_secret\_access\_key, which you can obtain in the following way
- Login to your AWS account
- Navigate to the AWS Management Console
- Click on your username at the top right corner and select "Security Credentials"
- Scroll down "Access keys"
- Click "Create access key" to create a new access key.
- Confirm and you can display or download the new access key.

**6. Opinion on AWS' passwordless sample**

While doing the integration, AWS released a first prototype of an own in-house [passwordless sample application](https://github.com/aws-samples/amazon-cognito-passwordless-auth). Just having a look at the code repository showed that the code base and adaptions to make on your own are massive. The code is not well documented. It's clearly not ready for production and without the CDK file or large AWS experience, the setup will be a nightmare. It took us quite long to get it up and running, but we managed it eventually. It's also not completely for free as AWS Key Management Service (KMS) is used, where [one KMS key currently is $1 per month](https://aws.amazon.com/de/kms/pricing/). Also, you're heavily locked into AWS infrastructure, so integrating it into your own tech stack seemed to be quite laborious.

**Summary**

In this tutorial, we've learned how to successfully integrate passkey authentication into an existing Amazon Cognito setup with Corbado. We leveraged Amazon Cognito custom auth flows to integrate the external authentication provider and showed the setup required to smoothly transition existing users to passkeys, while still offering passwords as fallback.

Due to our experiences with Amazon Cognito: if you are building a new application or website, we wouldn't recommend going with Cognito as it's clearly not in the focus of AWS and does not provide a passkey-first or passwordless-first experience. This is according to our and [Google's believe](https://blog.google/technology/safety-security/the-beginning-of-the-end-of-the-password/) essential, especially on mobile or native apps.

‍

_Other useful resources:_

- [https://theburningmonk.com/2023/03/passwordless-authentication-made-easy-with-cognito-a-step-by-step-guide/](https://theburningmonk.com/2023/03/passwordless-authentication-made-easy-with-cognito-a-step-by-step-guide/)
- [https://gist.github.com/mobilequickie/f4b4da3e42ae49d2306156ba3a9eaa75](https://gist.github.com/mobilequickie/f4b4da3e42ae49d2306156ba3a9eaa75)
- [https://stackoverflow.com/questions/63043865/aws-cognito-sign-in-without-password](https://stackoverflow.com/questions/63043865/aws-cognito-sign-in-without-password)
- [https://medium.com/@robert.broeckelmann/openid-connect-authorization-code-flow-with-aws-cognito-246997abd11a](https://medium.com/@robert.broeckelmann/openid-connect-authorization-code-flow-with-aws-cognito-246997abd11a)
- [https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1/](https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1/)
- [https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html)