<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: James Miller</title>
    <description>The latest articles on Forem by James Miller (@jamesmillerblog).</description>
    <link>https://forem.com/jamesmillerblog</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F777668%2Fe964f668-69aa-4b26-b3f0-7ef402fdcfb7.jpg</url>
      <title>Forem: James Miller</title>
      <link>https://forem.com/jamesmillerblog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jamesmillerblog"/>
    <language>en</language>
    <item>
      <title>Multiplayer WebXR Readyplayer.me Avatars — Part 1</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Thu, 10 Aug 2023 17:03:32 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/multiplayer-webxr-readyplayerme-avatars-part-1-31ca</link>
      <guid>https://forem.com/jamesmillerblog/multiplayer-webxr-readyplayerme-avatars-part-1-31ca</guid>
      <description>&lt;h3&gt;
  
  
  Multiplayer WebXR Readyplayer.me Avatars — Part 1
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Overview of technical concepts for multiplayer WebXR readyplayer.me avatars
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AE4yRSMFQvf9Sx-UbfIR_xg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AE4yRSMFQvf9Sx-UbfIR_xg.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Real-time multiplayer WebXR experiences is an area I am very excited about.&lt;/p&gt;

&lt;p&gt;I don’t think its an area that has been fully utilised or explored to its potential.&lt;/p&gt;

&lt;p&gt;In my previous series of posts about this topic, I talked through the &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-1/" rel="noopener noreferrer"&gt;concepts&lt;/a&gt;, &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-2/" rel="noopener noreferrer"&gt;practicalities of real time user interactions&lt;/a&gt; and &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-3/" rel="noopener noreferrer"&gt;steps to enabling 3d object interactions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this series of blog posts, I’m going to expand on the enhancement of user interactions through the use of free-to-use &lt;a href="https://readyplayer.me/" rel="noopener noreferrer"&gt;readyplayer.me&lt;/a&gt; avatars.&lt;/p&gt;

&lt;p&gt;This first blog post will focus on the concepts, the following blog posts will explain more about the practical steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F337%2F1%2Ar5gdpwinHqsvxokY_1SuVA.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F337%2F1%2Ar5gdpwinHqsvxokY_1SuVA.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;GIF of avatar enhanced interactions&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;In this section, I’ll explain the core principles of how to build a real-time multiplayer WebXR experience that uses &lt;a href="https://readyplayer.me/" rel="noopener noreferrer"&gt;readyplayer.me&lt;/a&gt; avatars!&lt;/p&gt;

&lt;h4&gt;
  
  
  The Concept
&lt;/h4&gt;

&lt;p&gt;This example works by creating a web application that renders on different devices (e.g an Oculus Quest Pro or a Macbook Pro), then getting those users to log into the website from those devices.&lt;/p&gt;

&lt;p&gt;When those users log in they then have the option to create an avatar using Ready Player Me, which will return a 3D model that is stored in the cloud — along with the positional data which is all shared between users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Aimz-rEFmEGSxHz8bn25Xkg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Aimz-rEFmEGSxHz8bn25Xkg.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a very broad overview:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user visits the website on their device and logs in using their credentials, the site will render as appropriate for their device (thanks to the WebXR API)&lt;/li&gt;
&lt;li&gt;Users can choose to select a model using Ready Player Me, which is then stored in the cloud.&lt;/li&gt;
&lt;li&gt;This 3d model is then visualised on their screen, as well as other users avatars — which is enabled by the cloud connecting these devices together.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;As this was originally written for &lt;a href="https://github.com/JamesMillerBlog/wrapper.js" rel="noopener noreferrer"&gt;Wrapper.js&lt;/a&gt;, it adheres to the use of &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;, &lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt; and &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The below diagram details this further.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ArZTWzzICfheo5ekfl1J1-A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ArZTWzzICfheo5ekfl1J1-A.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use Terraform to create all cloud resources except for Lambda functions, which are created and managed by Serverless Framework.&lt;/p&gt;

&lt;p&gt;All environment variables required to interact with Amazon Cognito are passed to the React.js Front End created using Next.js.&lt;/p&gt;

&lt;p&gt;The Front End also requests the 3D model from Ready Player Me, which is then rendered along with the rest of the 3D environment with React Three Fiber and Three.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  The App Flow
&lt;/h3&gt;

&lt;p&gt;If you were to map out when these technologies get used within this example, it would look something like the below diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AG9a7SulpFOTXbSvxG8lBRQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AG9a7SulpFOTXbSvxG8lBRQ.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you start at the bottom of the diagram, you can see that all the Front End files are exported statically and hosted on the &lt;strong&gt;S3 Bucket&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These files are then distributed with the &lt;strong&gt;Cloudfront CDN&lt;/strong&gt; , assigned with an SSL certificate by &lt;strong&gt;AWS Certificate Manager&lt;/strong&gt; and are provided with a custom domain name with &lt;strong&gt;Amazon Route 53&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At this point, Step 1 begins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user opens the website on their device (e.g the Oculus Quest Pro) by visiting the domain name&lt;/li&gt;
&lt;li&gt;At this point React-Three-XR renders the website based on the device’s capability (in this case a mixed reality headset)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next up is Step 2:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user logs into their AWS Cognito account, with the use of the AWS Amplify library on the Front End&lt;/li&gt;
&lt;li&gt;Having logged in, the website now has a unique identifier for the person that is using that device&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the user has successfully logged in and uniquely identified themselves, this sets them up nicely for Step 3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user has the option to open a model, that allows them to configure and create a 3D model&lt;/li&gt;
&lt;li&gt;If the user then selects this model at the end of the configuration process, a URL is returned back to the Front End that contains the .GLB files to render the avatar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its at this point that Step 4 begins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If an Avatar was selected, this is saved to DynamoDB, other users can then pull this avatar into their environments to be rendered.&lt;/li&gt;
&lt;li&gt;The user then emits their x/y/z co-ordinates in real-time to the websocket API gateway which then stores these in DynamoDB, other users can then see these positions and update their Front Ends in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This has been a long blog post, I hope it has been helpful in showing you how real-time multiplayer WebXR experiences that use readyplayer.me avatars work!&lt;/p&gt;

&lt;p&gt;In the next post, I will details the practical sides of the actual code that enables all of this.&lt;/p&gt;

&lt;p&gt;In the meantime, hope you enjoyed this post and have fun :D&lt;/p&gt;




</description>
      <category>reactthreefiber</category>
      <category>webxr</category>
      <category>serverless</category>
      <category>metaverse</category>
    </item>
    <item>
      <title>Multiplayer Metaverse Creation on Ethereum</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sun, 02 Jul 2023 12:13:59 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/multiplayer-metaverse-creation-on-ethereum-1cf3</link>
      <guid>https://forem.com/jamesmillerblog/multiplayer-metaverse-creation-on-ethereum-1cf3</guid>
      <description>&lt;h3&gt;
  
  
  How to create a serverless Metaverse with Solidity, Lambda, API Gateway, React Three Fiber, DynamoDB and Metamask.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bDmo3VB4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A6SweJHt7pNpZ4w-ODLIV8A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bDmo3VB4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A6SweJHt7pNpZ4w-ODLIV8A.jpeg" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro​
&lt;/h3&gt;

&lt;p&gt;The word Metaverse has been thrown around a lot and it can be quite confusing to actually understand what it means.&lt;/p&gt;

&lt;p&gt;I’ve &lt;a href="https://jamesmiller.blog/what-is-the-metaverse/"&gt;previously created a post that explains what the term ‘metaverse’ means&lt;/a&gt;, in this blog post I want to talk through the steps on how to build a Metaverse — like this one&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UiFZiawx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2A-WpPhrAiWdiraU363BWD9Q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UiFZiawx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2A-WpPhrAiWdiraU363BWD9Q.gif" alt="" width="600" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This demo is a combination of the &lt;a href="https://jamesmiller.blog/wrapperjs-webxr-template/"&gt;WebXR&lt;/a&gt; and &lt;a href="https://jamesmiller.blog/wrapperjs-eth-auth-template/"&gt;Web3 log-in&lt;/a&gt; codebases I’ve previously created.&lt;/p&gt;

&lt;p&gt;If you want to follow along with this, it is my strong recommendation that you read through the &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-1/"&gt;WebXR&lt;/a&gt; and &lt;a href="https://jamesmiller.blog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1/"&gt;Web3 log-in&lt;/a&gt; blog posts I’ve created, as a lot of the content in this post is built on top of them.&lt;/p&gt;

&lt;p&gt;If you want to skip all this and try out getting started with building a metaverse using a template I’ve created, you can have a &lt;a href="https://jamesmiller.blog/wrapperjs-eth-metaverse-template/"&gt;play with that here&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;LETS JUMP IN :D&lt;/p&gt;

&lt;h3&gt;
  
  
  The theory behind building a Metaverse​
&lt;/h3&gt;

&lt;p&gt;In this section I’ll explain the theory behind how building a Metaverse can work, by talking through the concept, tech stack and app flow!&lt;/p&gt;

&lt;h3&gt;
  
  
  Concept​
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PUBse6vq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AVVEm2qxMxB77pLq2GkuMQQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PUBse6vq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AVVEm2qxMxB77pLq2GkuMQQ.jpeg" alt="" width="800" height="1051"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo I’ve created essentially works as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The users who want to use the Metaverse log in to the web page using their Metamask wallets (&lt;a href="https://jamesmiller.blog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1/"&gt;see this blog post&lt;/a&gt; for details on how that works)&lt;/li&gt;
&lt;li&gt;Once logged in, those users can interact with other users through the use of websockets (&lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-1/"&gt;see this blog post&lt;/a&gt; for further details)&lt;/li&gt;
&lt;li&gt;Those users can then interact with smart contracts on the Ethereum blockchain via the use of their Metamask wallets&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Tech Stack​
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lUmcMAW---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AcDfzafplN43BJB-MOigrzw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lUmcMAW---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AcDfzafplN43BJB-MOigrzw.jpeg" alt="" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve summarised the technologies used to create the Metaverse demo as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I use Terraform to create all cloud resources on AWS excluding Lambda functions that run business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Back End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Serverless Framework is used to create Node.JS lambda functions, which interact with the Ethereum network via Web3.js and with the AWS resources that generate credentials for authenticated users. The Back End also implements Websockets using API Gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blockchain&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve used Ethereum related technologies such as Solidity and Hardhat for the smart contract creation / deployment, Web3.js is a core technology in the stack that lets the Back End and Front End interact with Smart Contracts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Front End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next.js a React.js framework that contains the logic for the Three.js library that generates the 3D environment in WebGL.&lt;/p&gt;

&lt;h3&gt;
  
  
  The App Flow​
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C8Hul2ZQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AZGhD57Z6AvcLWUuwZvJXjw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C8Hul2ZQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AZGhD57Z6AvcLWUuwZvJXjw.jpeg" alt="" width="800" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This app flow is a mix between the &lt;a href="https://jamesmiller.blog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1/"&gt;Web3 Auth&lt;/a&gt; and the &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-1/"&gt;WebXR multiplayer&lt;/a&gt; blog posts that I’ve previously created.&lt;/p&gt;

&lt;p&gt;As a summary for the Web3 Auth flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user signs a signature with their metamask wallet and submits this to AWS via the API Gateway&lt;/li&gt;
&lt;li&gt;A lambda function then processes the signature to verify if this has been signed by a valid user, if so it will then generate AWS STS credentials for the user to log in with&lt;/li&gt;
&lt;li&gt;Once logged in, the user can then interact with smart contracts and other users on the DApp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a summary for what happens in the DApp:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user opens a websocket connection using the AWS credentials that they’ve been provided with from the Auth process&lt;/li&gt;
&lt;li&gt;This websocket broadcasts the users position in 3d space, as well as receives other users positions in that 3d space and visualises them on the users screen&lt;/li&gt;
&lt;li&gt;The users can then move around the environment and interact with smart contracts together in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion​
&lt;/h3&gt;

&lt;p&gt;I hope this post has been useful in summarising how to go about developing a Metaverse.&lt;/p&gt;

&lt;p&gt;As this post is a cross between Web3 Auth and WebXR multiplayer experiences (both of which I have created long tutorials posts about previously), I’ve deliberately kept this one shorter.&lt;/p&gt;

&lt;p&gt;For the full code base and instructions on how to set up, please see &lt;a href="https://jamesmiller.blog/wrapperjs-eth-metaverse-template/"&gt;this template I’ve made for Wrapper.js&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Have fun :D&lt;/p&gt;




</description>
      <category>webxr</category>
      <category>ethereum</category>
      <category>aws</category>
      <category>threejs</category>
    </item>
    <item>
      <title>How to set up Auth with Metamask using Amazon Cognito (part 2)</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Fri, 23 Jun 2023 13:58:27 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-2-oa7</link>
      <guid>https://forem.com/jamesmillerblog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-2-oa7</guid>
      <description>&lt;h3&gt;
  
  
  Code samples that demonstrate how to set up Amazon Cognito Auth with Metamask in a DApp.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wmCa50eE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AmUfQEIsSPV9wAP6crYoIcA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wmCa50eE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AmUfQEIsSPV9wAP6crYoIcA.jpeg" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;In my &lt;a href="https://jamesmiller.blog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1/"&gt;previous blog post&lt;/a&gt;, I talked through the theory of how to implement web3 log in to a DApp using MetaMask and AWS, the below gif demonstrates the end result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8N7orfGR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2As90DAuSkcGkSGhfDbq9sow.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8N7orfGR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2As90DAuSkcGkSGhfDbq9sow.gif" alt="" width="600" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post I’ll talk through some code samples that enable this Auth process, step-by-step!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The below code samples are experiments I make in my spare time and are not recommendations of how to implement a production web app. If you choose to use these examples in your code base, be sure to test thoroughly and audit as appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code samples that enable Web3 Auth with MetaMask and AWS
&lt;/h3&gt;

&lt;p&gt;I’ll be referring to this diagram that I created in my &lt;a href="https://jamesmiller.blog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1/"&gt;previous blog post&lt;/a&gt;, in order to understand the below code samples its important you read it before continuing with the rest of this blog post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xle5GAK1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AxIrdVGpfl05zpSQivyl7KA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xle5GAK1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AxIrdVGpfl05zpSQivyl7KA.jpeg" alt="" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this section I’ll show the code behind each of the numbered items in the above diagram.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JamesMillerBlog/wrapper.js"&gt;Here&lt;/a&gt; is the full repo, &lt;a href="https://jamesmiller.blog/wrapperjs-eth-auth-template/"&gt;use this url&lt;/a&gt; if you want to get a version of it running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating Credentials with the Metamask Wallet
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Get account address
&lt;/h4&gt;

&lt;p&gt;The first step is to retrieve the users public address for their account.&lt;/p&gt;

&lt;p&gt;A wallet has a number of addresses returned in an array, the active account is the first item in the array, so we return this as the address to log in with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vbx3z87Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AQevpdmDCyM1nwlGlIrQgIQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vbx3z87Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AQevpdmDCyM1nwlGlIrQgIQ.jpeg" alt="" width="800" height="527"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const accounts = await window.ethereum.request({
    method: "eth_requestAccounts",
});

