<?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: Alex Mitchell</title>
    <description>The latest articles on Forem by Alex Mitchell (@shamsup).</description>
    <link>https://forem.com/shamsup</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%2F125166%2F63efb0e9-34ab-4c01-94b8-7acea75974b9.jpeg</url>
      <title>Forem: Alex Mitchell</title>
      <link>https://forem.com/shamsup</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shamsup"/>
    <language>en</language>
    <item>
      <title>PSA: Lambda@Edge isn't a quick win</title>
      <dc:creator>Alex Mitchell</dc:creator>
      <pubDate>Tue, 14 Nov 2023 09:38:46 +0000</pubDate>
      <link>https://forem.com/shamsup/psa-lambdaedge-isnt-a-quick-win-3252</link>
      <guid>https://forem.com/shamsup/psa-lambdaedge-isnt-a-quick-win-3252</guid>
      <description>&lt;p&gt;&lt;em&gt;This is based on a post I originally wrote in the Remix Discord server in October of 2022 titled "Lambda@Edge isn't the same 'Edge'" to weigh Lambda@Edge against something like Cloudflare, but I decided to turn it into more of a blog post around the pitfalls of edge computing itself with the recent news around the Partial Pre-render feature from Vercel and &lt;a href="https://x.com/acdlite/status/1724151383136330233"&gt;them &lt;em&gt;refining&lt;/em&gt; their stance on edge-rendering&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The short of it&lt;/strong&gt;: Just stick to CloudFront + Lambda and &lt;strong&gt;avoid&lt;/strong&gt; Lambda@Edge unless you need to manage multi-region deployments anyway.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Lambda@Edge?
&lt;/h2&gt;

&lt;p&gt;Lambda@Edge is AWS's serverless function service that automatically distributes your code across regions to execute near your users. It uses CloudFront, AWS's CDN service, to detect the closest AWS region to the requesting user and starts a Lambda function in that region, the idea being to reduce the latency between the user and the executing code. It has a few limitations compared to the normal Lambda service, which I'll cover, but it's essentially the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How is it a different from Cloudflare's "Edge"?
&lt;/h2&gt;

&lt;p&gt;AWS Lambda@Edge is not the same "edge" that something like Cloudflare Workers runs on. Lambda@Edge is essentially just your Lambda being automatically deployed to other regions dynamically based on where requests are coming in from. This is to say that a request coming from Washington (state) might hit a Lambda in &lt;code&gt;us-west-2&lt;/code&gt; region, while you deployed to &lt;code&gt;us-east-1&lt;/code&gt;. &lt;strong&gt;It will still access your resources wherever they are deployed&lt;/strong&gt;, so a DynamoDB table or RDS cluster will stay in whatever region you deployed it in, but the Lambda will use AWS's region-to-region optimized network to access those resources from whatever region the Lambda is executed in.&lt;/p&gt;

&lt;p&gt;Compared to Cloudflare's pages or workers, where the code actually runs on the CDN servers themselves, there will be latency involved between the CloudFront edge server calling the nearest region for your Lambda, and the region potentially having to cold-start an instance of your function. In Cloudflare, there is virtually 0 cold-start because &lt;a href="https://developers.cloudflare.com/workers/learning/how-workers-works/#isolates"&gt;they are using V8-isolates&lt;/a&gt;, which has a much lower overhead to start compared to Lambda's Node runtime.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: CloudFront also has &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html"&gt;CloudFront Functions&lt;/a&gt;, but they are severely limited in scope, like not being able to execute any asynchronous code&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Lambda@Edge bad?
&lt;/h2&gt;

&lt;p&gt;Well there are 2 categories I want to address here: edge computing on its own, and Lambda@Edge vs AWS Lambda. It's also not &lt;em&gt;bad&lt;/em&gt;, but it may take more effort than you expect to realize a win by using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge Computing's Biggest Hurdle: Distance to the Origin
&lt;/h2&gt;

&lt;p&gt;Edge computing has a major pitfall for executing code with data dependencies: latency to the origin. If the code is querying a database or hitting an API in a different region, the latency from requests to the data source can easily end up taking longer than the latency you save for your user by moving the code closer to them, especially when doing any requests in series (or request waterfalls).&lt;/p&gt;

&lt;p&gt;As an example, let's use something I tested on my own: DynamoDB deployed to &lt;code&gt;us-east-1&lt;/code&gt; with Lambda@Edge executing in &lt;code&gt;us-west-2&lt;/code&gt;, where I live. I have an application that uses database-backed sessions, where a cookie in the user's browser is just an ID for an item in the database that contains the actual session contents. Most requests begin by retrieving the user's session before proceeding with any other functionality so that we can do things like check the user's permissions to access the resource they are requesting, account-level rate-limiting, read user preferences, read feature flags, and so on.&lt;/p&gt;

