dApp authentication with Amazon Cognito and Web3 proxy with Amazon API Gateway
AWS Architecture BlogdApp authentication with Amazon Cognito and Web3 proxy with Amazon API Gatewayby Nicolas Menciere | on17 MAY 2023| inAmazon API Gateway,Amazon CloudFront,Amazon Cognito,Architecture,AWS Lambda,Serverless|Permalink|ShareIf your decentralized application (dApp) must interact directly with AWS services likeAmazon S3orAmazon API Gateway, you must authorize your users by granting them temporary AWS credentials. This solution usesAmazon Cognitoin combination with your users’ digital wallet to obtain valid Amazon Cognito identities and temporary AWS credentials for your users. It also demonstrates how to use Amazon API Gateway to secure and proxy API calls to third-party Web3 APIs.In this blog, you will build a fully serverless decentralized application (dApp) called “NFT Gallery”. This dApp permits users to look up their own non-fungible token (NFTs) or any other NFT collections on the Ethereum blockchain using one of the following two Web3 providers HTTP APIs:AlchemyorMoralis. These APIs help integrate Web3 components in any web application without Blockchain technical knowledge or access.Solution overviewThe user interface (UI) of your dApp is a single-page application (SPA) written in JavaScript usingReactJS,NextJS, andTailwind CSS.The dApp interacts with Amazon Cognito for authentication and authorization, and with Amazon API Gateway to proxy data from the backend Web3 providers’ APIs.Architecture diagramFigure 1. Architecture diagram showing authentication and API request proxy solution for Web3PrerequisitesInstallNode.js,yarn, ornpm, and theAWS Serverless Application Model Command Line Interface (AWS SAM CLI)on your computer.Have an AWS account and the properAWS Identity and Access Management (IAM)permissions to deploy the resources required by this architecture.Install a digital wallet extension on your browser and connect to the Ethereum blockchain.Metamaskis a popular digital wallet.Get an Alchemy account (free) and an API Key for the Ethereum blockchain. Read theAlchemy Quickstart guidefor more information.Sign up for a Moralis account (free) and API Key. Read theMoralis Getting Started documentationfor more information.Using the AWS SAM frameworkYou’ll useAWS SAMas your framework to define, build, and deploy your backend resources. AWS SAM is built on top ofAWS CloudFormationand enables developers to define serverless components using a simpler syntax.WalkthroughClone thisGitHub repository.Build and deploy the backendThe source code has two top level folders:backend:contains the AWS SAM Templatetemplate.yaml. Examine thetemplate.yamlfile for more information about the resources deployed in this project.dapp:contains the code for the dApp1. Go to thebackendfolder and copy theprod.parameters.examplefile to a new file calledprod.parameters. Edit it to add your Alchemy and Moralis API keys.2. Run the following command to process the SAM template (review thesam build Developer Guide).sam build3. You can now deploy the SAM Template by running the following command (review thesam deploy Developer Guide).sam deploy --parameter-overrides $(cat prod.parameters) --capabilities CAPABILITY_NAMED_IAM --guided --confirm-changeset4. SAM will ask you some questions and will generate asamconfig.tomlcontaining your answers.You can edit this file afterwards as desired. Future deployments will use the .toml file and can be run usingsam deploy. Don’t commit thesamconfig.tomlfile to your code repository as it contains private information.Your CloudFormation stack should be deployed after a few minutes. TheOutputsshould show the resources that you must reference in your web application located in thedappfolder.Run the dAppYou can now run your dApp locally.1. Go to thedappfolder and copy the.env.examplefile to a new file named.env. Edit this file to add the backend resources values needed by the dApp. Follow the instructions in the.env.examplefile.2. Run the following command to install the JavaScript dependencies:yarn3. Start the development web server locally by running:yarn devYour dApp should now be accessible athttp://localhost:3000(see Figure 2).Figure 2. NFT collection lookup applicationDeploy the dAppThe SAM template creates an Amazon S3 bucket and anAmazon CloudFrontdistribution, ready to serve yourSingle Page Application (SPA)on the internet.You can access your dApp from the internet with the URL of the CloudFront distribution. It is visible in your CloudFormation stackOutputtab in theAWS Management Console, or as output of thesam deploycommand.For now, your S3 bucket is empty. Build the dApp for production and upload the code to the S3 bucket by running these commands:cd dappyarn buildcd outaws s3 sync . s3://${BUCKET_NAME}Replace${BUCKET_NAME}by the name of your S3 bucket.Automate deployment using SAM PipelinesSAM Pipelinesautomatically generates deployment pipelines for serverless applications. If changes are committed to your Git repository, it automates the deployment of your CloudFormation stack and dApp code.With SAM Pipeline, you can choose a Git provider likeAWS CodeCommit, and a build environment likeAWS CodePipelineto automatically provision and manage your deployment pipeline. It also supportsGitHub Actions.Read more about thesam pipeline bootstrapcommand to get started.Host your dApp using Interplanetary File System (IPFS)IPFSis a good solution to host dApps in a decentralized way.IPFS Gatewaycan serve as Origin to your CloudFront distribution and serve IPFS content over HTTP.dApps are often hosted on IPFS to increase trust and transparency. With IPFS, your web application source code and assets are not tied to a DNS name and a specific HTTP host. They will live independently on the IPFS network.Read more abouthosting a single-page website on IPFS, andhow to run your own IPFS cluster on AWS.Implementation detailsIn this section, we’ll demonstrate how to:Authenticate users via their digital wallet using Amazon Cognito user poolProtect your API Gateway from the public internet by authorizing access to both authenticated and unauthenticated usersCall Alchemy and Moralis third party APIs securely using API Gateway HTTP passthrough andAWS Lambdaproxy integrationsUse theJavaScript Amplify Librariesto interact with Amazon Cognito and API Gateway from your web applicationAuthenticationYour dApp is usable by both authenticated and unauthenticated users. Unauthenticated users can look up NFT collections while authenticated users can also look up their own NFTs.In your dApp, there is no login/password combination orIdentity Provider (IdP)in place to authenticate your users. Instead, users connect their digital wallet to the web application.To capture users’ wallet addresses and grant them temporary AWS credentials, you can useAmazon Cognito user poolandAmazon Cognito identity pool.You can create a custom authentication flow by implementing anAmazon Cognito custom authentication challenge, which usesAWS Lambda triggers. This challenge requires your users to sign a generated message using their digital wallet. If the signature is valid, it confirms that the user owns this wallet address. The wallet address is then used as a user identifier in the Amazon Cognito user pool.Figure 3 details the Amazon Cognito authentication process. Three Lambda functions are used to perform the different authentication steps.Figure 3. Amazon Cognito authentication processTo define the authentication success conditions, the Amazon Cognito user pool calls the “Define auth challenge” Lambda function (defineAuthChallenge.js).To generate the challenge, Amazon Cognito calls the “Create auth challenge” Lambda function (createAuthChallenge.js). In this case, it generates a random message for the user to sign. Amazon Cognito forwards the challenge to the dApp, which prompts the user to sign the message using their digital wallet and private key. The dApp then returns the signature to Amazon Cognito as a response.To verify if the user’s wallet effectively signed the message, Amazon Cognito forwards the user’s response to the “Verify auth challenge response” Lambda function (verifyAuthChallengeResponse.js). If True, then Amazon Cognito authenticates the user and creates a new identity in the user pool with the wallet address asusername.Finally, Amazon Cognito returns a JWT Token to the dApp containing multiple claims, one of them beingcognito:username, which contains the user’s wallet address. These claims will be passed to yourAWS Lambda eventandAmazon API Gateway mapping templatesallowing your backend to securely identify the user making those API requests.AuthorizationAmazon API Gateway offers multiple ways of authorizing access to an API route. This example showcases three different authorization methods:AWS_IAM: Authorization with IAM Roles. IAM roles grant access to specific API routes or any other AWS resources. The IAM Role assumed by the user is granted by Amazon Cognito identity pool.COGNITO_USER_POOLS: Authorization with Amazon Cognito user pool. API routes are protected by validating the user’s Amazon Cognito token.NONE: No authorization. API routes are open to the public internet.API Gateway backend integrationsHTTP proxy integrationThe HTTP proxy integration method allows you to proxy HTTP requests to another API. The requests and responses can passthrough as-is, or you can modify them on the fly usingMapping Templates.This method is a cost-effective way to secure access to any third-party API. This is because your third-party API keys are stored in your API Gateway and not on the frontend application.You can also activate caching on API Gateway to reduce the amount of API calls made to the backend APIs. This will increase performance, reduce cost, and control usage.Inspect theGetNFTsMoralisGETMethodandGetNFTsAlchemyGETMethodresources in the SAM template to understand how you can use Mapping Templates to modify the headers, path, or query string of your incoming requests.Lambda proxy integrationAPI Gateway can use AWS Lambda as backend integration. Lambda functions enable you to implement custom code and logic before returning a response to your dApp.In thebackend/srcfolder, you will find two Lambda functions:getNFTsMoralisLambda.js: Calls Moralis API and returns raw responsegetNFTsAlchemyLambda.js: Calls Alchemy API and returns raw responseTo access your authenticated user’s wallet address from your Lambda function code, access thecognito:usernameclaim as follows:var wallet_address = event.requestContext.authorizer.claims["cognito:username"];Using Amplify Libraries in the dAppThe dApp uses theAWS Amplify Javascript Librariesto interact with Amazon Cognito user pool, Amazon Cognito identity pool, and Amazon API Gateway.With Amplify Libraries, you can interact with the Amazon Cognito custom authentication flow, get AWS credentials for your frontend, and make HTTP API calls to your API Gateway endpoint.TheAmplify Auth libraryis used to perform the authentication flow. To sign up, sign in, and respond to the Amazon Cognito custom challenge, use the Amplify Auth library. Examine theConnectButton.jsanduser.jsfiles in thedappfolder.To make API calls to your API Gateway, you can use theAmplify API library. Examine theapi.jsfile in the dApp to understand how you can make API calls to different API routes. Note that some are protected byAWS_IAMauthorization and others byCOGNITO_USER_POOL.Based on the current authentication status, your users will automatically assume theCognitoAuthorizedRoleorCognitoUnAuthorizedRoleIAM Roles referenced in the Amazon Cognito identity pool. AWS Amplify will automatically use the credentials associated with your AWS IAM Role when calling an API route protected by the AWS_IAM authorization method.Amazon Cognito identity poolallows anonymous users to assume theCognitoUnAuthorizedRoleIAM Role. This allows secure access to your API routes or any other AWS services you configured, even for your anonymous users. Your API routes will then not be publicly available to the internet.Cleaning upTo avoid incurring future charges, delete the CloudFormation stack created by SAM. Run thesam deletecommand or delete the CloudFormation stack in the AWS Management Console directly.ConclusionIn this blog, we’ve demonstrated how to use different AWS managed services to run and deploy a decentralized web application (dApp) on AWS. We’ve also shown how to integrate securely with Web3 providers’ APIs, like Alchemy or Moralis.You can use Amazon Cognito user pool to create a custom authentication challenge and authenticate users using a cryptographically signed message. And you can secure access to third-party APIs, using API Gateway and keep your secrets safe on the backend.Finally, you’ve seen how to host a single-page application (SPA) using Amazon S3 and Amazon CloudFront as your content delivery network (CDN).Nicolas MenciereNicolas is a Senior Startup Solution Architect based in New York. He helps high potential Web3 and FinTech startups succeed on AWS. A startup founder himself, he's been in the space for more than 15 years. Specializing in software development, security, and user experiences, he's now an active advocate of Web3 technologies.
If your decentralized application (dApp) must interact directly with AWS services like Amazon S3 or Amazon API Gateway, you must authorize your users by granting them temporary AWS credentials. This solution uses Amazon Cognito in combination with your users’ digital wallet to obtain valid Amazon Cognito identities and temporary AWS credentials for your users. It […]
Nicolas Menciere
Amazon API Gateway Amazon CloudFront Amazon Cognito Architecture AWS Lambda Serverless