const address = accounts[0];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Get / create nonce
&lt;/h4&gt;

&lt;p&gt;Once the users account address has been retrieved, we then need to get a nonce (random string of characters) that we can then use for the user to sign and prove they are the owner of the account that created the signature.&lt;/p&gt;

&lt;p&gt;To do this, we pass the address into a function on the Front End, that then passes POST’s this address to the Back End.&lt;/p&gt;

&lt;p&gt;Once the Back End receives this address, check if a nonce already exists (if it doesn’t it will generate one), then return that nonce back to the Front End.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TUbK5zEk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AXO3EaeIjD3hKDkw84E6isQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TUbK5zEk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AXO3EaeIjD3hKDkw84E6isQ.jpeg" alt="" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Front End&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { nonce } = await getUserNonce(address);

const getUserNonce = async (address) =&amp;gt;
  await axios({
    method: "post",
    url: `${httpApiURL}/signup/nonce`,
    data: {
      address: address,
    },
    headers: {
      "Content-Type": "application/json",
    },
  }).then(
    (res) =&amp;gt; {
      const data = JSON.parse(res.data.body);
      return data;
    },
    (error) =&amp;gt; {
      console.log(error);
    }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Back End&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use strict";
const AWS = require("aws-sdk");
const dynamoDb = new AWS.DynamoDB.DocumentClient();
const crypto = require("crypto");

const { nonce_table_id } = process.env;

module.exports.handler = async (event, context, callback) =&amp;gt; {
  const response = {
    statusCode: 200,
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Credentials": true,
    },
    body: JSON.stringify(await data(event.body.address)),
  };
  callback(null, response);
};

const data = async (address) =&amp;gt; {
  const nonce = crypto.randomBytes(16).toString("hex");
  const putParams = {
    Item: {
      address: address,
      nonce: nonce,
    },
    TableName: nonce_table_id,
  };

  if (!await getData(address)) {
    await dynamoDb.put(putParams).promise();
  }
  return await getData(address);
};

const getData = async (address) =&amp;gt; {
  const getParams = {
    TableName: nonce_table_id,
  };
  const users = await dynamoDb.scan(getParams).promise();
  let chosenUser;
  for (let x = 0; x &amp;lt; users.Items.length; x++) {
    if (users.Items[x].address == address) {
      chosenUser = users.Items[x];
    }
  }
  return chosenUser;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Sign message with nonce &amp;amp; private key
&lt;/h4&gt;

&lt;p&gt;Back on the Front End, we call a function called &lt;strong&gt;web3.eth.person.sign()&lt;/strong&gt; — and pass the nonce and the users address into it, this will trigger a Metamask pop up to appear with these parameters encrypted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kjJHcxMt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A3NFJFA7hfeWEiWRrhCU-tg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kjJHcxMt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A3NFJFA7hfeWEiWRrhCU-tg.jpeg" alt="" width="800" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const signature = await web3.eth.personal.sign(
    web3.utils.sha3(`Welcome message, nonce: ${nonce}`),
    address
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Metamask pop up (see below) will have a ‘Sign’ button, which when clicked will cryptographically generate a signature using your private key and the props that you passed into the &lt;strong&gt;web3.eth.personal.sign&lt;/strong&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pKJo5U5s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2AO_TYtPTob3DkoKuZ7JC1Qg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pKJo5U5s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2AO_TYtPTob3DkoKuZ7JC1Qg.gif" alt="" width="600" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please note, the private key itself is not actually seen at any point during this process and is not visible in the codebase. In clicking the ‘Sign’ button, you are allowing Metamask to confirm that you own the private key that is associated with the public address you’re signing, this enables the creation of a cryptographically valid signature.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Log in with signature &amp;amp; public address
&lt;/h4&gt;

&lt;p&gt;Now you have a valid signature, you pass this to the Back End, along with the nonce and your public address via a POST method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FN6iufkR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ATbjLLmZjpBXT9zShJcfKDg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FN6iufkR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ATbjLLmZjpBXT9zShJcfKDg.jpeg" alt="" width="800" height="489"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {
    AccessKeyId, SecretKey, SessionToken, Expiration
} = await login(nonce, signature, address);