&lt;p&gt;A typical request from my computer in Vancouver, WA to &lt;code&gt;us-east-1&lt;/code&gt; through CloudFront takes about 60ms round-trip. This latency will be very similar to AWS's region-to-region latency for &lt;code&gt;us-west-2&lt;/code&gt; because CloudFront routes through the same network. You can check the latency between other regions on &lt;a href="https://www.cloudping.co/grid"&gt;CloudPing&lt;/a&gt;. This cross-region latency "tax" is paid once between CloudFront and the origin. Once the code is executing in &lt;code&gt;us-east-1&lt;/code&gt;, the same region as the database, the requests to the database have 1-2ms of latency each, which is pretty miniscule compared to the overall latency between the user and the origin. Say reading the session record takes ~10ms, 1-2ms of that is latency, then we request the resource from the database with another 10ms total with 1-2ms of latency. We then return a response from Lambda, which gets routed back through the region-to-region network to CloudFront's edge, then back to the user. Overall, this takes about 200ms, with about 75ms of unavoidable latency just due to physical distance. There's other overhead by using Lambda in the first place, but we can ignore it for this purpose.&lt;/p&gt;

&lt;p&gt;We can add up the total cost of just the latency from the physical distances of the requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         User to CF: 10ms
CF to origin Lambda: 60ms
       DB request 1:  2ms
       DB request 2:  2ms

      Total Latency: 74ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's compare this to the same application, but the code is deployed to Lambda@Edge instead of Lambda. We now have 10ms latency to the code instead of only to CloudFront (we'll give the benefit of the doubt and say that there is 0 additional latency between CloudFront and the nearest AWS Region). However, remember our code is executing in &lt;code&gt;us-west-2&lt;/code&gt; but our database is in &lt;code&gt;us-east-1&lt;/code&gt;, so each database request pays the 60ms region-to-region latency because we have to wait for the first query to complete before requesting the second resource. We then return the response to the user, the full request taking about 260ms, with roughly 130ms of that being unavoidable due to physical distance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         User to CF: 10ms
CF to origin Lambda:  0ms
       DB request 1: 60ms
       DB request 2: 60ms

      Total Latency: 130ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution to the data locality problem is to do multi-region deploys of your data as well, which is much harder to do correctly and comes with its own set of tradeoffs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lambda@Edge vs Lambda
&lt;/h2&gt;

&lt;p&gt;The next few points will focus on differences between Lambda@Edge and Lambda.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No unified logging&lt;/strong&gt;. Lambda@Edge's logs will be in Cloudwatch, but they will be in whichever region the function executed in, making it &lt;strong&gt;your&lt;/strong&gt; job to aggregate the logs into a single place. Knowing which regions your function has been executed in is just the first part of this problem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No function versioning&lt;/strong&gt;. There are no function versions or aliases for Lambda@Edge, so canary deployments or other partial rollout solutions are a significant undertaking. I honestly don't have a solution in mind for this challenge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No environment variables&lt;/strong&gt;. You will either need a solution to inject values into the executable at build-time, or use a service such as SSM to store runtime configuration for &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploys take significantly longer&lt;/strong&gt;. Lambda@Edge deployments have to propagate out to the CloudFront distribution, just like any other change to the distribution configuration, so the major bottleneck for deployment time is the time it takes to propagate a change out to CloudFront, which regularly takes 15 minutes or more. This can significantly reduce velocity, especially when iterating on new features or attempting to debug problems in a deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increase in cold starts&lt;/strong&gt;. We could ignore this point, since every request in an app with 0 users will be a cold start anyway. However, if you want to explore a hypothetical with me, let's say we have more than 0 users. Lambda@Edge fragments the pool of warm Lambda instances with your code across regions, which could lead to an increase in the cold-start ratio for your application depending on the geographic distribution of your userbase. If all users are routed to the same region as they are without Lambda@Edge or multi-region deployments, they share a pool of Lambda instances, which increases the likelihood that one is available as the Lambda service juggles concurrent requests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Unless you are working on something big and have a lot of developer hours to put into monitoring, management tooling, and cross-region deploys/maintenance for your dependencies, &lt;strong&gt;I would highly advise you not to use Lambda@Edge&lt;/strong&gt; and just trust AWS's internal network to route from CloudFront to your static AWS regions automatically.&lt;/p&gt;