const login = (nonce, signature, address) =&amp;gt;
  axios({
    method: "post",
    url: `${httpApiURL}/signup/login`,
    data: {
      nonce: nonce,
      signature: signature,
      address: address,
    },
    headers: {
      "Content-Type": "application/json",
    },
  }).then(
    (res) =&amp;gt; {
      const data = JSON.parse(res.data.body);
      return data;
    },
    (error) =&amp;gt; {
      console.log(error);
    }
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Validate signature
&lt;/h4&gt;

&lt;p&gt;Once the Back End receives your signature, nonce and address — it can then start the process of validating the signature.&lt;/p&gt;

&lt;p&gt;You do this by manually generating a cryptographic hash that contains your nonce, then passing that into the &lt;strong&gt;web3.eth.accounts.recover&lt;/strong&gt; function along with the signature you generated in the previous step, all going well this will then return the public address of the account that signed the transaction.&lt;/p&gt;

&lt;p&gt;If this returned address matches the address that you sent from the Front End to the Back End, then we can consider the public address of the account that was received by the Back End to be sent with a valid signature and it can now have AWS credentials generated for it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ycI-Smn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/678/1%2AqcHpQAwhStENiNtAz7-v2A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ycI-Smn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/678/1%2AqcHpQAwhStENiNtAz7-v2A.jpeg" alt="" width="678" height="501"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const sigValidated = await validateSig(nonce, signature, address);

const validateSig = async (nonce, signature, address) =&amp;gt; {
    const message = `Welcome message, nonce: ${nonce}`;
    const hash = web3.utils.sha3(message);
    const signing_address = await web3.eth.accounts.recover(hash, signature);
    return signing_address.toLowerCase() === address.toLowerCase();
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we go any further, since we have validated a user with this nonce — it has served its purpose and we will now generate a new nonce in the database for that user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await updateNonce(address, nonce);

const updateNonce = async (address, oldNonce) =&amp;gt; {
    const nonce = crypto.randomBytes(16).toString("hex");

    const putParams = {
      Item: {
        address: address,
        nonce: nonce,
      },
      TableName: process.env.nonce_table_id,
    };

    const setData = () =&amp;gt; dynamoDb.put(putParams).promise();
    await module.exports.deleteData(address, oldNonce);
    return await setData();
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. Generate / retrieve Cognito Identity
&lt;/h4&gt;

&lt;p&gt;Since we have validated a genuine user, we now want to generate access credentials for that user.&lt;/p&gt;

&lt;p&gt;We do this by retrieving that public address’s Amazon Cognito Identity, if one does not exist then we will generate one for them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6c_gg-qq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/788/1%2Az3bvmnX06kl1U6C9wtbiVQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6c_gg-qq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/788/1%2Az3bvmnX06kl1U6C9wtbiVQ.jpeg" alt="" width="788" height="793"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { IdentityId: identityId, Token: token } = await getIdToken(address);

const getIdToken = (address) =&amp;gt; {
    const param = {
      IdentityPoolId: process.env.cognito_identity_pool_id,
      Logins: {},
    };
    param.Logins[process.env.domain_name] = address;
    return cognitoidentity.getOpenIdTokenForDeveloperIdentity(param).promise();
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  7. Generate Cognito Identity credentials
&lt;/h4&gt;

&lt;p&gt;Using that Amazon Cognito Identity, we will now using AWS Security Token Service (STS) to generate a temporary set of Identity Access Management (IAM) credentials and then return these to the Front End.&lt;/p&gt;

&lt;p&gt;Important to note, is you must ensure that the permissions of these temporarily generated credentials are limited in scope to your usecase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SJFZGoOx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AzrnKmbw-DPsmSMBvVq69Nw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SJFZGoOx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AzrnKmbw-DPsmSMBvVq69Nw.jpeg" alt="" width="800" height="488"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { Credentials: credentials } = await getCredentials(
      identityId,
      token
);

const getCredentials = (identityId, cognitoOpenIdToken) =&amp;gt; {
    const params = {
      IdentityId: identityId,
      Logins: {},
    };
    params.Logins["cognito-identity.amazonaws.com"] = cognitoOpenIdToken;
    return cognitoidentity.getCredentialsForIdentity(params).promise();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using those credentials for Front End and Back End Auth
&lt;/h3&gt;

&lt;p&gt;Now that we’ve generated credentials for a valid user and returned them to the Front End — we can now grant that user access to our DApp and to our Back End.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--27DMSrKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AZCz6TSlxoxVaUgxbNcnmZA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--27DMSrKy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AZCz6TSlxoxVaUgxbNcnmZA.jpeg" alt="" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Front End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the Front End receives the credentials from the login process, it will then store these credentials in local storage and generate an AWS Client.&lt;/p&gt;

&lt;p&gt;This AWS Client is then used to make a request to the Back End, with a signed url that is generated with the temporary AWS IAM credentials.&lt;/p&gt;

&lt;p&gt;All going well, the result of this API call is logged in the browsers’ console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {
    AccessKeyId, SecretKey, SessionToken, Expiration
} = await login(nonce, signature, address);

if (AccessKeyId &amp;amp;&amp;amp; SecretKey &amp;amp;&amp;amp; SessionToken &amp;amp;&amp;amp; Expiration) {
    const authenticatedUser = authenticateUser(
      address,
      AccessKeyId,
      SecretKey,
      SessionToken,
      Expiration
    );
    const request = await authenticatedUser.sign(`${httpApiURL}/signup/testData`, {
        method: "GET",
    });
    const response = await fetch(request);
    console.log(await response.json());
}

const authenticateUser = (
  address,
  accessKeyId,
  secretAccessKey,
  sessionToken,
  expiration
) =&amp;gt; {
  localStorage.setItem("address", JSON.stringify(address));
  localStorage.setItem("sessionToken", JSON.stringify(sessionToken));
  localStorage.setItem("accessKeyId", JSON.stringify(accessKeyId));
  localStorage.setItem("secretAccessKey", JSON.stringify(secretAccessKey));
  localStorage.setItem("expiration", JSON.stringify(expiration));

  return new AwsClient({
    accessKeyId,
    secretAccessKey,
    sessionToken,
    region: process.env.region,
    service: "execute-api",
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Back End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From a Back End perspective, I will demonstrate how this works with Lambda functions in Serverless Framework.&lt;/p&gt;

&lt;p&gt;The yaml file that is used to configure the lambda function is set to be an http endpoint, expecting IAM credentials as an authorizer.&lt;/p&gt;

&lt;p&gt;The lambda function itself just returns a simple string (assuming the credentials used in generating the signed url are correct).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// lambda.yml

testData:
  handler: testData/index.handler
events:
  - http:
      path: signup/testData
      method: get
      authorizer: aws_iam
      cors: true

// index.js

"use strict";

const headers = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Credentials": true,
};
module.exports.handler = async (event) =&amp;gt; {
  return {
    headers,
    statusCode: 200,
    body: JSON.stringify(
      "Hello!! This is test data that you are pulling from the Back End, based on your authenticated wallet!"
    ),
  };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;SO!! Those are the practical steps to enabling Web3 Auth using Metamask and AWS!&lt;/p&gt;

&lt;p&gt;There was a lot in there, I hope it all made sense and was helpful!!&lt;/p&gt;

&lt;p&gt;If you want to have a play with a template that has all of this working ‘out-of-the-box’, &lt;a href="https://jamesmiller.blog/wrapperjs-eth-auth-template/"&gt;here is a link for you to explore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the meantime, have fun :D&lt;/p&gt;




</description>
      <category>aws</category>
      <category>web3</category>
      <category>ethereum</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>How to set up Auth with Metamask using Amazon Cognito (part 1)</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Tue, 20 Jun 2023 20:11:34 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1-de</link>
      <guid>https://forem.com/jamesmillerblog/how-to-set-up-auth-with-metamask-using-amazon-cognito-part-1-de</guid>
      <description>&lt;h3&gt;
  
  
  Implement Auth in your DApp using Web3.js, Metamask, Amazon Cognito and serverless technologies!!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2XQ5o46g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AVKmLfa8fRCyw9beLjX9SmA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2XQ5o46g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AVKmLfa8fRCyw9beLjX9SmA.jpeg" alt="" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this series of posts, I’ll explain how to set up Amazon Cognito with the rest of your Serverless infrastructure to Auth users using their Metamask wallet from within your Decentralised Application (DApp)!&lt;/p&gt;

&lt;p&gt;This first post will be more about the concepts of how everything works, as always I’ll do my best to simplify and make everything easy to understand :)&lt;/p&gt;

&lt;p&gt;The following posts in the series will then show code samples to demonstrate practically how to build these concepts.&lt;/p&gt;

&lt;p&gt;This post builds on my previous series on &lt;a href="https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1/"&gt;what Auth with Amazon Cognito is and how to implement it&lt;/a&gt;, I won’t be re-explaining those points in this post so feel free to read that before jumping into this ( &lt;strong&gt;trust me it will help!!&lt;/strong&gt; ) :D&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Metamask?
&lt;/h3&gt;

&lt;p&gt;Metamask is a crypto wallet that is used to interact with DApps and send or receive money, through the use of a browser extension / mobile app.&lt;/p&gt;

&lt;p&gt;I may do a further explanation of what a crypto wallet is in a future post, for now the core details you need to understand in order to follow along with the concepts in this post — are that once you’ve set up a Metamask wallet (e.g in your desktop web browser) you will then be able to create accounts.&lt;/p&gt;

&lt;p&gt;When an account is created, ‘keys’ are also generated:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A public key&lt;/strong&gt; is an address that identifies you and allows&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;people to send you money and allows them to also identify payments sent from you&lt;/li&gt;
&lt;li&gt;your transactions to be searchable on public blockchains like Ethereum via their block explorer&lt;/li&gt;
&lt;li&gt;you to be identified on dApps &amp;amp; interact with their smart contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;A private key&lt;/strong&gt; is kind of like a password for that account which must be kept secret, this can be used to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;restore (or import) that account into a wallet&lt;/li&gt;
&lt;li&gt;approve transactions / interactions with DApps through the process of ‘signing’&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Demo
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8N7orfGR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2As90DAuSkcGkSGhfDbq9sow.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8N7orfGR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2As90DAuSkcGkSGhfDbq9sow.gif" alt="" width="600" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In practice, the process of logging into and interacting with Smart Contracts on a DApp from a user’s perspective is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User enters the password in order to open the Metamask Browser Extension&lt;/li&gt;
&lt;li&gt;User signs a cryptographically generated message, which interacts with the Ethereum Blockchain and Amazon Cognito to authenticate and authorise your access to the dApp&lt;/li&gt;
&lt;li&gt;Once access is granted, you can then interact with the Smart Contracts on the DApp by signing further transactions!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to try this out, feel free to check out &lt;a href="https://jamesmiller.blog/wrapperjs-eth-auth-template/"&gt;this Wrapper.js template&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Oversimplified explanation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UHFuzHai--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Agj9sOF2_kIriStyDax671A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UHFuzHai--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2Agj9sOF2_kIriStyDax671A.jpeg" alt="" width="800" height="1051"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oversimplifying the Auth process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You login to Metamask and sign a transaction that generates a cryptographic message that could only be generated if you have access to the account, this is visualised as the &lt;strong&gt;green key&lt;/strong&gt; on the left.&lt;/li&gt;
&lt;li&gt;This cryptographic message is sent to an API, which goes through a bunch of Auth processes — if successful then Amazon Cognito will generate a special token that grants access to the rest of the infrastructure — this token is visualised as the &lt;strong&gt;orange key&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The theory of Amazon Cognito Auth with Ethereum
&lt;/h3&gt;

&lt;p&gt;Implementing Auth with Ethereum and Amazon Cognito within a DApp can be done in different ways, here is how I personally tend to go about it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tech stack to implement Auth with Cognito and Ethereum
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SQGg0tWL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AVkmxJJcXNNDYUJvDEt-LGQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SQGg0tWL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AVkmxJJcXNNDYUJvDEt-LGQ.jpeg" alt="" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use Terraform to create all cloud resources except for Lambda functions, which are created and managed by Serverless Framework.&lt;/p&gt;

&lt;p&gt;I use Solidity for smart contract development, Hardhat for helper functions that speed up localhost development + deployment, Web3.js for interacting with the Ethereum Network and with Metamask.&lt;/p&gt;

&lt;p&gt;All environment variables required to interact with Amazon Cognito and Metamask / Ethereum are passed to the React.js Front End created using Next.js.&lt;/p&gt;

&lt;h4&gt;
  
  
  App flow for Cognito Auth with Ethereum
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xle5GAK1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AxIrdVGpfl05zpSQivyl7KA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xle5GAK1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AxIrdVGpfl05zpSQivyl7KA.jpeg" alt="" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how it looks in an app flow format:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user starts by visiting the DApp (a website) and clicking on the log in button to allow the DApp to request the users’s account address as a public key from Meta Mask.&lt;/li&gt;
&lt;li&gt;The DApp will then send this public key to the API and check if a nonce (randomly generated cryptographic string) exists for this key, it will generate a nonce if not — then return this nonce back to the DApp.&lt;/li&gt;
&lt;li&gt;The user signs a message that contains the nonce and a signature is created&lt;/li&gt;
&lt;li&gt;The Dapp sends this signature and public address to the API&lt;/li&gt;
&lt;li&gt;The API runs logic to validate that the nonce that is provided in the signature is the same that is in the database, it also checks that the provided address is the same as the address that created the signature&lt;/li&gt;
&lt;li&gt;Assuming this validation is successful, this will retrieve that public address’s Cognito Identity — if one does not exist then one will be created&lt;/li&gt;
&lt;li&gt;Temporary AWS credentials will then be generated for that Cognito Identity and returned to the DApp&lt;/li&gt;
&lt;li&gt;Using these Credentials, the user can then access the Front End and retrieve data from the Back End (using the AWS Credentials for Auth in retrieving appropriate data), the user can also interact with the Smart Contracts!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There was a lot of information squeezed into this post, I hope it helped to show how Auth can be done with Amazon Cognito and Ethereum!&lt;/p&gt;

&lt;p&gt;There were a few sites that I found online that helped me understand these concepts, the most useful one I’d like to to note these here &lt;a href="https://hackernoon.com/how-to-integrate-amazon-cognito-with-ethereum-blockchain-a-step-by-step-guide-922235b2"&gt;Yi Ai’s post on Hackernoon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want an ‘out-of-the-box’ implementation of Cognito Ethereum Auth, I have created a &lt;a href="https://jamesmiller.blog/wrapperjs-eth-auth-template/"&gt;template in wrapperjs&lt;/a&gt; that you can download and use :D&lt;/p&gt;

&lt;p&gt;Hope this is all helpful, have fun!&lt;/p&gt;




</description>
      <category>web3</category>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>crypto</category>
    </item>
    <item>
      <title>How to add Amazon Cognito Auth to a Web App (part 4)</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sun, 23 Apr 2023 10:24:49 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-4-4mm7</link>
      <guid>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-4-4mm7</guid>
      <description>&lt;h3&gt;
  
  
  Looking at the NodeJS logic that is run in lambda functions to power the Back End of Amazon Cognito Authentication.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3q5Z9bBF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AgRr0OnvA4uvlXHOIl_0xFQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3q5Z9bBF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AgRr0OnvA4uvlXHOIl_0xFQ.jpeg" alt="" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;In my &lt;a href="https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-3/"&gt;last post&lt;/a&gt;, I explained how Amazon Cognito is integrated into the Front End, to allow content to become visible to users that have gone through the Auth process as shown in the below demo 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lQk1Ha9R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2AZwnJBv07kegP57Pn0q7ysg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lQk1Ha9R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2AZwnJBv07kegP57Pn0q7ysg.gif" alt="" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I’ll explain how this same process can be implemented in the Back End.&lt;/p&gt;

&lt;p&gt;If you look at the right side of the above gif, you will see an open console log. Please note the ‘Hello World!! :D‘ message that appears in the logs once the user has successfully signed in.&lt;/p&gt;

&lt;p&gt;This message is returned to users, that have logged into the Front End and then gone through the Auth process on the Back End.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Cognito Auth on the Back End
&lt;/h3&gt;

&lt;p&gt;I have a &lt;a href="https://jamesmiller.blog/wrapperjs-auth-template/"&gt;Wrapper.js template&lt;/a&gt; that you can use to spin up an implementation of Cognito, here are some highlight files from that template:&lt;/p&gt;

&lt;h4&gt;
  
  
  serverless/serverless.common.yml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service: ${file(./../../../serverless.env.json):service_name}-${file(./../../../serverless.env.json):stage}

provider:
  environment: ${file(./../../../serverless.env.json)}
  name: aws
  region: ${file(./../../../serverless.env.json):region}
  runtime: nodejs12.x
  stage: ${file(./../../../serverless.env.json):stage}
  apiGateway: # Optional API Gateway global config
    restApiId: ${file(./../../../serverless.env.json):api_gateway_rest_api_id} # REST API resource ID. Default is generated by the framework
    restApiRootResourceId: ${file(./../../../serverless.env.json):api_gateway_root_resource_id} # Root resource ID, represent as / path
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:Query
            - dynamodb:Scan
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
          Resource: "*"
        - Effect: Allow
          Action:
            - "execute-api:ManageConnections"
          Resource:
            - "arn:aws:execute-api:*:*:**/@connections/*"

custom:
  apiAuthorizer:
    id : ${file(./../../../serverless.env.json):cognito_authorizer}
  serverless-offline:
    host: '0.0.0.0'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Serverless Framework in WrapperJS is organised into HTTP and Websocket services, each service has its own yaml configuration.&lt;/p&gt;

&lt;p&gt;Variables that are exported from Terraform (the cognito details) are passed to the above file, which the individual services can then reference.&lt;/p&gt;

&lt;p&gt;These variables are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;restApiId&lt;/strong&gt; : the ID of the API Gateway that Serverless Framework should deploy lambda end points to&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;restApiIdRootResourceId&lt;/strong&gt; : the root path of that API Gateway, that all lambda endpoints will be added to&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;apiAuthorizer&lt;/strong&gt; : an API Authorizer that was created by Terraform from the ARN of the Cognito User Pool&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;serverless/services/http/users/serverless.yml&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service: ${file(./../../../serverless.common.yml):service}-${self:custom.service}
useDotenv: true

provider: ${self:custom.common.provider}

functions:
  - ${file(userData/lambda.yml)}

plugins:
  - serverless-offline

custom:
  service: users-service
  common: ${file(./../../../serverless.common.yml)}
  apiAuthorizer: ${self:custom.common.custom.apiAuthorizer.id}
  serverless-offline: ${self:custom.common.custom.serverless-offline}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In lines 4 and 15 of the Users service’s yaml (our demo service), you can see the configurations declared in the previous file being set in this service.&lt;/p&gt;

&lt;h4&gt;
  
  
  serverless/services/http/users/userData/lambda.yml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;getUserData:
handler: userData/index.handler
events:
  - http:
      path: users/data
      method: get
      integration: lambda
      authorizer:
        type: COGNITO_USER_POOLS
        authorizerId: ${self:custom.apiAuthorizer}
      cors:
        origin: '*'
        headers: # &amp;lt;-- Specify allowed headers
          - Content-Type
          - Authorization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration for the users/data endpoint (our test endpoint) is configured to return data if the Front End provides the authorizer that has been defined in lines 8–10.&lt;/p&gt;

&lt;h4&gt;
  
  
  serverless/services/http/users/userData/index.js
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

module.exports.handler = async(event, context, callback) =&amp;gt; {
  const response = {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true
    },
    body: 'Hello World!! :D'
  };
  callback(null, response);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, this is the lambda that is executed when the end point has been called and the appropriate authorisation is provided.&lt;/p&gt;

&lt;p&gt;The function returns a body of ‘Hello World!! :D‘.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So that is it, the end of the 4 part tutorial series!!!!&lt;/p&gt;

&lt;p&gt;I hope this has been helpful for you in understanding how to use Amazon Cognito on the Back End for Lambda functions in Serverless Framework.&lt;/p&gt;

&lt;p&gt;Feel free to use the template I created for Wrapper.js to spin this up and give it a go!!&lt;/p&gt;

&lt;p&gt;In the meantime, go build cool stuff and have fun :D&lt;/p&gt;




</description>
      <category>serverlessframework</category>
      <category>amazonwebservices</category>
      <category>amazoncognito</category>
      <category>serverless</category>
    </item>
    <item>
      <title>How to add Amazon Cognito Auth to a Web App (part 3)</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sat, 25 Mar 2023 17:13:30 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-3-4baf</link>
      <guid>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-3-4baf</guid>
      <description>&lt;p&gt;Understanding the Next.JS code samples that creates the User Interface for an Amazon Cognito Authenticated Web App.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kWuVqxlb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AXwOhS2e-Uo0ysIPf3SDVTA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kWuVqxlb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AXwOhS2e-Uo0ysIPf3SDVTA.jpeg" alt="header" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;In my &lt;a href="https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-2/"&gt;last post&lt;/a&gt;, I explained how Terraform was being used to create the cloud infrastructure that enables Amazon Cognito to be implemented in the below demo 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lQk1Ha9R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2AZwnJBv07kegP57Pn0q7ysg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lQk1Ha9R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/600/1%2AZwnJBv07kegP57Pn0q7ysg.gif" alt="demo gif" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, I will explain how to integrate Amazon Cognito into the Front End of a Web App, so you can see it as visualised above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Cognito Auth on the Front End
&lt;/h3&gt;

&lt;p&gt;I have a &lt;a href="https://jamesmiller.blog/wrapperjs-auth-template/"&gt;Wrapper.js template&lt;/a&gt; that you can use to spin up an implementation of Cognito, here are some highlight files from that template:&lt;/p&gt;

&lt;p&gt;In this example, I’ll show you a Next.js React App, that uses the Amazon Amplify library to authenticate the Front End with Amazon Cognito.&lt;/p&gt;

&lt;p&gt;I recommend reading the &lt;a href="https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1/"&gt;first post in this series&lt;/a&gt; before continuing, so you understand the concept of what is being implemented!&lt;/p&gt;

&lt;h4&gt;
  
  
  components/Cognito/utils.js
&lt;/h4&gt;

&lt;p&gt;Let’s start by showing how the variables that were exported from Terraform integrate into the Front End.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Amplify, { Auth } from 'aws-amplify';

export function setupAmplify() {
    Amplify.configure({
        Auth: {
            // REQUIRED only for Federated Authentication - Amazon Cognito Identity Pool ID
            identityPoolId: process.env.cognito_identity_pool_id,
            // REQUIRED - Amazon Cognito Region
            region: process.env.region,
            // OPTIONAL - Amazon Cognito User Pool ID
            userPoolId: process.env.cognito_user_pool_id,
            userPoolWebClientId: process.env.cognito_user_pool_client_id
        }
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the following variables being set up in the AWS Amplify library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;identityPoolId&lt;/strong&gt; : the ID for the identity pool that is used to authorise access to other AWS services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;region&lt;/strong&gt; : the region for the Amazon Cognito resource&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;userPoolId&lt;/strong&gt; : the ID for the user pool where that allows users to create credentials that can be authenticated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;userPoolWebClientId&lt;/strong&gt; : the ID that is assigned from Cognito to our app, to allow it to access the User Pool&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  pages/_app.js
&lt;/h4&gt;

&lt;p&gt;At the core of the application is _app.js, which executes logic each time a page is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createGlobalStyle } from 'styled-components'
import Cognito from '../components/Cognito'
import { Authenticator } from '@aws-amplify/ui-react';

const GlobalStyle = createGlobalStyle`
    html {
        overflow: hidden;
    }
    *, *:before, *:after {
        box-sizing: inherit;
    }
    body{
        margin: 0;
    }
    :root {
        --amplify-primary-color:lightblue;
        --amplify-primary-tint: #0A3369;
        --amplify-primary-shade:#0A3369;
        --amplify-secondary-color:#0A3369;
        --amplify-secondary-tint:#D00C1B;
        --amplify-secondary-shade:#1F2A37;
        --amplify-tertiary-color:#5d8aff;
        --amplify-tertiary-tint:#7da1ff;
        --amplify-tertiary-shade:#537BE5;
        --amplify-grey:#828282;
        --amplify-light-grey:#c4c4c4;
        --amplify-white:#ffffff;
        --amplify-red:#dd3f5b;
        --amplify-primary-contrast: var(--amplify-white);
        --amplify-secondary-contrast:var(--amplify-white);
        --amplify-tertiary-contrast:var(--amplify-red);
        --amplify-font-family:'Helvetica Neue Light', 'Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif';
        --amplify-text-xxs:0.75rem;
        --amplify-text-xs:0.81rem;
        --amplify-text-sm:0.875rem;
        --amplify-text-md:1rem;
        --amplify-text-lg:1.5rem;
        --amplify-text-xl:2rem;
        --amplify-text-xxl:2.5rem;
      }

    amplify-authenticator {
        justify-content:center;
        align-items: center;
        display: inline-block;
        height: auto;
        --width:400px;
        border: 0px solid;
        color: #0A3369;
        font-size:var(--amplify-text-md);
        --box-shadow:none;
        --container-height:400px;
        --padding:20px;
      }
`

export default function App({ Component, pageProps }) {
    return (
        &amp;lt;Authenticator.Provider&amp;gt;
            &amp;lt;Cognito&amp;gt;
                &amp;lt;GlobalStyle /&amp;gt;
                &amp;lt;Component {...pageProps} /&amp;gt;
            &amp;lt;/Cognito&amp;gt;
        &amp;lt;/Authenticator.Provider&amp;gt;
    )
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, our app checks if a user is authenticated with Cognito (the &lt;strong&gt;App&lt;/strong&gt; function) and renders content based on that users level of authorisation.&lt;/p&gt;

&lt;h4&gt;
  
  
  components/Cognito/index.js
&lt;/h4&gt;

&lt;p&gt;This component contains the core Amazon Cognito logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Auth } from 'aws-amplify';
import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import React, { useEffect } from 'react';
import cognitoStore from './../../stores/cognito';
import userStore from './../../stores/user';
import styled from 'styled-components';
import { setupAmplify } from './utils.js';
import { httpApiURL } from '../../utils';
import axios from 'axios';

setupAmplify();

const getUserData = (cognito) =&amp;gt; axios({
    method: 'post',
    url: `${httpApiURL}/users/data`,
    data: {
      cognito: cognito
    },
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${cognito.jwt}`
    }
}).then((res) =&amp;gt; {
    const data = JSON.parse(res.data.body);
    return data;
}, (error) =&amp;gt; {
    throw new Error(error);
})

const Cognito = (props) =&amp;gt; {
    const { children } = props;
    const { cognito, setCognito, signInState, setSignInState } = cognitoStore();
    const { setUser } = userStore();
    const { user } = useAuthenticator((context) =&amp;gt; [context.user]);

    useEffect(() =&amp;gt; {
        const fetchData = async() =&amp;gt; {
            // Update the document title using the browser API
            if(cognito != '' &amp;amp;&amp;amp; cognito != undefined) {
                try{
                    setUser(await getUserData(cognito));
                } catch (e) {
                    console.log(e);
                }
            }

            if(user != undefined &amp;amp;&amp;amp; cognito == '' &amp;amp;&amp;amp; user.signInUserSession) {
                const {accessToken, idToken} = user.signInUserSession;
                const role = accessToken.payload['cognito:groups'];
                const token = {
                    jwt: idToken.jwtToken,
                    role: (role) ? role[0] : '',
                    username: accessToken.payload.username
                }
                setCognito(token);
            }
        }

        try {
            fetchData();
        }
        catch (e) {
            console.error(e);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [user])

    useEffect(() =&amp;gt; {
        const checkState = async() =&amp;gt; {
            if(signInState == 'signOut') {
                await Auth.signOut();
                setCognito('');
                setSignInState('signedOut');
            }
        }
        checkState();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [signInState]);
    return (
        &amp;lt;&amp;gt;
            {cognito == '' ?
                &amp;lt;AmplifyWrapper&amp;gt;
                    &amp;lt;Authenticator
                        headerText="Enter your details below"
                        submitButtonText="Sign in"
                        usernameAlias="email"
                        hideSignUp
                    &amp;gt;
                    &amp;lt;/Authenticator&amp;gt;
                &amp;lt;/AmplifyWrapper&amp;gt;

            :
                &amp;lt;ContentWrapper&amp;gt;
                    {children}
                &amp;lt;/ContentWrapper&amp;gt;
            }
        &amp;lt;/&amp;gt;
    )
}

const AmplifyWrapper = styled('div')`
    position: absolute;
    top: 50%;
    left: 50%;
    -ms-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
`
const ContentWrapper = styled('div')`
    position: relative;
    z-index: 1;
    width: 100vw;
    height: 100vh;
`

export default Cogn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Diving into the logic of how this Cognito component works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The first useEffect life cycle method&lt;/strong&gt; demonstrates the logic that attempts to make a request to authenticate with Cognito and get the JWT as a response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The returned elements&lt;/strong&gt; demonstrates the logic that renders a log in page if the user is not authenticated and renders the children content if the user is authenticated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The idea being, if a user is not authenticated, they enter their details into the log in form, this will trigger the an attempt to authenticate with Cognito and then to show content upon success.&lt;/p&gt;

&lt;h4&gt;
  
  
  pages/index.js
&lt;/h4&gt;

&lt;p&gt;Finally, we have a component that renders the web page (assuming that the user has passed Cognito Auth).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Head from 'next/head'
import React, { useEffect } from 'react'
import styled from 'styled-components'
import axios from 'axios'

import { httpApiURL } from './../utils'
import Header from '../components/Header'
import cognitoStore from '../stores/cognito'

export default function Home() {
  const { cognito } = cognitoStore();

  useEffect(() =&amp;gt; {
    const getData = async(cognito) =&amp;gt; await axios({
      method: 'get',
      url: `${httpApiURL}/users/data`,
      data: {
        cognito: cognito
      },
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${cognito.jwt}`
      }
    }).then((res) =&amp;gt; {
      console.log(res.data.body)
    }, (error) =&amp;gt; {
      console.log(error);
    })

    getData(cognito);

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Wrapper.js Authentication Example&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;Header /&amp;gt;

      &amp;lt;Img src='/wrapperjs.png' /&amp;gt;
      &amp;lt;P&amp;gt;Authentication template&amp;lt;/P&amp;gt;
    &amp;lt;/&amp;gt;
  )
}

const Img = styled('img')`
  display: block;
  height: 60vh;
  width: auto;
  margin-left:auto;
  margin-right:auto;
  position: relative;
  // min-width: 500px;
`

const P = styled('p')`
  text-align: center;
  font-weight: bold;
`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Talking through this example of how the content that could be shown on the Front End works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The useEffect method&lt;/strong&gt; shows data being loaded from the Back End based on an API request that uses JWT authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The returned elements&lt;/strong&gt; show what the Front End should render if authentication has been successful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;I hope the above is helpful in showing you how to implement Amazon Cognito on the Front End using the Amplify library, to only show content to users that have gone through the Auth process!&lt;/p&gt;

&lt;p&gt;In the next (and last) post in this series, I’ll talk through how to implement Amazon Cognito on the Back End to secure API data.&lt;/p&gt;

&lt;p&gt;In the meantime, have fun 😀&lt;/p&gt;




</description>
      <category>serverless</category>
      <category>react</category>
      <category>amazoncognito</category>
      <category>terraform</category>
    </item>
    <item>
      <title>How to add Amazon Cognito Auth to a Web App (part 2)</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sun, 05 Feb 2023 12:58:24 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-2-4mo0</link>
      <guid>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-2-4mo0</guid>
      <description>&lt;p&gt;Terraform code samples that demonstrate how to set up Amazon Cognito Auth in a Web App.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sikjwi43vs9s1yfkjx6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9sikjwi43vs9s1yfkjx6.jpeg" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://jamesmiller.blog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1/" rel="noopener noreferrer"&gt;last blog post&lt;/a&gt;, I talked through the concepts of how Amazon Cognito works in a Web App.&lt;/p&gt;

&lt;p&gt;In this post, I will explain how Amazon Cognito can be programmatically set up using Terraform — so you can build cool stuff like this 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6qg1i4s2yhvd1cw4fca.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6qg1i4s2yhvd1cw4fca.gif" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Amazon Cognito with Terraform​
&lt;/h3&gt;

&lt;p&gt;I have a &lt;a href="https://jamesmiller.blog/wrapperjs-auth-template/" rel="noopener noreferrer"&gt;Wrapper.js template&lt;/a&gt; that you can use to spin up an implementation of Cognito, here are some highlight files from that template:&lt;/p&gt;

&lt;p&gt;I use Terraform as the tool to create the majority of the cloud infrastructure.&lt;/p&gt;

&lt;h4&gt;
  
  
  main.tf​
&lt;/h4&gt;

&lt;p&gt;This is the core file that creates all the terraform resources, I’ll go deeper into lines 7–12 and 42–52, which are used to create Cognito and the Secrets Manager that is used to pass the Cognito details to both the Back End and Front End.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module "nextjs_app_s3" {
    source = "./modules/s3"
    bucket = var.domain_name
    acl = "public-read"
}

module "acm" {
    source = "./modules/acm"
    root_domain_name = var.root_domain_name
    domain_name = var.domain_name
    region = "us-east-1"
}

module "nextjs_app_cloudfront" {
    source = "./modules/cloudfront"
    hosted_zone_name = var.root_domain_name
    domain_name = var.domain_name
    # lambda_edge_qualified_arn = module.uri_redirect_edge_lambda.qualified_arn
    acm_cert_arn = module.acm.cert_arn
    acm_cert_id = module.acm.cert_id
    s3_website_endpoint = module.nextjs_app_s3.website_endpoint
    default_root_object = "index.html"
}

module "rest_api_gateway" {
    source = "./modules/restApiGateway"
    certificate_arn = module.acm.cert_arn
    root_domain_name = var.root_domain_name
    domain_name = "api.${var.domain_name}"
    certificate_id = module.acm.cert_id
    stage_name = var.stage
    cognito_arn = module.cognito.user_pool_client_arn
}

module "cognito" {
    source = "./modules/cognito"
    domain_name = var.domain_name
    stage_name = var.stage
    service_name = var.service_name
}

module "secrets_manager" {
    source = "./modules/secretsManager"
    service_name = var.service_name
    cognito_identity_pool_id = module.cognito.identity_pool_id
    cognito_user_pool_id = module.cognito.user_pool_id
    cognito_user_pool_client_id = module.cognito.user_pool_client_id
    cognito_user_pool_client_arn = module.cognito.user_pool_client_arn
    cognito_authorizer = module.rest_api_gateway.cognito_authorizer
    api_gateway_rest_api_id = module.rest_api_gateway.rest_api_id
    api_gateway_root_resource_id = module.rest_api_gateway.root_resource_id
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  modules/cognito/main.tf​
&lt;/h4&gt;

&lt;p&gt;This is the file that creates the Amazon Cognito configuration.&lt;/p&gt;

&lt;p&gt;Here you can see how the User Pool (lines 1–3) and Identity Pool (lines 11–20) are configured to allow authenticated users access to aws resources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_cognito_user_pool" "this" {
  name = "${var.service_name}-${var.stage_name}-pool"
}

resource "aws_cognito_user_pool_client" "this" {
  name = "${var.service_name}-${var.stage_name}-client"

  user_pool_id = aws_cognito_user_pool.this.id
}

resource "aws_cognito_identity_pool" "this" {
  identity_pool_name = "${var.service_name}_${var.stage_name}_identity_pool"
  allow_unauthenticated_identities = false

  cognito_identity_providers {
    client_id = aws_cognito_user_pool_client.this.id
    provider_name = "cognito-idp.eu-west-2.amazonaws.com/${aws_cognito_user_pool.this.id}"
    server_side_token_check = false
  }
}

resource "aws_iam_role" "authenticated" {
  name = "${var.service_name}-${var.stage_name}-cognito_authenticated"

  assume_role_policy = &amp;lt;&amp;lt;EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "cognito-identity.amazonaws.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "cognito-identity.amazonaws.com:aud": "${aws_cognito_identity_pool.this.id}"
        },
        "ForAnyValue:StringLike": {
          "cognito-identity.amazonaws.com:amr": "authenticated"
        }
      }
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "authenticated" {
  name = "${var.service_name}-${var.stage_name}-authenticated_policy"
  role = aws_iam_role.authenticated.id

  policy = &amp;lt;&amp;lt;EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "mobileanalytics:PutEvents",
        "cognito-sync:*",
        "cognito-identity:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}

resource "aws_cognito_identity_pool_roles_attachment" "this" {
  identity_pool_id = aws_cognito_identity_pool.this.id

  roles = {
    authenticated = aws_iam_role.authenticated.arn
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  modules/cognito/outputs.tf
&lt;/h4&gt;

&lt;p&gt;After creating your Amazon Cognito configuration, the outputs file allows you to export variables to be used in other parts of the Terraform configuration.&lt;/p&gt;

&lt;p&gt;This outputs file generates 4 variables that will enable the Front End and Back End to interact with Amazon Cognito to generate and use JSON Web Tokens (JWT’s).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output "identity_pool_id" {
  value = aws_cognito_identity_pool.this.id
}

output "user_pool_id" {
  value = aws_cognito_user_pool.this.id
}

output "user_pool_client_id" {
  value = aws_cognito_user_pool_client.this.id
}

output "user_pool_client_arn" {
  value = aws_cognito_user_pool.this.arn
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  modules/restApiGateway/authorizer.tf
&lt;/h4&gt;

&lt;p&gt;One of the important resources that will allow the Back End to leverage Cognito Auth is an API Gateway Authorizer.&lt;/p&gt;

&lt;p&gt;Here you can see one the 4 variables, the user pool arn — that is used to create an authorizer for the REST API Gateway, that is configured to interact with the Cognito User Pool we just generated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_api_gateway_authorizer" "this" {
  name = var.domain_name
  rest_api_id = aws_api_gateway_rest_api.this.id
  type = "COGNITO_USER_POOLS"
  provider_arns = [var.cognito_arn]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  modules/restApiGateway/outputs.tf
&lt;/h4&gt;

&lt;p&gt;Once this API Gateway Authorizer is created, we then need to output some configuration variables so that it can be saved into an AWS Secret (next step) and retrieved by Serverless Framework for the Back End Auth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output "root_resource_id" {
  value = aws_api_gateway_rest_api.this.root_resource_id
}

output "rest_api_id" {
  value = aws_api_gateway_rest_api.this.id
}

output "cognito_authorizer" {
  value = aws_api_gateway_authorizer.this.id
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  modules/secretsManager/main.tf
&lt;/h4&gt;

&lt;p&gt;Lastly, we create an AWS Secret with all the exported outputs, so that the Front End and Back End have the configurations they need in order to interact with Amazon Cognito.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_secretsmanager_secret" "this" {
  # Fill in the name you gave to your secret
  name = "${var.service_name}-tf"
  description = "Generated by Terraform for ${var.service_name}"

  recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "this" {
  secret_id = aws_secretsmanager_secret.this.id
  secret_string = jsonencode(local.secrets)
}

locals {
  secrets = merge(
    var.secrets,
    {
      next_cognito_identity_pool_id = "${var.cognito_identity_pool_id}"
      next_sls_cognito_user_pool_id = "${var.cognito_user_pool_id}"
      next_sls_cognito_user_pool_client_id = "${var.cognito_user_pool_client_id}"
      sls_cognito_authorizer = "${var.cognito_authorizer}"
      sls_cognito_user_pool_client_arn = "${var.cognito_user_pool_client_arn}"
      sls_api_gateway_root_resource_id = "${var.api_gateway_root_resource_id}"
      sls_api_gateway_rest_api_id = "${var.api_gateway_rest_api_id}"
    },
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So this is how you use Terraform to create the Amazon Cognito resource and export its variables into a secret that can be used on the Front End and the Back End.&lt;/p&gt;

&lt;p&gt;For the full codebase, check out &lt;a href="https://github.com/JamesMillerBlog/wrapper.js/tree/main/templates/auth" rel="noopener noreferrer"&gt;this link&lt;/a&gt; and see &lt;a href="https://jamesmiller.blog/wrapperjs-auth-template/" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt; for further details on the &lt;a href="https://jamesmiller.blog/wrapperjs/" rel="noopener noreferrer"&gt;wrapper.js&lt;/a&gt; template!&lt;/p&gt;

&lt;p&gt;In the next post, I’ll talk through how to use these secrets on the Front End!&lt;/p&gt;

&lt;p&gt;Until then, I hope this has been helpful and have fun :D&lt;/p&gt;




</description>
      <category>amazoncognito</category>
      <category>amazonwebservices</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>How to add Amazon Cognito Auth to a Web App (part 1)</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Mon, 02 Jan 2023 22:03:44 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1-572d</link>
      <guid>https://forem.com/jamesmillerblog/how-to-add-amazon-cognito-auth-to-a-web-app-part-1-572d</guid>
      <description>&lt;p&gt;Using Terraform, Serverless Framework and Next.JS to implement Auth in a Web App.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cus4yhmh9gycybhbehb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cus4yhmh9gycybhbehb.jpeg" width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this series of posts, I’ll explain how to set up Amazon Cognito in your app.&lt;/p&gt;

&lt;p&gt;This first post will explain the conceptual aspects of what Amazon Cognito is, why it is useful and how it works.&lt;/p&gt;

&lt;p&gt;I’ll try my best to simplify the concepts, to help make it quicker to digest :)&lt;/p&gt;

&lt;p&gt;The next posts will in this series will be more practical, focussing on code samples etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Amazon Cognito?
&lt;/h3&gt;

&lt;p&gt;Amazon Cognito is an AWS service that provides web apps with the ability to provide authentication and authorisation of users to content.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication is the process of proving the users identity by assessing their credentials (e.g look at someones passport/id card photo, then looking at that persons face to see if they match)&lt;/li&gt;
&lt;li&gt;Authorisation is the mechanism to understand what level of access that person should have (e.g the id card may permit that person into the ‘lobby’ but not into the ‘top secret vault’)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also comes with a bunch of other useful features such as user management, out of the box sign-up / sign-in options and various third party integrations such as Facebook / Google etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  Demo
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6qg1i4s2yhvd1cw4fca.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6qg1i4s2yhvd1cw4fca.gif" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In practice, this process looks something like the above GIF&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User provides this email address, along with a password to the login to the web app ( &lt;strong&gt;authentication&lt;/strong&gt; of their credentials)&lt;/li&gt;
&lt;li&gt;User can see content based on what permissions are assigned to them ( &lt;strong&gt;authorisation&lt;/strong&gt; of that user)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to try this out, feel free to have a &lt;a href="https://jamesmiller.blog/wrapperjs-auth-template/" rel="noopener noreferrer"&gt;look at this Wrapper.js template&lt;/a&gt;!&lt;/p&gt;

&lt;h4&gt;
  
  
  Oversimplified explanation
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8uqyuba0hu7h31pokgub.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8uqyuba0hu7h31pokgub.jpeg" width="800" height="951"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In very oversimplified terms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user enters their log in credentials into the sign in form on the Front End, which is then assessed by Amazon Cognito (the green key shown above).&lt;/li&gt;
&lt;li&gt;If the provided credentials correct, Amazon Cognito with authenticate that users identity and provide a JWT token as a response, allowing them access to view the Front End content based on the level of authorisation that is assigned to that user.&lt;/li&gt;
&lt;li&gt;This same JWT token can then be used to authorise what data is allowed to be provided from the Back End to a user visiting the website on the Front End (the orange key shown above).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a user provides invalid credentials when attempting to log in, then authentication fails and they are not authorised to view any content (the red key above).&lt;/p&gt;

&lt;h3&gt;
  
  
  The theory of Amazon Cognito Auth
&lt;/h3&gt;

&lt;p&gt;Implementing Amazon Cognito within a web app can be done in different ways, here is how I tend to go about it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tech stack to implement Authentication with Cognito
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdvhp9ypsg7aeii4ufnw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdvhp9ypsg7aeii4ufnw.jpeg" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use Terraform to create all cloud resources except for Lambda functions, which are created and managed by Serverless Framework.&lt;/p&gt;

&lt;p&gt;All environment variables required to interact with Amazon Cognito are passed to the React.js Front End created using Next.js.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0shhnlx6nm8n88p8hsx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0shhnlx6nm8n88p8hsx.jpeg" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From an app flow perspective, it works something as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User opens the web app on their device (e.g macbook pro), the statically hosted S3 website is delivered through Cloudfront CDN.&lt;/li&gt;
&lt;li&gt;When the user attempts to log in to their device, a Front End library called AWS Amplify is used to help make the authentication process quicker. If successful, Cognito will return a JWT token to the Front End.&lt;/li&gt;
&lt;li&gt;This JWT token provides access to the Front End and is then used to make requests to the API Gateway which runs lambda functions that execute the Back End logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So that sums up the concept for how Amazon Cognito works and how I tend to go about implementing it.&lt;/p&gt;

&lt;p&gt;If you want an ‘out-of-the-box’ implementation of it, I have a &lt;a href="https://jamesmiller.blog/wrapperjs-auth-template/" rel="noopener noreferrer"&gt;template created in wrapper.js&lt;/a&gt; that you can download and use :)&lt;/p&gt;

&lt;p&gt;Hope this is all helpful, have fun!!&lt;/p&gt;




</description>
      <category>amazoncognito</category>
      <category>react</category>
      <category>terraform</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>How to configure Github Actions</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sun, 11 Dec 2022 14:03:07 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-configure-github-actions-fnd</link>
      <guid>https://forem.com/jamesmillerblog/how-to-configure-github-actions-fnd</guid>
      <description>&lt;p&gt;A post about what Github Actions is, why it is useful, how to get it working on your project!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iosamk3t1y4rv1t0ln3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iosamk3t1y4rv1t0ln3.jpeg" width="800" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;I’ve recently been playing with &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt; and have been able to now integrate it with &lt;a href="https://jamesmiller.blog/wrapperjs/" rel="noopener noreferrer"&gt;Wrapper.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is super cool and opens up a load of exciting possibilities for speeding up deployment of apps by automating the wrapper script functions!&lt;/p&gt;

&lt;p&gt;In this post, I’ll explain what Github Actions is and how it works, so you can then understand how it is implemented in &lt;a href="https://jamesmiller.blog/wrapperjs-getting-started/" rel="noopener noreferrer"&gt;Wrapper.js&lt;/a&gt; (so you can take what you like and implement it in your own projects) :D&lt;/p&gt;

&lt;h4&gt;
  
  
  What is Github Actions
&lt;/h4&gt;

&lt;p&gt;Github Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your tests, build and deployment pipeline.&lt;/p&gt;

&lt;p&gt;In simple terms, it allows developers to automate testing and deployment of their apps, by simply pushing updates to their code repositories :)&lt;/p&gt;

&lt;h4&gt;
  
  
  Why is Github Actions useful
&lt;/h4&gt;

&lt;p&gt;I’ve now integrated Github Actions with &lt;a href="https://jamesmiller.blog/wrapperjs/" rel="noopener noreferrer"&gt;Wrapper.js&lt;/a&gt;, it’s useful because if you want to deploy an app (e.g a &lt;a href="https://jamesmiller.blog/wrapperjs-webxr-template/" rel="noopener noreferrer"&gt;WebXR template&lt;/a&gt;) like this 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47cy6zddrqwqw0uy7gob.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47cy6zddrqwqw0uy7gob.gif" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can automate this deployment and better yet, spin up multiple environments (dev / stage / prod etc), all by just pushing code to your git repository — like this 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1bknx1axhn6xh884wvu.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn1bknx1axhn6xh884wvu.jpeg" width="800" height="789"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the developers perspective, they develop a feature and commit their work to a branch in their code base — the pipeline takes are of deploying it into an environment that can then be viewed on its own url!&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;In the below steps, I’ll show how it is all pieced together.&lt;/p&gt;

&lt;p&gt;I’ll demonstrate this using a Wrapper.js template as an example, you can extract what you’d like from this into your own set up.&lt;/p&gt;

&lt;h4&gt;
  
  
  Set up the project and pipeline
&lt;/h4&gt;

&lt;p&gt;The first step, is to set up the &lt;a href="https://jamesmiller.blog/wrapperjs-webxr-template/" rel="noopener noreferrer"&gt;Wrapper.js template&lt;/a&gt; and the Github repo for accessing your AWS account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding AWS and Secret details to Github&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a Github repository for your project and then add your AWS credentials to the Repository Secrets.&lt;/p&gt;

&lt;p&gt;You find this under ‘Settings’ &amp;gt; ‘Secrets’ &amp;gt; ‘Actions’, then click on the green ‘New repository secret’ button to add your AWS_REGION, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY details.&lt;/p&gt;

&lt;p&gt;Please note, you get these details from your AWS IAM credentials — never share these with anyone or post them online.&lt;/p&gt;

&lt;p&gt;Lastly, add the name of your wrapper.js secret in as a Repository Secret called WRAPPERJS_SECRET_NAME.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx89bfny156yooco5zvre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx89bfny156yooco5zvre.png" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set up Wrapper.js Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up your project, by following the steps outlined on &lt;a href="https://jamesmiller.blog/wrapperjs-getting-started/" rel="noopener noreferrer"&gt;this instruction page&lt;/a&gt;, until you get to the end of step 3.&lt;/p&gt;

&lt;p&gt;Doing this will mean you have chosen a &lt;a href="https://jamesmiller.blog/wrapperjs-webxr-template/" rel="noopener noreferrer"&gt;wrapper.js template&lt;/a&gt; and have used the unwrap CLI command configured your own project settings (domain name etc) that you’d like your app to be deployed with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lt21w2rltypqf8h82b2.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lt21w2rltypqf8h82b2.jpeg" width="800" height="794"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Run the pipeline automations
&lt;/h4&gt;

&lt;p&gt;Now you have the basic project and github repository set up, the next step is to trigger the deployment and watch the automations do their work!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raise a PR and trigger the automations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example, I’ll explain this as if you raised a Pull Request (PR) to the github repository. Please note, this process also works with pushing to master and creating a ‘release’.&lt;/p&gt;

&lt;p&gt;When a PR is raised, the pipeline is activated and several automations happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Github spins up a temporary virtual machine (VM) to run automations&lt;/li&gt;
&lt;li&gt;All necessary libraries (e.g Wrapper.js) are installed onto that VM&lt;/li&gt;
&lt;li&gt;During the setup processs, the AWS Credentials are configured&lt;/li&gt;
&lt;li&gt;Wrapper.js uses this AWS access, to retrieve the secret you configured for your project&lt;/li&gt;
&lt;li&gt;Wrapper.js then attempts to create a duplicate of this secret (if it doesn’t already exist), modifying the values for a PR environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all the above steps complete, the deployment process is ready to deploy your environment, using the newly duplicated Wrapper.js secret!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiux7460d6jpscvei94dn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiux7460d6jpscvei94dn.jpeg" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieve secrets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, Github Actions runs the gobble secrets command to retrieve the duplicated secret and create a temporary .env file&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff11d6iv4awpnm8jdapp9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff11d6iv4awpnm8jdapp9.jpeg" width="800" height="792"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamically create Terraform resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With this .env file (saved as terraform.tfvars.json), it then runs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gobble tf init&lt;/li&gt;
&lt;li&gt;gobble tf plan&lt;/li&gt;
&lt;li&gt;gobble tf apply&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This command essentially is initiating Terraform with the .env values, planning out what resources it is going to generate and then applies its plan to manage the selected cloud resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F680xrn5py2jqttlhz6w0.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F680xrn5py2jqttlhz6w0.jpeg" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrieve secrets from Terraform resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once Terraform has finished running, the Github Actions pipeline then runs the gobble secrets command to retrieve the newly generated secrets file that Terraform has created.&lt;/p&gt;

&lt;p&gt;This updates the locally generated .env file with new details from cloud resources like API Gateway and S3 etc, so the Front End and Back End can use them as part of their set up processes (coming up next).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5l8f4sulfkg6nccu8gu.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5l8f4sulfkg6nccu8gu.jpeg" width="800" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy Back End endpoints to the API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With the new details updated in the local .env file, the Github Actions pipeline then runs gobble sls deploy, which then deploys the lambda endpoints to the API Gateway.&lt;/p&gt;

&lt;p&gt;Like with the previous terraform commands, these gobble commands inject the .env details (API Gateway variables) into the Serverless Framework config files, so that the lambda functions can be deployed to the correct API Gateway endpoints.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5x07i66o13kiq7xtvjc6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5x07i66o13kiq7xtvjc6.jpeg" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy the statically hosted Front End&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lastly, Github Actions then runs gobble next export — which then creates a new static build of a Next.JS app and exports this to the S3 bucket whose details were originally generated from Terraform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ap7vtz58jxuqurdwn9s.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ap7vtz58jxuqurdwn9s.jpeg" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is it, the pipeline has finished automating and the app is deployed! :D&lt;/p&gt;

&lt;h3&gt;
  
  
  Example pipeline config
&lt;/h3&gt;

&lt;p&gt;I hope the above diagrams were useful from a visual perspective to understand what was going on.&lt;/p&gt;

&lt;p&gt;Here is the yaml file, that turns those diagrams into actual useable Github Actions configuration :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/media/351834c2ab277831e662f59c8975cb48/href" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://medium.com/media/351834c2ab277831e662f59c8975cb48/href" rel="noopener noreferrer"&gt;https://medium.com/media/351834c2ab277831e662f59c8975cb48/href&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This has been a long blog post!&lt;/p&gt;

&lt;p&gt;With any luck, you now understand what Github Actions is, why it is useful and how exciting it is that it can automate deployment of your app deployment!!&lt;/p&gt;

&lt;p&gt;If you want to have a play, feel free to take a look at these Wrapper.js templates and have go :D&lt;/p&gt;

&lt;p&gt;In the meantime, have fun making stuff!!&lt;/p&gt;




</description>
      <category>continousintegration</category>
      <category>javascript</category>
      <category>continousdeployment</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Easy win WIFI set up on Ubuntu</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sat, 05 Nov 2022 16:10:07 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/easy-win-wifi-set-up-on-ubuntu-125l</link>
      <guid>https://forem.com/jamesmillerblog/easy-win-wifi-set-up-on-ubuntu-125l</guid>
      <description>&lt;p&gt;Fast method to connect a hard wired device to a wireless network when WIFI adapters aren’t working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5gTkbIzJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1IJk2oeRQdL4gvq3frih_Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5gTkbIzJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1IJk2oeRQdL4gvq3frih_Q.jpeg" alt="" width="880" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve owned a Linux computer for the past 2 years and have LOVED it!&lt;/p&gt;

&lt;p&gt;There are however aspects of Ubuntu that are surprisingly difficult to get working.. like setting up a WIFI connection 🤯&lt;/p&gt;

&lt;p&gt;In this post, I’ll explain how I managed to get my Ubuntu computer set up on a WIFI network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Having tried various different WIFI adapters and installing multiple drivers, I haven’t been able to get any of them to actually work and have instead just relied on a wired Ethernet connection for internet access!!&lt;/p&gt;

&lt;p&gt;If you’re reading this post, you’re probably the same as me — just wanting an ‘easy win’ way to get your computer connected to a wireless network!&lt;/p&gt;

&lt;p&gt;I have found a super easy fix to help get my Linux computer access to WIFI that I wanted to share, so others struggling with this can make use of what I’ve found.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Quick Caveat: this is&lt;/em&gt; &lt;strong&gt;&lt;em&gt;NOT&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;my ideal solution and as with most technical problems there are multiple ways to fix it, but this method works — &lt;/em&gt; &lt;strong&gt;&lt;em&gt;FAST&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Enough talk, lets get into it!!&lt;/p&gt;

&lt;h3&gt;
  
  
  WIFI to Ethernet Adaptor
&lt;/h3&gt;

&lt;p&gt;I used the &lt;a href="https://amzn.to/3zlhbmn"&gt;BrosTrend WiFi Bridge&lt;/a&gt;, that essentially converts the wireless connection into a wired one.&lt;/p&gt;

&lt;p&gt;This means that you can use an Ethernet cable to connect your computer to the WiFi Bridge in order to receive the internet connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9uU2RiDH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/425/1%2AFp-CJZXYd25t3oMPafRcFA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9uU2RiDH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/425/1%2AFp-CJZXYd25t3oMPafRcFA.jpeg" alt="" width="425" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You plug the &lt;a href="https://amzn.to/3zlhbmn"&gt;Wireless Bridge&lt;/a&gt; into a wall socket and it creates a temporary wireless hot spot&lt;/li&gt;
&lt;li&gt;Use your mobile device to connect to the wireless hot spot and follow the instructions to set it to the network you’re looking to connect a wired device to&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://amzn.to/3zlhbmn"&gt;Wifi Bridge&lt;/a&gt; will now act as an extension of your chosen network, you can now use an Ethernet cable to connect your device to the wireless bridge to get an internet connection!!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xHRmxkUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2AFXiI9uCjpA9Nb7XVImtQgA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xHRmxkUq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2AFXiI9uCjpA9Nb7XVImtQgA.jpeg" alt="" width="880" height="397"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Diagram showing how the Wireless Bridge can be used to connect a hard wired device to a wireless network (you can also connect the WiFi Bridge to a network switch if you have multiple hard wired devices to connect)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this blog post, I’ve shown you an alternative and easy-win method for enabling your hard wired devices to get connected to a wireless network via the use of a &lt;a href="https://amzn.to/3zlhbmn"&gt;WiFi Bridge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I need to make an admission in my last closing comments of this post…&lt;/p&gt;

&lt;p&gt;After making the effort to write a blog post about a work around for setting up WIFI on ubuntu, I embarrassingly was able to actually get it working.&lt;/p&gt;

&lt;p&gt;Buy &lt;a href="https://amzn.to/3PRCAus"&gt;this BrosTrend wifi adapter&lt;/a&gt;, then follow the steps mentioned in &lt;a href="https://cdn.shopify.com/s/files/1/0270/1023/6487/files/AC1L_AC3L_AC5L_Linux_Manual_BrosTrend_WiFi_Adapter_v7.pdf?v=1654497605"&gt;their manual&lt;/a&gt;. It might require a bit of fiddling (a few restarts and updates etc), but it should work for you in the end.&lt;/p&gt;

&lt;p&gt;If you go through all those steps and still are not able to get it working, then at least the content in this blog post will be useful for you 😉&lt;/p&gt;

&lt;p&gt;In the meantime, hope this was helpful and have fun 😄&lt;/p&gt;




</description>
      <category>ubuntu</category>
      <category>wifi</category>
      <category>wifibridge</category>
      <category>connectivity</category>
    </item>
    <item>
      <title>Wrapper.js: A New Devops Library</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Sun, 16 Oct 2022 16:49:16 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/wrapperjs-what-why-how-and-where-575e</link>
      <guid>https://forem.com/jamesmillerblog/wrapperjs-what-why-how-and-where-575e</guid>
      <description>&lt;p&gt;Unwrapping the details on Wrapper.js: what it is, why I made it, how it works and where to find out more!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lsHaVKZU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2A5yPOoD8Ylgoevkx93i42Iw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lsHaVKZU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1000/1%2A5yPOoD8Ylgoevkx93i42Iw.jpeg" alt="" width="880" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve recently created documentation for my first open source project called &lt;a href="https://jamesmiller.blog/wrapperjs/"&gt;Wrapper.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post, I’ll briefly explain what it is, why I made it, how it works and where you can learn more about how to use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Wrapper.js
&lt;/h3&gt;

&lt;p&gt;Wrapper.js is an open source configurable wrapper script, that aims to speed up development and deployment of web experiences.&lt;/p&gt;

&lt;p&gt;To start off with, it is released with a couple of ready made templates that let you spin up an out-of-the-box and fully configurable experience (see the &lt;a href="https://jamesmiller.blog/wrapperjs-webxr-template/"&gt;WebXR template&lt;/a&gt; as an example).&lt;/p&gt;

&lt;p&gt;In the future the hope is that, you can choose to configure your app with other libraries and easily spin up projects that use emerging technologies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--10X69dt9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Alick3Y365nbeda3KO8ABtQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--10X69dt9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Alick3Y365nbeda3KO8ABtQ.jpeg" alt="" width="880" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I made it
&lt;/h3&gt;

&lt;p&gt;Over the years of working on Creative Technology projects, I’ve slowly grown to use certain technologies as part of a mono-repo setup, that are flexible enough to be adapted to most projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; : an opinionated React.js frame work for Front End development, that comes with a variety of helper functions to help speed up development and build of your web app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Framework&lt;/strong&gt; : an Infrastructure as Code (IaC) framework that comes with CLI commands that help you develop lambda functions locally and then deploy them to an API Gateway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; : an Infrastructure as Code (IaC) framework that uses Hashicorp Configuration Language (HCL) config files to create, manage and deploy cloud resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time I ended up informally creating a wrapper script to help orchestrate these technologies, so I could re-use this base template from a ‘full stack’ perspective and spend time focusing on project specific functionality.&lt;/p&gt;

&lt;p&gt;I’ve found this wrapper script very useful in helping me deliver projects — so I thought I’d formalise it into an &lt;a href="https://github.com/JamesMillerBlog/wrapper.js"&gt;Open Source project &lt;/a&gt;— in case someone else other than me finds it helpful :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1N3hf8t---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AmDVQINUx2-5La8y8hC-Vgw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1N3hf8t---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AmDVQINUx2-5La8y8hC-Vgw.jpeg" alt="" width="880" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it work
&lt;/h3&gt;

&lt;p&gt;Full instructions can be found on &lt;a href="https://jamesmiller.blog/wrapperjs-getting-started/"&gt;this page&lt;/a&gt;, but here is an oversimplified summary in 3 steps.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Select a template
&lt;/h4&gt;

&lt;p&gt;Once you’ve installed necessary libraries, choose a template you want to use, for example the WebXR template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CF3pD_cs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AgVY8KKyKM5vNSbR45w_AqQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CF3pD_cs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AgVY8KKyKM5vNSbR45w_AqQ.gif" alt="" width="880" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Configure the app
&lt;/h4&gt;

&lt;p&gt;Use CLI commands to create an AWS Secret and S3 bucket for your template configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Oa3oMGvL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/998/1%2AJsic3UkqsdTdBJ_sPVwwDA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Oa3oMGvL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/998/1%2AJsic3UkqsdTdBJ_sPVwwDA.jpeg" alt="" width="880" height="903"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then use &lt;strong&gt;gobble&lt;/strong&gt; CLI commands to use those secrets to deploy your customised template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OtFMNIU5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AI1H8EJq5j-CeMiKu4sDs9Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OtFMNIU5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AI1H8EJq5j-CeMiKu4sDs9Q.jpeg" alt="" width="880" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Dev and deploy
&lt;/h4&gt;

&lt;p&gt;Continue using gobble CLI commands to develop your full stack app on your local machine — or integrate wrapper.js into your continous deployment pipeline to automate dynamic deployments for the different environments of your app!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WdabTYBv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/783/1%2AFzF_ViobPDrgON9jXp2vRA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WdabTYBv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/783/1%2AFzF_ViobPDrgON9jXp2vRA.jpeg" alt="" width="783" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Where to learn more
&lt;/h3&gt;

&lt;p&gt;To find out more details about Wrapper.js, you can see the below links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jamesmiller.blog/wrapperjs/"&gt;Summary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jamesmiller.blog/about-wrapperjs/"&gt;About&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jamesmiller.blog/wrapperjs-getting-started/"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/JamesMillerBlog/wrapper.js"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/JamesMillerBlog/wrapper.js/pkgs/npm/wrapper.js"&gt;NPM Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mTEuu5Jd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A9yPGm0SLT7rcTYW3cSTgVw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mTEuu5Jd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A9yPGm0SLT7rcTYW3cSTgVw.png" alt="" width="880" height="380"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>serverless</category>
      <category>javascript</category>
      <category>webxr</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to make real-time multiplayer WebXR experiences — part 3</title>
      <dc:creator>James Miller</dc:creator>
      <pubDate>Wed, 07 Sep 2022 21:11:29 +0000</pubDate>
      <link>https://forem.com/jamesmillerblog/how-to-make-real-time-multiplayer-webxr-experiences-part-3-26ki</link>
      <guid>https://forem.com/jamesmillerblog/how-to-make-real-time-multiplayer-webxr-experiences-part-3-26ki</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_9DVKlZd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/viiu60e4ak09ks131qei.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_9DVKlZd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/viiu60e4ak09ks131qei.jpg" alt="Header Image" width="880" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;This is the third blog post detailing how to make real-time multiplayer WebXR experiences.&lt;/p&gt;

&lt;p&gt;Having covered &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-1/"&gt;the conceptual sides of how it works&lt;/a&gt; and the &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-2/"&gt;practical sides of how to implement user position data&lt;/a&gt;, this blog post will clarify how to implement interactions with 3D models so users can interact with their environment in real time.&lt;/p&gt;

&lt;p&gt;If you havent already, read the other two posts referenced above and have fun reading the below :D&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Examples
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://jamesmiller.blog/how-to-make-real-time-multiplayer-webxr-experiences-part-2/"&gt;previous blog post&lt;/a&gt;, I talk about the XRScene Higher Order Component (HOC) which is declared in the index directory.&lt;/p&gt;

&lt;p&gt;Well start from there and expand on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;how to declare a 3D models&lt;/li&gt;
&lt;li&gt;how to make those 3D models interactive&lt;/li&gt;
&lt;li&gt;how to integrate the Websockets with the 3D models&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Lets go :D&lt;/p&gt;

&lt;h4&gt;
  
  
  How to declare 3D models
&lt;/h4&gt;

&lt;p&gt;In order to emit and retrieve a 3D models position data with Websockets, you need to first instantiate the Websockets (as explained in part 2 of this tutorial series) and then declare a 3D model component that is set up to leverage those Websockets.&lt;/p&gt;

&lt;p&gt;Lets take a look at how Ive set them up, by first looking at the index.js file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The index.js file&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;See lines 8 and 2024, which show where the 3D model is loaded.&lt;/p&gt;

&lt;p&gt;You can see that we are passing a name, position and rotation property into this componentlets take a closer look at the Shiba.js component to understand how those are being used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Head from 'next/head'
import dynamic from 'next/dynamic';
import React, { useRef, useState, Suspense, lazy, useEffect } from 'react'

import Header from '../components/Header'

const XRScene = dynamic(() =&amp;gt; import("../components/XRScene"), { ssr: false });
const Shiba = lazy(() =&amp;gt; import("../components/3dAssets/Shiba.js"), {ssr: false});
const Slide = lazy(() =&amp;gt; import("../components/3dAssets/Slide.js"), {ssr: false});
const Dome = lazy(() =&amp;gt; import("../components/3dAssets/Dome.js"), {ssr: false});

export default function Home() {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Wrapper.js Web XR Example&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;Header /&amp;gt;
      &amp;lt;XRScene&amp;gt;
        &amp;lt;Shiba
          name={'shiba'}
          position={[1, -1.1, -3]}
          rotation={[0,1,0]}
        /&amp;gt;
        &amp;lt;Dome
          name={'breakdown'}
          image={'space.jpg'}
          admin={true}
        /&amp;gt;
        &amp;lt;Slide
          name={'smile'}
          image={'smile.jpeg'}
          position={[-2, 1, 0]}
          rotation={[0,-.5,0]}
          width={10}
          height={10}
        /&amp;gt;
        &amp;lt;ambientLight intensity={10} /&amp;gt;
        &amp;lt;spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} /&amp;gt;
        &amp;lt;pointLight position={[-10, -10, -10]} /&amp;gt;
        &amp;lt;spotLight position={[10, 10, 10]} angle={15} penumbra={1} /&amp;gt;
      &amp;lt;/XRScene&amp;gt;
    &amp;lt;/&amp;gt;
  )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The 3D model component (Shiba.js)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This component is responsible for rendering the GLTF 3D model, allowing it to be interactive with XR controllers and connecting it to Websockets.&lt;/p&gt;

&lt;p&gt;To break this down further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rendering the GLTF model&lt;/strong&gt; : between lines 1632 is the logic that is auto-generated by an &lt;a href="https://github.com/pmndrs/gltfjsx"&gt;opensource repository that converts GLTFs into React-Three-Fiber components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabling the use of XR interactivity for the model&lt;/strong&gt; : on line 12 and 39 the Higher Order Component (HOC) withXrInteractivity.js is used to provide Shiba.js with XR interactivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabling the use of Websockets for the model:&lt;/strong&gt; on lines 11 and 40 the HOC withCollaboration.js is used to provide Shiba.js with
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
author: zixisun02 (https://sketchfab.com/zixisun51)
license: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
source: https://sketchfab.com/3d-models/shiba-faef9fe5ace445e7b2989d1c1ece361c
title: Shiba
*/

import React, { useRef, forwardRef, useEffect } from 'react'
import { useGLTF, useAnimations } from '@react-three/drei'
import withCollaboration from './withCollaboration';
import withXrInteractivity from './withXrInteractivity';

const Model = forwardRef((props, group) =&amp;gt; {
  const {name } = props;
  const { nodes, materials } = useGLTF('/shiba/scene.gltf')
  return (
    &amp;lt;group ref={group} {...props} dispose={null} name={name}&amp;gt;
      &amp;lt;group rotation={[-Math.PI / 2, 0, 0]}&amp;gt;
        &amp;lt;group rotation={[Math.PI / 2, 0, 0]}&amp;gt;
          &amp;lt;group rotation={[-Math.PI / 2, 0, 0]}&amp;gt;
            &amp;lt;mesh geometry={nodes.Group18985_default_0.geometry} material={nodes.Group18985_default_0.material} /&amp;gt;
          &amp;lt;/group&amp;gt;
          &amp;lt;group rotation={[-Math.PI / 2, 0, 0]}&amp;gt;
            &amp;lt;mesh geometry={nodes.Box002_default_0.geometry} material={nodes.Box002_default_0.material} /&amp;gt;
          &amp;lt;/group&amp;gt;
          &amp;lt;group rotation={[-Math.PI / 2, 0, 0]}&amp;gt;
            &amp;lt;mesh geometry={nodes.Object001_default_0.geometry} material={nodes.Object001_default_0.material} /&amp;gt;
          &amp;lt;/group&amp;gt;
        &amp;lt;/group&amp;gt;
      &amp;lt;/group&amp;gt;
    &amp;lt;/group&amp;gt;
  )
});

useGLTF.preload('/shiba/scene.gltf')

const InteractiveModel = withXrInteractivity(Model);
const Shiba = withCollaboration(InteractiveModel);

export default Shiba;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How to make those models interactive
&lt;/h4&gt;

&lt;p&gt;In order to enable the use of your XR controllers (e.g Oculus Quest 2 controllers or HoloLens 2 hand tracked controllers etc), you need to implement a library called react-three/xr.&lt;/p&gt;

&lt;p&gt;In this example, Ive implemented this in a HOC called withXrInteractivity.js, lets take a deeper look at it!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Higher Order ComponentwithXrInteractivity.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This HOC is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enabling the object to become grabbable by the XR controller:&lt;/strong&gt; lines 4046 we are using the RayGrab HOC that is provided by react-three/xr, for devices that are capable of XR interactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tracking the position values of the model that has been moved:&lt;/strong&gt; lines 1826 we are using the useXREvent function from react-three/xr to track the position of where an object has moved to and from&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updating the global app state with the name and positions of the object that has been moved:&lt;/strong&gt; finally, between lines 2836 we are figuring out which of the objects with this HOC attached to it have been moved and then update the apps global state with that objects name and position values
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useRef, useEffect, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { RayGrab, useXREvent } from '@react-three/xr';
import deviceStore from '../../stores/device';

import selectedObjectStore from '../../stores/selectedObject';
import { Matrix4, Vector3, } from 'three';

const withXrInteractivity = (BaseComponent) =&amp;gt; (props) =&amp;gt; {
  const { device } = deviceStore();
  const { setSelectedObject } = selectedObjectStore();
  const group = useRef();
  const { scene } = useThree();
  const [oldPosition, setOldPosition] = useState();
  const [newPosition, setNewPosition] = useState()

  if(device != '' &amp;amp;&amp;amp; device != 'web') {
    useXREvent('selectstart', (e) =&amp;gt; {
      updatePosition(props.name, scene, setOldPosition);
    });

    useXREvent('selectend', (e) =&amp;gt; {
      updatePosition(props.name, scene, setNewPosition);
    })
  }

  useEffect(()=&amp;gt; {
    if(oldPosition &amp;amp;&amp;amp; newPosition) {
      // if the old positions are not equal to the new positions
      if(oldPosition.x != newPosition.x || oldPosition.y != newPosition.y || oldPosition.z != newPosition.z) {
        // then you know this object has just been updated, execute logic to update websockets and analytics
        selectedObject(props.name, scene.getObjectByName(props.name), setSelectedObject, group);
      }
    }
  },[oldPosition, newPosition])

  return (
    &amp;lt;&amp;gt;
        {device != '' &amp;amp;&amp;amp; device != 'web' &amp;amp;&amp;amp;
          &amp;lt;RayGrab&amp;gt;
              &amp;lt;BaseComponent
                ref={group}
                {...props}
              /&amp;gt;
          &amp;lt;/RayGrab&amp;gt;
        }
        {device != '' &amp;amp;&amp;amp; device == 'web' &amp;amp;&amp;amp;
          &amp;lt;BaseComponent
          ref={group}
          {...props}
          /&amp;gt;
        }
    &amp;lt;/&amp;gt;
  )
};

const updatePosition = (name, scene, setPosition) =&amp;gt; {
  let pos = new Vector3();
  let tempMatrix = new Matrix4;
  tempMatrix = scene.getObjectByName(name).matrixWorld;

  // set the oldPosition based on the matrix world positions
  pos.setFromMatrixPosition(tempMatrix);
  setPosition(pos)
};

const selectedObject = (objectname, object, setSelectedObject, group) =&amp;gt; {
  let { x, y, z } = object.position
  setSelectedObject({
    objectname: objectname,
    position: {
      x:x,
      y:y,
      z:z
    },
    group: group
  });
}

export default withXrInteractivity;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How to integrate the Websockets with the 3D models
&lt;/h4&gt;

&lt;p&gt;Once weve made the model interactive with the XR controllers, we then need to integrate the use of Websockets to allow other users to see the new position of the model youve moved.&lt;/p&gt;

&lt;p&gt;There are two sides of this to understand, the first is the &lt;strong&gt;withCollaboration.js&lt;/strong&gt; HOC that is responsible for the actual Websocket integration on the Front End and the second is what the data actually looks like in DynamoDB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Higher Order ComponentwithCollaboration.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Starting with the Front End, withCollaboration.js does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Submits the positions of the object youve just moved with your XR controllers to the Websocket&lt;/strong&gt; : lines 1925 check if youre device is capable of XR interactivity, if so then it will update the Websocket with the name of the model being moved, as well as the position of that moved model and the name of the user which moved it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listens to the Websocket for any updates and updates the positions of the moved models&lt;/strong&gt; : lines 2832 listen for updates on the Websocket and then check if the submission was made from a different user from the one that submitted it, if so then the new position of the model is applied to the scene.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building on that last bullet point, the reason it needs to check if the user that submitted the movement is different from the user receiving the positional data on the web app, is to prevent bouncing of position values for the user that originally submitted the new position of the model.&lt;/p&gt;

&lt;p&gt;For example, if you submit position values to the Websocket, the Websocket would immediately detect that you had moved the model and would would try to update the position of the model youve just movedthis can cause confusion at the Three.JS layer and cause the position of the model to move erratically and become buggy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useRef, useEffect, useState } from 'react'
import { useThree } from '@react-three/fiber';
import { RayGrab, useXREvent } from '@react-three/xr';
import deviceStore from '../../stores/device';
import socketStore from '../../stores/socket';
import cognitoStore from '../../stores/cognito';
import selectedObjectStore from '../../stores/selectedObject';
import { Matrix4 } from 'three';

const withCollaboration = (BaseComponent) =&amp;gt; (props) =&amp;gt; {
  const { device } = deviceStore();
  const { selectedObject } = selectedObjectStore();
  const { sendJsonMessage, lastJsonMessage } = socketStore();
  const { cognito } = cognitoStore();
  const { scene } = useThree();
  const [socketMode, setSocketMode] = useState('initialLoad');

  useEffect(()=&amp;gt; {
    if(device != '' &amp;amp;&amp;amp; device != 'web') {
      if(selectedObject.objectname) {
        submitPositionsToCloud(selectedObject.objectname, cognito.username, scene.getObjectByName(selectedObject.objectname), sendJsonMessage);
      }
    }
  },[selectedObject])

  useEffect(()=&amp;gt; {
    if(device != '') {
      updateModelFromWebSockets(lastJsonMessage, props.name, cognito, scene.getObjectByName(props.name), socketMode, setSocketMode);
    }
  }, [lastJsonMessage])

      return (
        &amp;lt;BaseComponent
          {...props}
        /&amp;gt;
      )
};

const submitPositionsToCloud = (objectname, username, object, sendJsonMessage) =&amp;gt; {
  let newData = {
    type: 'objects',
    uid: objectname,
    data: {
      submittedBy: username,
      matrixWorld: object.matrixWorld
    }
  };
  sendJsonMessage({
    action: 'positions',
    data: newData
  });
}

const updateModelFromWebSockets = (lastJsonMessage, name, cognito, group, socketMode, setSocketMode) =&amp;gt; {
  let data;
  if(lastJsonMessage != null){
    for(let x=0; x&amp;lt;lastJsonMessage.length; x++) {
      if(lastJsonMessage[x].uid == name) {
        if(lastJsonMessage[x].data.matrixWorld.length != 0) {
          data = lastJsonMessage[x].data;
        }
      }
    }
  }
  if(data) {
    if(socketMode == 'stream' &amp;amp;&amp;amp; data.submittedBy != cognito.username || socketMode == 'initialLoad') {
      let tempMatrix = new Matrix4();
      tempMatrix.copy(data.matrixWorld);
      tempMatrix.decompose(group.position, group.quaternion, group.scale)
      if(socketMode == 'initialLoad') {
        setSocketMode('stream');
      }
    }
  }
}

export default withCollaboration;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Database for Websocket positional data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having submitted the new position values of the model to the Websocket, this is then passed onto DynamoDB through the same process outlined in part 2 of this tutorial series.&lt;/p&gt;

&lt;p&gt;Ive attached a screenshot below of the DynamoDB table, which shows two types of entries: &lt;strong&gt;objects&lt;/strong&gt; and &lt;strong&gt;users&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Users&lt;/strong&gt; refers to people who have logged in and have moved around, their position values are stored there (see part 2 of this tutorial series).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Objects&lt;/strong&gt; refers to the models that users have moved using their XR controllers as described in this blog post, lets take a deeper look at this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1IqbgS-i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1662585560775/Zzb0kFObZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1IqbgS-i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1662585560775/Zzb0kFObZ.png" alt="Screenshot of the positional data in the DynamoDB Table&amp;lt;br&amp;gt;
" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Screenshot of the positional data in the DynamoDB Table&lt;/p&gt;

&lt;p&gt;Looking at an example of the model entry that is stored, you can see that the models name is stored (in this case shiba) as well as the position of the model (called matrixWorld) and the name of the user that submitted the new position.&lt;/p&gt;

&lt;p&gt;The reason that we stored the position as a matrixWorld instead of x/y/z value, is down to the complexities of how a models position is changed when you move it using an XR controller.&lt;/p&gt;

&lt;p&gt;When you grab a model using an XR controller, that model becomes a child of the controller, until the moment that you release that model.&lt;/p&gt;

&lt;p&gt;In order to provide other users with the accurate position of where youve moved the model to, we are storing that models matrixWorld position, which is essentially its global positionnot its position in relation to the XR controller (aka not its position as a child of the XR controller).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "type": {
    "S": "objects"
  },
  "uid": {
    "S": "shiba"
  },
  "data": {
    "M": {
      "matrixWorld": {
        "M": {
          "elements": {
            "L": [
              {
                "N": "-0.8743998152939527"
              },
              {
                "N": "-0.23723998239253116"
              },
              {
                "N": "-0.4231857431325429"
              },
              {
                "N": "0"
              },
              {
                "N": "-0.23408684389239517"
              },
              {
                "N": "0.9703077655463146"
              },
              {
                "N": "-0.0602815775361555"
              },
              {
                "N": "0"
              },
              {
                "N": "0.4249385470147294"
              },
              {
                "N": "0.046353861928324164"
              },
              {
                "N": "-0.9040077143825459"
              },
              {
                "N": "0"
              },
              {
                "N": "0.6380046282561553"
              },
              {
                "N": "1.0890667315312834"
              },
              {
                "N": "-4.233745217744318"
              },
              {
                "N": "1"
              }
            ]
          }
        }
      },
      "submittedBy": {
        "S": "hi@jamesmiller.blog"
      }
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Now THAT is a lot of information!!!&lt;/p&gt;

&lt;p&gt;I really hope this has been helpful in enabling you to make real-time multiplayer WebXR experiences using React-Three-Fiber and Websockets :D&lt;/p&gt;

&lt;p&gt;With any luck, you will be able to adapt this code for your appor even better use &lt;a href="https://github.com/JamesMillerBlog/wrapper.js"&gt;Wrapper.js&lt;/a&gt; to build your WebXR experiences!&lt;/p&gt;

&lt;p&gt;I hope this is helpful and in the meantimehave fun :D&lt;/p&gt;

</description>
      <category>webxr</category>
      <category>metaverse</category>
      <category>serverless</category>
      <category>react</category>
    </item>
  </channel>
</rss>