&lt;p&gt;There are many valid use-cases for edge computing, but it's hard to do correctly. Generally speaking, your compute resources need to live close to the data that they depend on. There are a ton of projects and companies working on bringing your data closer to the edge, which is an awesome problem space. Cloudflare, Fly.io, and Turso, to name a few. The purpose of this article, again, was to highlight the challenges of moving to an edge compute model specifically with Lambda@Edge and should not be taken to discount the viability of edge computing as a whole. Vercel's Partial pre-render feature is incredibly cool for taking no extra effort on the developer's part and getting a ridiculously fast TTFB. I'm excited about the future of this space. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>edge</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Calculating the Secret Hash for AWS Cognito in Node.js</title>
      <dc:creator>Alex Mitchell</dc:creator>
      <pubDate>Thu, 30 Jun 2022 20:09:11 +0000</pubDate>
      <link>https://forem.com/shamsup/creating-the-secret-hash-for-aws-cognito-in-nodejs-50f7</link>
      <guid>https://forem.com/shamsup/creating-the-secret-hash-for-aws-cognito-in-nodejs-50f7</guid>
      <description>&lt;p&gt;While Amplify and the Cognito client libraries don't support user pools with a client secret, this is only to ensure that the client secret isn't exposed in the browser. However, this doesn't mean that you can't use the full Cognito API from Node.js. &lt;/p&gt;

&lt;p&gt;Recently I was attempting to use Cognito API from a Node.js Lambda function to customize our signup flow, but kept getting the error &lt;code&gt;SecretHash does not match for the client&lt;/code&gt; when trying to sign up users. Some digging led me to the &lt;a href="https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html#cognito-user-pools-computing-secret-hash"&gt;Cognito documentation&lt;/a&gt;, which contains some example code in Java, but otherwise just some pseudo-code to go off of for generating the secret hash:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;SecretHash&lt;/code&gt; value is a Base 64-encoded keyed-hash message authentication code (HMAC) calculated using the secret key of a user pool client and username plus the client ID in the message. The following pseudocode shows how this value is calculated. In this pseudocode, &lt;code&gt;+&lt;/code&gt; indicates concatenation, &lt;code&gt;HMAC_SHA256&lt;/code&gt; represents a function that produces an HMAC value using HmacSHA256, and &lt;code&gt;Base64&lt;/code&gt; represents a function that produces Base-64-encoded version of the hash output.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base64 ( HMAC_SHA256 ( "Client Secret Key", "Username" + "Client Id" ) )
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;HMAC is a special kind of hash that two parties can create and verify if they both know the key. This is used often for things like signed cookies, JWTs, and verifying webhooks. This makes sense as a simple verification method for AWS to use since only our app and Cognito should know the client secret, and HTTPS is already encrypting the request.&lt;/p&gt;

&lt;p&gt;I know that Node.js has the &lt;code&gt;crypto&lt;/code&gt; module built-in, which I've used in the past to generate SHA-256 hashes, but I had never used a &lt;em&gt;specific key&lt;/em&gt; to do it. Time to dig into the &lt;a href="https://nodejs.org/api/crypto.html#cryptocreatehmacalgorithm-key-options"&gt;&lt;code&gt;crypto&lt;/code&gt; docs&lt;/a&gt;! Luckily for us, the Node.js &lt;code&gt;crypto&lt;/code&gt; module follows a similar "builder" pattern as the Java implementation in the Cognito documentation referenced earlier.&lt;/p&gt;

&lt;p&gt;Here's how we can create an HMAC value using the SHA-256 algorithm in node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHmac&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// create the hmac with the sha256 algorithm and a secret key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// add the value we want to hash&lt;/span&gt;
&lt;span class="nx"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value to hash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// get the hashed value as base64&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can plug in our User Pool values where needed to create our &lt;code&gt;SecretHash&lt;/code&gt;. I'm using the &lt;a href="https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SignUp.html"&gt;&lt;code&gt;SignUp&lt;/code&gt;&lt;/a&gt; method as an example, but it's the same for &lt;code&gt;ConfirmSignUp&lt;/code&gt;, &lt;code&gt;ForgotPassword&lt;/code&gt;, &lt;code&gt;ConfirmForgotPassword&lt;/code&gt;, and &lt;code&gt;ResendConfirmationCode&lt;/code&gt; (and probably more). I'm also using the AWS SDK for JavaScript v2 which is what is included in the Lambda Node.js runtime, but the secret hash is generated the same way for v3 of the SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHmac&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CognitoIdentityServiceProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// grab all the constant variables from the user pool&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COGNITO_CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COGNITO_CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USER_POOL_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COGNITO_USER_POOL_ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cognito&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CognitoIdentityServiceProvider&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_SECRET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// AWS wants `"Username" + "Client Id"`&lt;/span&gt;
  &lt;span class="nx"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secretHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hasher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cognito&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signUp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;UserPoolId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USER_POOL_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;SecretHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;secretHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;UserAttributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="c1"&gt;// some attributes as an example&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;given_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;family_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AWS Cognito has a very attractive pricing model and a lot of features to build out whatever type of authentication you want for your application, but it has more than its fair share of quirks that complicate adoption. Hopefully this saves you a few hours of digging.&lt;/p&gt;

&lt;p&gt;Let me know in the comments if there are any other issues you've been trying to wrestle with AWS Cognito.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cognito</category>
      <category>node</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
