Giter Club home page Giter Club logo

amazon-cognito-passwordless-auth's Introduction

Amazon Cognito Passwordless Auth

AWS Solution to implement Passwordless authenticaton with Amazon Cognito

Passwordless authentication improves security, reduces friction and provides better user experience for end-users of customer facing applications. Amazon Cognito provides features to implement custom authentication flows, which can be used to expand authentication factors for your application. This solution demonstrates several patterns to support passwordless authentication and provides reference implementations for these methods:

The reference implementation of each of these auth methods uses several AWS resources. This solution contains both CDK code (TypeScript) for the back-end, as well as front-end code (TypeScript) to use in Web, React and React Native to help developers understand the building blocks needed and expand/adjust the solution as necessary.

IMPORTANT: This AWS Solution is for demonstration purposes and uses several AWS resources, it is intended for developers with moderate to advanced AWS knowledge. If you plan to use these methods in production, you need to review, adjust and extend the sample code as necessary for your requirements.

Sign in with passkey, without needing to type in your username:

Passwordless Sign In

Sign in with non-discoverable FIDO2 credential, or Magic Link:

Passwordless Sign In

Video Introduction

Here's a short (11m41s) video that explains and demonstrates the solution:

Solution Intro on YouTube

Table of Contents

Installation

We've wrapped the sample code in an NPM package for convenient installation and use:

npm install amazon-cognito-passwordless-auth

The installation contains both the AWS CDK code as well as the front-end code to go with it.

Getting Started

Self-paced workshop

Follow the self-paced workshop (duration: 60 minutes) to understand how to use this solution to implement sign-in with FIDO2 (WebAuthn) and Magic Links. The workshop will walk you through all the steps to set up and use this solution: Implement Passwordless authentication with Amazon Cognito and WebAuthn

End-to-end example

Alternatively, you can deploy the end-to-end example into your own AWS account. You can run the accompanying front end locally, and sign-in with magic links and FIDO2 (WebAuthn), and try SMS OTP Step Up authentication.

Basic Usage

Create a CDK stack, instantiate the Passwordless CDK construct, and deploy. This will deploy all necessary AWS components, such as AWS Lambda triggers that implement custom authentication flows.

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { Passwordless } from "amazon-cognito-passwordless-auth/cdk";

class SampleTestStack extends cdk.Stack {
  constructor(scope?: Construct, id?: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const passwordless = new Passwordless(this, "Passwordless", {
      userPool: yourUserPool, // optional, if not provided an Amazon Cognito User Pool will be created for you
      allowedOrigins: [
        "http://localhost:5173", // Mention all URLs you're exposing the web app on
      ],
      magicLink: {
        sesFromAddress: "[email protected]", // must be a verified domain or identity in Amazon SES
      },
      fido2: {
        allowedRelyingPartyIds: [
          "localhost", // Domain names that you wish to use as Relying Party ID
        ],
      },
      smsOtpStepUp: {}, // leave this out to disable SMS OTP Step Up Auth. Likewise for magicLink and fido2
    });

    new cdk.CfnOutput(this, "ClientId", {
      value: passwordless.userPoolClients!.at(0)!.userPoolClientId,
    });
    new cdk.CfnOutput(this, "Fido2Url", {
      value: passwordless.fido2Api!.url!,
    });
  }
}

Then, with your CDK stack deployed, you're ready to wire up the frontend, see below for React, React Native and (plain) Web.

Notable Features

This library includes:

  • A CDK construct that deploys an Amazon Cognito User Pool with Custom Authorization configured to support the passwordless authentication flows (includes other AWS Services needed, notably DynamoDB and HTTP API).
  • Web functions to use in your Web Apps, to help implement the corresponding front-end.
  • React and React Native hooks, to make it even easier to use passwordless authentication in React and React Native.
  • React prebuilt components that you can drop into your webapp to get started with something that works quickly, as a basis for further development.

Other noteworthy features:

  • This library is built from the ground up in plain TypeScript and has very few dependencies besides aws-sdk and aws-cdk-lib. Most batteries are included:
    • The Magic Link back-end implementation has no dependencies
    • The FIDO2 back-end implementation only depends on cbor
    • The SMS Step-Up Auth back-end implementation only depends on aws-jwt-verify
    • The (plain) Web client implementation has no dependencies
    • The React Web client implementation only has a peer dependency on react itself
    • The React Native client implementation only depends on react-native-passkey
  • This library is fully compatible with AWS Amplify (JS library, aws-amplify), however it does not require AWS Amplify. If you just need Auth, this library should be all you need, but you can use AWS Amplify at the same time for any other features (and even for Auth too, as they can co-operate). See Usage with AWS Amplify.
  • The custom authentication implementations are also exported as separate functions, so you can reuse the code, configure them and tailor them in your own Custom Auth Functions. For example, you can use a custom JavaScript function to generate the HTML and Text contents of the e-mail with the Magic Links.

Security

See CONTRIBUTING for more information.

Keep Dependencies Up-to-date

This sample solution defines several peer dependencies that you must install yourself (e.g. AWS CDK, React). You must make sure to keep these dependencies updated, to account for any security issues that may be found (and solved) for these dependencies.

Token (JWT) Storage

By default, localStorage is used to store tokens (JWTs). This is similar to how e.g. AmplifyJS does it, and is subject to the same concerns. You may want to store tokens elsewhere, perhaps in memory only. You can do so by configuring a custom storage class, e.g.:

import { Passwordless } from "amazon-cognito-passwordless-auth";

class MemoryStorage {
  constructor() {
    this.memory = new Map();
  }
  getItem(key) {
    return this.memory.get(key);
  }
  setItem(key, value) {
    this.memory.set(key, value);
  }
  removeItem(key) {
    this.memory.delete(key);
  }
}

Passwordless.configure({
  ..., // other config
  storage: new MemoryStorage(),
});

Other Security Best Practices

This sample solution is secure by default. However, you should consider matching the security posture to your requirements, that might be stricter than the defaults:

Usage with AWS Amplify

This library by default uses the same token storage as Amplify uses by default, and thus is able to co-exist and co-operate with Amplify. That means that you can use this library to manage authentication, and use Amplify for other operations (e.g. Storage, PubSub).

After the user signed-in with this library, Amplify will recognize that sign-in as if it had managed the sign-in itself.

If you're using Amplify and this library together, you can use the following convenience methods to configure this library from Amplify configuration:

import { Passwordless } from "amazon-cognito-passwordless-auth";
import { Amplify } from "aws-amplify";

// Configure Amplify:
Amplify.configure({
  ...
})

// Next, configure Passwordless from Amplify:
Passwordless.configureFromAmplify(Amplify.configure());

// Or, to be able able to provide additional Passwordless configuration, do:
Passwordless.configureFromAmplify(Amplify.configure()).with({
  fido2: {
    baseUrl: "...",
  },
});

Usage in (plain) Web

See README.md

Usage in React

See README-REACT.md

Usage in React Native

See README-REACT-NATIVE.md

Usage in JavaScript environments other than Web

See README.md

Customizing Auth

If you want to do customization of this solution that goes beyond the parameters of the Passwordless construct, e.g. to use your own e-mail content for magic links, see CUSTOMIZE-AUTH.md

FAQ - Frequently Asked Questions

Who created this library?

The AWS Industries Prototyping team. We created this library initially to use in our own prototypes, that we build for customers. We thought it would benefit many customers, so we decided to spend the effort to open-source it.

Since we use this library ourselves, we'll probably keep it up-to-date and evolve it further. That being said, we consider this sample code: if you use it, be prepared to own your own fork of it.

Why is this on aws-samples, and not awslabs?

Having this repository be on aws-samples communicates most clearly that it is sample code. Users may run it as-is, but should be prepared to "own" it themselves.

We are considering to move it to awslabs in the future (which is why we released this under Apache-2.0 license, instead of MIT-0 which is common on aws-samples).

How have you tested the security posture of this solution?

If you use this solution, YOU must review it and be your own judge of its security posture.

Having said that, you should know that this solution was written by Amazon Cognito experts from AWS. We have run it through multiple internal reviews. We've used it for several of our projects. Amazon's application security team has reviewed and pentested it.

Can you also support other Infrastructure as Code tools than CDK?

This is currently out of scope, to keep maintenance effort manageable. However we'd like to track such requests: leave us a GitHub issue.

Can you also support other Client technologies such as VueJS, Angular, Ionic, etc?

This is currently out of scope, to keep maintenance effort manageable. However we'd like to track such requests: leave us a GitHub issue.

Can you also support other languages than JavaScript / TypeScript?

This is currently out of scope, to keep maintenance effort manageable. However we'd like to track such requests: leave us a GitHub issue.

License

This project is licensed under the Apache-2.0 License.

amazon-cognito-passwordless-auth's People

Contributors

amazon-auto avatar brno32 avatar dependabot[bot] avatar ericborland avatar fahadsadiq avatar geranimo avatar jordan-nelson avatar kylekotowick avatar martinpagelaws avatar mikemeerschaert avatar ottokruse avatar philipbeber avatar robharveydev avatar smnalex avatar tinti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

amazon-cognito-passwordless-auth's Issues

Question: Automating Sign Up process to trigger Magic Link Sign In

Hello @ottokruse ,

Upon reviewing the Passwordless stack documentation and client library, it appears that the Sign Up flow will be handled separately. To meet the business requirements, we will need to customize and implement our own Sign Up flow accordingly.

Once a new user onboards, the plan is to utilize the Magic Link Sign In flow as designed in the Passwordless stack. To achieve this, we will deploy an API Gateway with user sign up implementation. In order to trigger the sending of an email with a Magic Link to the user, the following steps will be taken:

  1. AdminCreateUserCommand: to create a new user at Cognito

  2. AdminInitiateAuthCommand: to initiate the custom auth flow

  3. AdminRespondToAuthChallengeCommand: to respond with ClientMetadata setting to signInMethod: "MAGIC_LINK"

  4. Email to be sent newly onboard user with a Redirect URL to sign in

Regarding the onboarding flow, we appreciate your assistance in providing advice for bundling. We would also like to evaluate the duration of the Lambda function triggered from the API Gateway:

  1. First trigger (Init) or Cold start
Duration: 3911.69 ms Billed Duration: 3912 ms Memory Size: 256 MB Max Memory Used: 103 MB Init Duration: 714.09 ms
Duration: 3954.84 ms Billed Duration: 3955 ms Memory Size: 256 MB Max Memory Used: 103 MB Init Duration: 719.79 ms
  1. Subsequent triggers (Hot)
Duration: 2068.67 ms Billed Duration: 2069 ms Memory Size: 256 MB Max Memory Used: 104 MB
Duration: 813.12 ms Billed Duration: 814 ms Memory Size: 256 MB Max Memory Used: 104 MB
Duration: 2243.66 ms Billed Duration: 2244 ms Memory Size: 256 MB Max Memory Used: 104 MB

I would appreciate your advice on whether reusing the Magic Link from Passwordless stack for onboarding is the correct approach giving the Lambda to await the response from using admin functions to interact with Cognito. From a technical standpoint, your guidance would be valuable.

Thank you,

Frankie

Custom email template fails to compile

I followed the documentation here: https://github.com/aws-samples/amazon-cognito-passwordless-auth/blob/main/CUSTOMIZE-AUTH.md to setup custom email templates and ran into an error

SyntaxError: Cannot use import statement outside a module.

This was fixed by adding:

import * as lambdaNodeJs from "aws-cdk-lib/aws-lambda-nodejs";

createAuthChallenge: {
  entry: "functions/src/passwordless/createAuthChallenge.ts",
  bundling: {
    format: lambdaNodeJs.OutputFormat.ESM,
    banner: "import{createRequire}from 'module';const require=createRequire(import.meta.url);",
  },
},

Looks like it needs ESM to use the import syntax.

`Error: Missing Fido2 config` when not providing `fido2` object to `Config`

I have the following config

Passwordless.configure({
	cognitoIdpEndpoint: `${process.env.USER_POOL_REGION}`,
	userPoolId: `${process.env.USER_POOL_ID}`,
	clientId: `${process.env.USER_POOL_CLIENT_ID}`
})

I'm intentionally not passing a fido2 object because I only want to use the functionality around the Magic Link Sign-In.

However, the above gives me the following error

Error: Missing Fido2 config

Looking at the code, I can see why. If fido2 isn't defined, it throws this error.

Am I reading this, right? The documentation says using fido is optional, but clearly it's required

Feature request:

I wonder if fido2CreateCredential() can actually name the passkey as per device the passkey has been stored. For example Google name it right away
image

Add option to register user if not already registered

The following code silently exits if the user does not exist in the user pool

This is good default behavior, as it prevents user enumeration.

However, it would be nice if there was an option to automatically register a user instead of silently erroring.

An option taken from the Passwordless cdk construct could be added here

if (event.request.userNotFound) {

What I would like to do is register a user and have their first magic link email also verify their email when they sign in for the first time. I do not want a separate register page.

Usage in a Vite/Vue App

Hey, I am currently trying to implement the magic link login in a Vite/Vue app.

As soon as I install the npm package I get the following error trying to run it in dev mode (before adding any other code):

image

The thing is, all of those react packages are already in the node_modules folder before adding it.

I have tried to add react, react-dom, etc to the "external" build option and ssr option. But no improvements.

Any idea what I am doing wrong? Is it simply not possible to use this in a Vite/Vue app?

requestSignInLink does not work with phone number.

I create a user with verified email and phone number. When call requestSignInLink({username: email}) using email, it works fine but when i requestSignInLink({username: phone}) using the verified phone number for the user i get "User does not exist."

I checked the comments for the functions it says that alias should work fine. Is this not supposed to work with phone number.

Magic Link user not found email manipulation - domain extraction

Per this snippet:

if (event.request.userNotFound) {
logger.info("User not found");
const chars = [...event.userName].filter((c) => c.match(/[a-z]/i));
const name = chars.join("");
const domain = chars.reverse().slice(1, 6).join("");
email = `${name}@${domain}.***`;
}

Were not sure what testing has been done here but in observation and limited testing it possibly not going work all cases:

The domain extraction if understood correctly given the email reconstruction ${name}@${domain}.***, is attempting to just get the root domain and any subdomains, but not TLD/public suffix parts? the slice(1,6) seem out of place?

Whist it understood that the attempt here is to have a fully self-contained implementation there seems to be maintained TLD/public suffix list like here and more specifically here as used by this url parser.

How to add own additional Lambda functions to the fido2Api?

Hello,
I would like to add the amazon-cognito-passwordless-auth to an existing AWS-CDK stack that already has an API Gateway and AWS Cognito Pool.

When I try to synthesise my stack, I get the following error, as I have tried to add my functions to the fido2Api that was created by the PasswordlessStack.

Error: 'X11CiCdStack/Dev/ApiStack' depends on 'X11CiCdStack/Dev/PasswordlessStack' ({X11CiCdStack/Dev/ApiStack}.addDependency({X11CiCdStack/Dev/PasswordlessStack})). Adding this dependency (X11CiCdStack/Dev/PasswordlessStack -> X11CiCdStack/Dev/ApiStack/AccountFunction/Resource.Arn) would create a cyclic reference.

Here is my setup:

...
import { SesStack } from './stacks/ses/stack';
import { CognitoStack } from './stacks/cognito/stack';
import { PasswordlessStack } from './stacks/passwordless/stack';
import { ApiStack } from './stacks/api/stack';
import TaggingStack from './tagging';


interface EnvironmentStageProps extends StageProps {
  readonly config: BaseInfraConfig;
  readonly repo: RepositoryConfig;
  readonly codestarConnectionArn: string;
  readonly envName: string;
  readonly ses: SesAttributes;
}

export class EnvironmentStage extends Stage {
  constructor(scope: Construct, id: string, props: EnvironmentStageProps) {
    super(scope, id, props);

    const sesStack = new SesStack(this, 'SesStack', {
      // config: props.config,
      ses: props.ses,
    });

    const cognitoStack = new CognitoStack(this, 'CognitoStack', {
      userPoolName: props.config.cognito.userPoolName,
      env: props.config.env,
      sesDomainName: props.ses.domainAttr.zoneName,
    });

    const passwordlessStack = new PasswordlessStack(this, 'PasswordlessStack', {
      stage: id,
      userPool: cognitoStack.userPool, // Pass the userPoolId from CognitoStack to PasswordlessStack
    });

    const apiStack = new ApiStack(this, 'ApiStack', {
      name: props.envName,
      fido2Api: passwordlessStack.fido2Api,
    });
   
...

    cognitoStack.addDependency(sesStack);
    passwordlessStack.addDependency(cognitoStack);
    apiStack.addDependency(passwordlessStack);

And my passwordless.ts stack is just from the example with a slight change

import { Duration, RemovalPolicy, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { BillingMode } from 'aws-cdk-lib/aws-dynamodb';
import { Passwordless } from 'amazon-cognito-passwordless-auth/cdk';
import {
  RestApi,
} from 'aws-cdk-lib/aws-apigateway';
import TaggingStack from '../../tagging';

interface Props extends StackProps {
  readonly stage: string;
}

export class PasswordlessStack extends TaggingStack {
  passwordless: Passwordless;
  public readonly fido2Api: RestApi;

  constructor(scope: Construct, id: string, props: Props) {
    super(scope, id, props);

    this.passwordless = new Passwordless(this, "Passwordless", {
      // userPool: props.userPool,
      allowedOrigins: [
        // Modify these origins as per your requirements
        "http://localhost:5173",
        // ... other origins ...
      ],
      clientMetadataTokenKeys: ["consent_id"],
      magicLink: {
        // Adjust the sesFromAddress based on your setup
        sesFromAddress: "[email protected]",
        secretsTableProps: {
          removalPolicy: RemovalPolicy.DESTROY,
          billingMode: BillingMode.PAY_PER_REQUEST,
        },
      },
      userPoolProps: {
        removalPolicy: RemovalPolicy.DESTROY,
      },
      fido2: {
        authenticatorsTableProps: {
          removalPolicy: RemovalPolicy.DESTROY,
          billingMode: BillingMode.PAY_PER_REQUEST,
        },
        relyingPartyName: "Passwordless Fido2 Example",
        allowedRelyingPartyIds: [
          "localhost",
          // ... other relying party ids ...
        ],
        attestation: "none",
        userVerification: "required",
      },
      smsOtpStepUp: {},
      userPoolClientProps: {
        idTokenValidity: Duration.minutes(5),
        accessTokenValidity: Duration.minutes(5),
        refreshTokenValidity: Duration.hours(1),
        preventUserExistenceErrors: false,
      },
      logLevel: "DEBUG",
    });
    if (!this.passwordless.fido2Api) {
      throw new Error('fido2Api not available on Passwordless construct.');
    }
    this.fido2Api = this.passwordless.fido2Api;
  }
}


Removing the fido2Api: passwordlessStack.fido2Api, everything builds correctly.

diagram

So it seems I will have two Api Gateway and two CognitoPools

How do I expose the RestApi so that I can add my functions?

Any advice is much appreciated

Getting 403 for any button click on example UI

I have deployed example code on my aes account and when I am clicking on any button getting this 403 for POST operation done on cloudfront.net/changeme/sign-in-challenge

and after putting email address and click on send me magic link I am getting 403

I have cloned main branch

React Native Support

I have been trying to use this library for two days to setup WebAuthn for mobile applications using React Native.

What do I possibly need to use for RP ID in the App.tsx configuration? I keep getting Failed to list credentials: [Error: Missing RP ID] when executing the fido2StartCreateCredential function. What do I need to do? Is it because I don't have Cognito configured to use Custom Domains?

I have tried every URL and ID in the AWS console regarding API GW and Cognito for the past two days. This is extremely frustrating the documentation falls just short of explaining how to use this repo for mobile applications. Can someone point me in the right direction?

UI goes to second challenge window & invalid redirectUri

I have followed the workshop using Cloud9 and opening the UI through Preview Running Application. I am first met with a challenge to Sign in with passkey or Enter an e-mail, as expected. If I use the e-mail option it takes me to another challenge window. Is this expected behavior? I would have expected the entered email address to receive an email with the magic link.

Secondly if I follow the second challenge window to Sign-in with magic link I get an Invalid redirectUri error message. The invalid uri looks like https://67fc3ff145d745d2b32f69eb454f9f3d.vfs.cloud9.us-east-1.amazonaws.com/. Is there some place I should have set the redirect URI?

Question: DefineAuth Fido2 challenge

On 25 we issue a challenge if there are no sessions present, but then in the handleFido2Response 87 function we expect to have 0 sessions.
Is there something that I'm missing? Thx!

Possible security issue with logout

Hi,
I might have found possible security issue.

if you have two tabs open with your website that wrapped with PasswordlessContextProvider / PasswordlessComponent and logout from one tab, the second open tab will be still logged in and site function like user has never logged out. Obviously all cognito api fail. But if there is other sensitive information, it will be still available for third party who want to do some looksy though the site. Page refresh is setting things where they should be. It will log out also on scheduled token refresh, which can be hours away.

I would assume that apart of the schedule token refresh function that runs infrequent there should be token check function that runs every second and trigger unscheduled token refresh (or page refresh) if tokens are missing.

Type error in Verifying FIDO2 Challenge Response

When attempting to sign in using the passkey stored in the AuthenticatorsTable in the following format and algorithm RS256, a TypeError occurs.

  "jwk": {
    "M": {
      "alg": {
        "S": "RS256"
      },
      "e": {
        "B": "AQAB"
      },
      "kty": {
        "S": "RSA"
      },
      "n": {
        "B": "{omission}"
      }
    }
  },

Use Library Version: v0.13.1 (P.S. This credential is created at Library Version: v0.10.0)
and we confirm that this issue is observed in the following device:

  • Device: Surface Laptop 5
  • OS: Windows11 (23H2)
  • Passkey Store: Windows Hello

The traceback is as follows:

TypeError [ERR_INVALID_ARG_TYPE]: The "key.n" property must be of type string. Received an instance of Uint8Array
    at new NodeError (node:internal/errors:405:5)
    at validateString (node:internal/validators:162:11)
    at getKeyObjectHandleFromJwk (node:internal/crypto/keys:497:3)
    at prepareAsymmetricKey (node:internal/crypto/keys:549:22)
    at createPublicKey (node:internal/crypto/keys:613:5)
    at verifyChallenge (file:///var/task/index.mjs:177:96)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async addChallengeVerificationResultToEvent (file:///var/task/index.mjs:114:5)
    at async Runtime.handler (file:///var/task/index.mjs:1713:7) {
  code: 'ERR_INVALID_ARG_TYPE'
}

User attribute refresh after updateUserAttributes()

Good day!
Currently I am implementing account portal using you great library. Is there method to pull new updated userAttributes from cognito after invoking updateUserAttributes() without need it logout and login again?

userPoolClients not being created

All resources are being created as shown in the doc when using the sample code. Except for a userPoolClient. Not passing any existing userpool or client, so the stack should be creating the userPoolClients after creating the userpool.

Anyone know why it is not being created?

thanks!

Type 'IUserPool' is missing the following properties from type 'UserPool'

Not sure where is the problem. I am trying to make example work with existing user user pool as per https://github.com/aws-samples/amazon-cognito-passwordless-auth#basic-usage.

    const myUserPool =  cdk.aws_cognito.UserPool.fromUserPoolId(
      this,
      'InterfaceIDUserPool',
      'ca-central-1_d35Awy8hr'
    );
    /** Add Passwordless authentication to the User Pool */
    const passwordless = new Passwordless(this, "Passwordless", {
      userPool: myUserPool,     /** error on this */
      allowedOrigins: [
        process.env.WS_PREVIEW_URL!,
        // `https://${distribution.distributionDomainName}`,
      ],
      fido2: {
        allowedRelyingPartyIds: [
          process.env.WS_PREVIEW_HOST!,
          // distribution.distributionDomainName,
        ],
      },
.....

But I get an error Type 'IUserPool' is missing the following properties from type 'UserPool' on userPool

ERR_PACKAGE_PATH_NOT_EXPORTED on /custom-auth

Hello,

I'm following your basic usage guide and it's going great! Everything is working as expected until I try some customisation.

Trying this read me in order to customise the magic link email's html.

However I'm running into a node error when I try to deploy my stack:

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './custom-auth' is not defined by "exports" in /Users/******/Documents/GitHub/my-repo/node_modules/amazon-cognito-passwordless-auth/package.json at new NodeError (node:internal/errors:399:5) at exportsNotFound (node:internal/modules/esm/resolve:361:10) at packageExportsResolve (node:internal/modules/esm/resolve:641:13) at resolveExports (node:internal/modules/cjs/loader:565:36) at Function.Module._findPath (node:internal/modules/cjs/loader:634:31) at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1061:27) at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] (/Users/******/Documents/GitHub/my-repo/node_modules/@cspotcode/source-map-support/source-map-support.js:811:30) at Function.Module._load (node:internal/modules/cjs/loader:920:27) at Module.require (node:internal/modules/cjs/loader:1141:19) at require (node:internal/modules/cjs/helpers:110:18) { code: 'ERR_PACKAGE_PATH_NOT_EXPORTED' }

I'm using node: 18.15.0, amazon-cognito-passwordless-auth: 0.7.1

The magic link import looks like this:
import { magicLink } from 'amazon-cognito-passwordless-auth/custom-auth'

Have I missed something obvious here?

Edit:

After digging around there is a difference between the exports in the main package.json file.
The cdk module, which was working fine, has a default key like this:
"./cdk": { "default": "./dist/cdk/lib/cognito-passwordless.js", "types": "./cdk.d.ts" }

Whereas the custom-auth module, which I'm having trouble with, uses an import:
"./custom-auth": { "import": "./dist/cdk/custom-auth/index.js", "types": "./custom-auth/index.d.ts" }

Swapping the import for default gets past the ERR_PACKAGE_PATH_NOT_EXPORTED error but then runs into an ERR_REQUIRE_ESM one.

This might be because there are different compiler option targets?
cdk/lib targets ES2020 while cdk/custom-auth targets ES2022

I'm lost though and could use some advice on this! Any help would be amazing

Passwordless user sign up

Hello and thank you for this excellent solution!

I am trying to implement a passwordless sign in/sign up for a NextJS 13 application and would like to understand if it is possible to automatically "Sign Up" a user if they request a magic link and they do not have an existing Cognito identity?
If so is there a recommended method to implement that?

I was not able to find much in the other issues, or the documentation about this and want to apologize in advance I missed something.

I have implemented the solution and it works if my email is already in the user pool, but the pre-signup lambda trigger is never called for new users. I think maybe that could be the issue that I am facing but I haven't been able to get to the bottom of that that lambda is never triggered.

ERR_PACKAGE_PATH_NOT_EXPORTED

Good day,
I am trying to follow with example from https://github.com/aws-samples/amazon-cognito-passwordless-auth/blob/main/CUSTOMIZE-AUTH.md#1-create-your-own-lambda-function-using-this-library-and-call-configure
but when I add the sample code to my stack.ts right before I call to construct I have an error
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './custom-auth' is not defined by "exports" in C:\Users\user\work\AWS-cognito\cdk\node_modules\amazon-cognito-passwordless-auth\package.json
Without the sample injection code works as expected.

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { Passwordless } from "amazon-cognito-passwordless-auth/cdk";
import { magicLink } from "amazon-cognito-passwordless-auth/custom-auth";

const sesFromAddress: string =
  "Login Link <[email protected]>";

export { createAuthChallengeHandler as handler } from "amazon-cognito-passwordless-auth/custom-auth";

const defaultConfig = magicLink.configure();

magicLink.configure({
  async contentCreator({ secretLoginLink }) {
    return {
      html: {
        data: `<html><body><p>Your login link: <a href="${secretLoginLink}">sign in</a></p>This link is valid for ${Math.floor(
          defaultConfig.secondsUntilExpiry / 60
        )} minutes<p>Do not share this email with anyone.</p></body></html>`,
        charSet: "UTF-8",
      },
      text: {
        data: `Your login link: ${secretLoginLink}`,
        charSet: "UTF-8",
      },
      subject: {
        data: "Your login link",
        charSet: "UTF-8",
      },
    };
  },
});

class stackID extends cdk.Stack {
  passwordless: Passwordless;
  constructor(scope?: Construct, id?: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // const spa = cloudfrontServedEmptySpaBucket(this, "ExampleSpa");

    this.passwordless = new Passwordless(this, "Passwordless", {
      allowedOrigins: [
        "http://localhost:5173",

      ],
      clientMetadataTokenKeys: ["consent_id"],
      magicLink: {
        sesFromAddress,
        secretsTableProps: {
          removalPolicy: cdk.RemovalPolicy.DESTROY,
          billingMode: cdk.aws_dynamodb.BillingMode.PAY_PER_REQUEST,
        },
      },
      userPoolProps: {
        removalPolicy: cdk.RemovalPolicy.DESTROY,
      },
      fido2: {
        authenticatorsTableProps: {
          removalPolicy: cdk.RemovalPolicy.DESTROY,
          billingMode: cdk.aws_dynamodb.BillingMode.PAY_PER_REQUEST,
        },
        relyingPartyName: "Passwordless Fido2",
        allowedRelyingPartyIds: [
          "localhost",

        ],
        attestation: "none",
        userVerification: "required",
      },
      smsOtpStepUp: {},
      userPoolClientProps: {
        idTokenValidity: cdk.Duration.minutes(5),
        accessTokenValidity: cdk.Duration.minutes(5),
        refreshTokenValidity: cdk.Duration.hours(1),

        preventUserExistenceErrors: false,
      },
      // while testing/experimenting it's heplful to see e.g. full request details in logs:
      logLevel: "DEBUG",

  }
}

const app = new cdk.App();
const stack = new stackID(app, "test");

Question: Can the User Pool also support the USER_PASSWORD_AUTH flow for classic signin/signup?

Hey. The library is fantastic. Perhaps this is more related to Cognito than specifically to this library.

We've successfully deployed the CDK setup and now want to allow a standard password auth flow for signin and signup. Password will be a fallback option for signin, and a default option for signup.

  1. To achieve that, we've manually added USER_PASSWORD_AUTH in the user pool.
  2. We've enabled self-registration for signups (with password).
  3. After the user is authenticated, we prompt them to add the Fido2 credentials via fido2CreateCredential().

Are there any problems with this? If not, I'm happy to help with a PR to expose these options.

Support for Office365 Safe Links

Hi

I'm going through the documentation for this repo. One concern I have is around users who are using the Office 365 Safe Links feature. With this enabled the Safe Links system would access the magic link URL before the user does and therefore the link would always be invalid.

Are there suggestions for a workaround or does this sample somehow already cater for this?

Any suggestions would be great thanks!

Cannot change the function name for the fido challenge lambda.

We have a specific format for out lambda function names and so need to rename the fido and fido challenge lambdas. However, the fido2challengeFn merges in the functionProps for the fido2 function.

Should this be fixed to use the props provided for the fido challenge function or is there a reason it needs the fido function props?

this.fido2challengeFn = new cdk.aws_lambda_nodejs.NodejsFunction(
  this,
  `Fido2Challenge${id}`,
  {...},
  timeout: cdk.Duration.seconds(30),
  ...props.functionProps?.fido2,
  environment: {...}
);

should be changed to this I think:

this.fido2challengeFn = new cdk.aws_lambda_nodejs.NodejsFunction(
  this,
  `Fido2Challenge${id}`,
  {...},
  timeout: cdk.Duration.seconds(30),
  ...props.functionProps?.fido2challenge
  environment: {...}
);

Unable to use 1password for passkey

Whenever I try to register a passkey saved in 1Password, this error message is shown in the UI:

Failed to activate face or touch unlock: credential.response is not an instance of AuthenticatorAttestationResponse

1Password is successfully saving the passkey though.

'updatedCredentialsNotification' does not exist in type

Hello,
Thank you for this library, I have an issue when trying to build and synthasise the cdk stack

TSError: ⨯ Unable to compile TypeScript:
stack.ts:60:9 - error TS2322: Type '{ authenticatorsTableProps: { removalPolicy: cdk.RemovalPolicy.DESTROY; billingMode: cdk.aws_dynamodb.BillingMode.PAY_PER_REQUEST; }; relyingPartyName: string; allowedRelyingPartyIds: string[]; attestation: "none"; userVerification: "required"; updatedCredentialsNotification: { ...; }; }' is not assignable to type '{ relyingPartyName?: string | undefined; allowedRelyingPartyIds: string[]; attestation?: "direct" | "enterprise" | "indirect" | "none" | undefined; userVerification?: "discouraged" | "preferred" | "required" | undefined; ... 6 more ...; api?: { ...; } | undefined; }'.
  Object literal may only specify known properties, and 'updatedCredentialsNotification' does not exist in type '{ relyingPartyName?: string | undefined; allowedRelyingPartyIds: string[]; attestation?: "direct" | "enterprise" | "indirect" | "none" | undefined; userVerification?: "discouraged" | "preferred" | "required" | undefined; ... 6 more ...; api?: { ...; } | undefined; }'.

60         updatedCredentialsNotification: {
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  node_modules/amazon-cognito-passwordless-auth/cdk.d.ts:56:9
    56         fido2?: {
               ~~~~~
    The expected type comes from property 'fido2' which is declared here on type '{ userPool?: UserPool | undefined; userPoolClients?: UserPoolClient[] | undefined; userPoolProps?: Partial<UserPoolProps> | undefined; ... 7 more ...; logLevel?: "DEBUG" | ... 2 more ... | undefined; }'
stack.ts:141:24 - error TS2339: Property 'fido2NotificationFn' does not exist on type 'Passwordless'.

141 if (stack.passwordless.fido2NotificationFn) {
                           ~~~~~~~~~~~~~~~~~~~
stack.ts:143:24 - error TS2339: Property 'fido2NotificationFn' does not exist on type 'Passwordless'.

143     stack.passwordless.fido2NotificationFn,
                           ~~~~~~~~~~~~~~~~~~~

    at createTSError (/home/khine/go/src/github.com/aws-samples/amazon-cognito-passwordless-auth/end-to-end-example/cdk/node_modules/ts-node/src/index.ts:859:12)
    at reportTSError (/home/khine/go/src/github.com/aws-samples/amazon-cognito-passwordless-auth/end-to-end-example/cdk/node_modules/ts-node/src/index.ts:863:19)
    at getOutput (/home/khine/go/src/github.com/aws-samples/amazon-cognito-passwordless-auth/end-to-end-example/cdk/node_modules/ts-node/src/index.ts:1077:36)
    at Object.compile (/home/khine/go/src/github.com/aws-samples/amazon-cognito-passwordless-auth/end-to-end-example/cdk/node_modules/ts-node/src/index.ts:1433:41)
    at Module.m._compile (/home/khine/go/src/github.com/aws-samples/amazon-cognito-passwordless-auth/end-to-end-example/cdk/node_modules/ts-node/src/index.ts:1617:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1287:10)
    at Object.require.extensions.<computed> [as .ts] (/home/khine/go/src/github.com/aws-samples/amazon-cognito-passwordless-auth/end-to-end-example/cdk/node_modules/ts-node/src/index.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1091:32)
    at Function.Module._load (node:internal/modules/cjs/loader:938:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) {
  diagnosticCodes: [ 2322, 2339, 2339 ]
}

Subprocess exited with error 1

Looking at node_modules/amazon-cognito-passwordless-auth/cdk.d.ts is missing the updatedCredentialsNotification property

export declare class Passwordless extends Construct {
    userPool: cdk.aws_cognito.UserPool;
    userPoolClients?: cdk.aws_cognito.UserPoolClient[];
    secretsTable?: cdk.aws_dynamodb.Table;
    authenticatorsTable?: cdk.aws_dynamodb.Table;
    kmsKey?: cdk.aws_kms.IKey;
    createAuthChallengeFn: cdk.aws_lambda.IFunction;
    verifyAuthChallengeResponseFn: cdk.aws_lambda.IFunction;
    defineAuthChallengeResponseFn: cdk.aws_lambda.IFunction;
    preSignUpFn?: cdk.aws_lambda.IFunction;
    preTokenGenerationFn?: cdk.aws_lambda.IFunction;
    fido2Fn?: cdk.aws_lambda.IFunction;
    fido2challengeFn?: cdk.aws_lambda.IFunction;
    fido2Api?: cdk.aws_apigateway.RestApi;
    fido2ApiWebACL?: cdk.aws_wafv2.CfnWebACL;
    constructor(scope: Construct, id: string, props: {
        /** Your existing User Pool, if you have one already. This User Pool will then be equipped for Passwordless: Lambda triggers will be added. If you don't provide an existing User Pool, one will be created for you */
        userPool?: cdk.aws_cognito.UserPool;
        /** Your existing User Pool Clients, if you have them already. If you don't provide an existing User Pool Client, one will be created for you */
        userPoolClients?: cdk.aws_cognito.UserPoolClient[];
        /** If you don't provide an existing User Pool, one will be created for you. Pass any properties you want for it, these will be merged with properties from this solution */
        userPoolProps?: Partial<cdk.aws_cognito.UserPoolProps>;
        /** If you don't provide an existing User Pool Client, one will be created for you. Pass any properties you want for it, these will be merged with properties from this solution */
        userPoolClientProps?: Partial<cdk.aws_cognito.UserPoolClientOptions>;
        /**
         * The origins where you will be hosting your Web app on: scheme, hostname, and optionally port.
         * Do not include path as it will be ignored. The wildcard (*) is not supported.
         *
         * Example value: https://subdomain.example.org
         *
         * This property is required when using FIDO2 or Magic Links:
         * - For FIDO2 it is validated that the clientData.origin matches one of the allowedOrigins. Also, allowedOrigins is used as CORS origin setting on the FIDO2 credentials API.
         * - For Magic Links it is validated that the redirectUri (without path) in each Magic Link matches one of the allowedOrigins.
         */
        allowedOrigins?: string[];
        /**
         * Enable sign-in with FIDO2 by providing this config object.
         */
        fido2?: {
            relyingPartyName?: string;
            allowedRelyingPartyIds: string[];
            attestation?: "direct" | "enterprise" | "indirect" | "none";
            userVerification?: "discouraged" | "preferred" | "required";
            authenticatorAttachment?: "cross-platform" | "platform";
            residentKey?: "discouraged" | "preferred" | "required";
            /** Timeouts (in milliseconds) */
            timeouts?: {
                credentialRegistration?: number;
                signIn?: number;
            };
            authenticatorsTableProps?: TableProps;
            exposeUserCredentialIDs?: boolean;
            /**
             * Should users who previously registered FIDO2 credentials be forced to sign in with FIDO2?
             * FIDO2 is a phishing resistant signInMethod. As long as other signInMethods are still available,
             * there is a risk of phishing to the user, e.g. an attacker might trick the user into revealing the magic link.
             * Set to `true` to disallow other custom signInMethods if the user has one or more FIDO2 credentials.
             * @default false
             */
            enforceFido2IfAvailable?: boolean;
            api?: {
                /**
                 * The throttling burst limit for the deployment stage: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html
                 *
                 * @default 1000
                 */
                throttlingBurstLimit?: number;
                /**
                 * The throttling rate limit for the deployment stage: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html
                 *
                 * @default 2000
                 */
                throttlingRateLimit?: number;
                /**
                 * Create a log role for API Gateway and add this to API Gateway account settings?
                 * Set to false if you have already set this up in your account and region,
                 * otherwise that config will be overwritten.
                 *
                 * @default true
                 */
                addCloudWatchLogsRoleAndAccountSetting?: boolean;
                /**
                 * Add a WAF Web ACL with rate limit rule to the API deployment stage? The included Web ACL will have 1 rule:
                 * rate limit incoming requests to max 100 per 5 minutes per IP address (based on X-Forwarded-For header).
                 * If you want to customize the Web ACL, set addWaf to false and add your own Web ACL instead.
                 *
                 * @default true
                 */
                addWaf?: boolean;
                /**
                 * The rate limit per unique IP (using X-Forwarded-For header) that AWS WAF will apply: https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based-high-level-settings.html
                 *
                 * @default 100
                 */
                wafRateLimitPerIp?: number;
                /**
                 * Pass any properties you want for the AWS Lambda Rest Api created, these will be merged with properties from this solution
                 */
                restApiProps?: Partial<cdk.aws_apigateway.RestApiProps>;
            };
        };
        /**
         * Enable sign-in with Magic Links by providing this config object
         * Make sure you've moved out of the SES sandbox, otherwise you can only send few e-mails,
         * and only from and to verified e-mail addresses: https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html
         */
         ```

To reproduce:

git clone https://github.com/aws-samples/amazon-cognito-passwordless-auth.git
cd amazon-cognito-passwordless-auth/end-to-end-example/cdk
npm install
npx cdk synth

Usage of stepUpAuthenticationWithSmsOtp in plain web.

Hello,
I want to implement step up auth with SMS on some of the pages when a user is logged in.
The application that we are using it in is svelte. We tried to re-enact the same login as in react end to end example for step up auth but we are not able to do the same procedure.
When calling the function, the following api's are called:

  1. AWSCognitoIdentityProviderService.RespondToAuthChallenge with username as the auth parameter
  2. AWSCognitoIdentityProviderService.RespondToAuthChallenge four times. The first three times it works while the last time it fails with 400: "invalid username or password".

Could you please shine some light on how we would implement the same in plain web without the await able state that you have used in react.

spinner.gif not working in Next13

When trying to integrate this library into our frontend built with Next13, the spinner.gif included in the package is unable to resolve. I had to clone the repo and redeploy a new npm package without the spinner.gif in order for it to run without errors.

1Password passkey causes Illegal Invocation error

1Password passkey implementation intercepts webauthn call and replace it with its own implementation in 1Password Chrome extension.

The below line in

debug?.("Created credential:", {
causes Uncaught TypeError: Illegal invocation. which results in Illegal invocation shown in the UI when registering fido2 credential.

    debug?.("Created credential:", {
        credential,
        getTransports: response.getTransports?.(),
        getAuthenticatorData: response.getAuthenticatorData?.(),
        getPublicKey: response.getPublicKey?.(),
        getPublicKeyAlgorithm: response.getPublicKeyAlgorithm?.(),
    });

End-to-End Example: PassKey on other device fails

All deployed per instructions

Running on MacOS, and using an iPhone. and already set up "sign in with Face or Touch":
Screenshot 2023-08-06 at 3 26 08 PM

If one log logs in, and then attempt to "Register New Authenticator":

Screenshot 2023-08-06 at 3 14 27 PM

And Selects "Other Options":
Screenshot 2023-08-06 at 3 15 47 PM

and Chooses "iPhone, iPad or Android Device", on cease presented with a QR code (I messed with it so it won't work):
Screenshot 2023-08-06 at 3 16 30 PM

And once uses one phone to scan and add the passkey (per following instructions) it results in and "Operation Failed":
Screenshot 2023-08-06 at 3 18 02 PM

Not sure where to look to see what the error is, we're not seeing anything in the various Lambda logs.

End-to-end-example CDK deployment fails due to error

When I run npx cdk deploy, the deployment fails with the following error:

4:41:21 PM | CREATE_FAILED        | AWS::ApiGatewayV2::Stage                        | PLHttpApiPLDefaultStage9CEDBFC2
Cannot enable logging. Policy document length breaking Cloudwatch Logs Constraints, either < 1 or > 5120 (Service: AmazonApiGatewayV2; Status Code: 400; Error Code: Ba
dRequestException; Request ID: 3194b994-036d-4c86-b86f-efe54758e130; Proxy: null)

CDK Version: 2.96.2 (build 3edd240)

I've made various changes to the input variables available to me to see if they are contributing to hitting the size constraints. And the error persists.

Integration with Flutter App

I've integrated this package successfully with a web app for magic link authentication and am now building a flutter app that will need to use the same Cognito user pool. Is there an example somewhere of flutter integration that I can use for reference when building my mobile app?

Amplify cli end-to-end example

Hi, Thanks for bringing this gem to the world!

I tried to replicate the steps on the end to end example but with amplify but no luck (I'll keep trying!) but it would be great if there could be an end to end example using amplify cli.

first error was due to not having esbuild as a dependency and then after installing it got another error but this time from the esbuild execution.

So if this is something that will happen, I would truly appreciate it!.

Fails when setting addWaf to false

When you set the addWaf prop to false, it still tries to add the WafAssociation to the API Gateway despite it being undefined. It is failing with the error TypeError: Cannot read properties of undefined (reading 'attrArn')

The problem seems to be here:

if (props.fido2.api?.addWaf !== false) {
        this.fido2ApiWebACL = new cdk.aws_wafv2.CfnWebACL(
          scope,
          `Fido2ApiWebACL${id}`,
          {
            defaultAction: {
              allow: {},
            },
            scope: "REGIONAL",
            visibilityConfig: {
              cloudWatchMetricsEnabled: true,
              metricName: `Fido2ApiWebACL${id}`,
              sampledRequestsEnabled: true,
            },
            rules: [
              {
                name: "RateLimitPerIP",
                priority: 1,
                action: {
                  block: {},
                },
                visibilityConfig: {
                  sampledRequestsEnabled: true,
                  cloudWatchMetricsEnabled: true,
                  metricName: "RateLimitPerIP",
                },
                statement: {
                  rateBasedStatement: {
                    limit: props.fido2.api?.wafRateLimitPerIp ?? 100, // max 100 requests per 5 minutes per IP address
                    aggregateKeyType: "FORWARDED_IP",
                    forwardedIpConfig: {
                      headerName: "X-Forwarded-For",
                      fallbackBehavior: "MATCH",
                    },
                  },
                },
              },
            ],
          }
        );
      }
      new cdk.aws_wafv2.CfnWebACLAssociation(scope, `WafAssociation${id}`, {
        resourceArn: this.fido2Api.deploymentStage.stageArn,
        webAclArn: this.fido2ApiWebACL!.attrArn,
      });

where the CfnWebACLAssociation should be inside the IF statement.

Dynamic require of \"stream\" is not supported

Hello, I have an issue when trying to use a custom email as per the docs, https://github.com/aws-samples/amazon-cognito-passwordless-auth/blob/main/CUSTOMIZE-AUTH.md#customize-auth, here is my stack:

...
    // 👇 Passwordless
    this.passwordless = new Passwordless(this, 'Passwordless', {
      userPool: this.userPool,
      allowedOrigins: [
        'http://localhost:5173',
        // ... other origins ...
      ],
      clientMetadataTokenKeys: ['consent_id'],
      magicLink: {
        // Adjust the sesFromAddress based on your setup
        sesFromAddress: props.sesFromAddress,
        sesRegion: region,
        secretsTableProps: {
          removalPolicy: RemovalPolicy.DESTROY,
          billingMode: BillingMode.PAY_PER_REQUEST,
        },
      },
      fido2: {
        authenticatorsTableProps: {
          removalPolicy: RemovalPolicy.DESTROY,
          billingMode: BillingMode.PAY_PER_REQUEST,
        },
        relyingPartyName: 'Passwordless Fido2 Example',
        allowedRelyingPartyIds: ['localhost'],
        attestation: 'none',
        userVerification: 'required',
        updatedCredentialsNotification: {
          sesFromAddress: props.sesFromAddress,
          sesRegion: region,
        },
      },
      smsOtpStepUp: {},
      userPoolClientProps: {
        // perrty short so you see token refreshes in action often:
        idTokenValidity: Duration.minutes(5),
        accessTokenValidity: Duration.minutes(5),
        refreshTokenValidity: Duration.hours(1),
        // while testing/experimenting it's best to set this to false,
        // so that when you try to sign in with a user that doesn't exist,
        // Cognito will tell you that––and you don't wait for a magic link
        // that will never arrive in your inbox:
        preventUserExistenceErrors: false,

      },
      functionProps: {
        createAuthChallenge: {
          // Override entry, to point to your custom code:
          entry: path.join(__dirname, "../../../src/lambda/workflow/create-auth-challenge/index.ts"),
        },
      },
      logLevel: 'DEBUG',
    })
...

Where index.ts is:

import { magicLink } from "amazon-cognito-passwordless-auth/custom-auth";

// Export the solution's handler to be the handler of YOUR Lambda function too:
export { createAuthChallengeHandler as handler } from "amazon-cognito-passwordless-auth/custom-auth";

// Calling configure() without arguments retrieves the current configuration:
const defaultConfig = magicLink.configure();

// Swap in your own logic:
magicLink.configure({
  async contentCreator({ secretLoginLink }) {
    return {
      html: {
        data: `<html><body><p>Your secret sign-in link: <a href="${secretLoginLink}">sign in</a></p>This link is valid for ${Math.floor(
          defaultConfig.secondsUntilExpiry / 60
        )} minutes<p></p></body></html>`,
        charSet: "UTF-8",
      },
      text: {
        data: `Your secret sign-in link: ${secretLoginLink}`,
        charSet: "UTF-8",
      },
      subject: {
        data: "Your secret sign-in link",
        charSet: "UTF-8",
      },
    };
  },
});

But I get the following error:

{
    "errorType": "Error",
    "errorMessage": "Dynamic require of \"stream\" is not supported",
    "stack": [
        "Error: Dynamic require of \"stream\" is not supported",
        "    at file:///var/task/index.mjs:12:9",
        "    at node_modules/cbor/lib/commented.js (file:///var/task/index.mjs:2240:18)",
        "    at __require2 (file:///var/task/index.mjs:15:50)",
        "    at node_modules/cbor/lib/cbor.js (file:///var/task/index.mjs:3851:25)",
        "    at __require2 (file:///var/task/index.mjs:15:50)",
        "    at file:///var/task/index.mjs:5655:27",
        "    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)"
    ]
}

Disabling it, the email works without issue.

Any advice is much appreciated

Documentation: End-toEnd Example - SMS Setup & config for Step Up Auth

We were under the impression that one can setup SMS as the OTP, it's not clear how this is achieved via the sample UX.

Also In trying to use the "Step Up Auth":

Screenshot 2023-08-06 at 3 51 56 PM

It seem that needs to be more configured in SNS to enable this to work?

Additional documentation would be helpful here!

Thanks!

install-passwordless-local npm run task does not seem to work

Per the end-to-end-example, if one runs the install-passwordless-local in the hope that it will use the local implementation of the passswordles JSX components. Subsequently if one tries to run the example locally per rpm run dev, this error is displayed:

[plugin:vite:import-analysis] Failed to resolve entry for package "amazon-cognito-passwordless-auth". The package may have incorrect main/module/exports specified in its package.json.

is there a documented procedure to get this to work?

Feature request OAuth2

Would it be possible to add hooks to support OAuth2 for federated authentication from social / enterprise IdPs? I'm willing to help implement if needed, but I wanted to open this issue before doing any work on it to start a discussion to see if it's inline with the goals of this library.

The idea would be to start by updating the configure method and maybe add something like

Passwordless.configure({
  //...
  oAuth: {
    clientId: "user pool app client id",
    callbackUrl: "redirect_uri, must be specified in callbackUrls when setting up the cdk stack"
  }
})

Then add a hook for signing in with the various providers, e.g.

const { signInWithProvider } = usePasswordless()
//...
<button onClick=() => { signInWithProvider({ provider: "Google" }) }>Sign in with Google</button>

Then add some logic to the PasswordlessContextProvider to grab the access_token from the query string and add it to localstorage.

It's fairly easy for the user to set up their cdk stack to enable this like below, but it might be worth adding it to the passwordless stack and exposing an interface for enabling / configuring oAuth.

/** Create User Pool */
const userPool = new cdk.aws_cognito.UserPool(this, "UserPool", {
  signInAliases: {
    username: false,
    email: true,
  },
})

/** add auth domain to user pool, could also use a cognito domain */
const certificateArn = `arn:aws:acm:us-east-1:${process.env.AWS_ACCOUNT_ID}:certificate/${process.env.ACM_CERTIFICATE_ID}`;

const domainCert = cdk.aws_certificatemanager.Certificate.fromCertificateArn(this, 'domainCert', certificateArn);
userPool.addDomain('CustomDomain', {
  customDomain: {
    domainName: 'auth.example.com',
    certificate: domainCert,
  },
});

/** Add sign in with google functionality */
const googleSecret = cdk.aws_secretsmanager.Secret.fromSecretAttributes(
  this,
  "CognitoClientSecret",
  {
    secretCompleteArn:
     `arn:aws:secretsmanager:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:secret:${process.env.GOOGLE_SECRET_KEY_ID}`,
  }
).secretValue
new cdk.aws_cognito.UserPoolIdentityProviderGoogle(
  this,
  "Google",
  {
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecretValue: googleSecret,
    userPool,
  }
)

/** Add sign in with facebook functionality */
new cdk.aws_cognito.UserPoolIdentityProviderFacebook(this, "Facebook", {
    clientId: process.env.FACEBOOK_CLIENT_ID!,
    clientSecret: process.env.FACEBOOK_CLIENT_SECRET!,
    userPool,
  })

/** Add Passwordless authentication to the User Pool */
const passwordless = new Passwordless(this, "Passwordless", {
  userPool,
  //...
  //need this to enable callback urls
  userPoolClientProps: {
        oAuth: {
          callbackUrls: ["https://example.com/callback"],
          logoutUrls: ["https://example.com/logout"],
        }
      }
  }
})

Combined lambda functions?

Hello,

This isn't really an issue per se, but I don't see a discussion section on this repository.

I've been working on a very similar project since last fall, which hasn't been published yet. I started with separate lambda functions for challenge define/create/verify, but I later combined them into a single lambda. For a low-traffic site, or one that doesn't re-authenticate very often, a user could experience 3 cold-starts for the lambda functions - one for each. I've especially noticed that the first DynamoDB connection can take some time on a cold start. By using a single lambda, you only experience these delays once. This is a little less conceptually clear, but it could provide a better user experience.

The event that gets delivered to the function specifies what triggered the event. This is the relevant section from my code:

        switch (event.triggerSource) {
            case 'DefineAuthChallenge_Authentication':
                return define_handler(event, context, callback);
            case 'CreateAuthChallenge_Authentication':
                return create_handler(event, context, callback);
            case 'VerifyAuthChallengeResponse_Authentication':
                return verify_handler(event, context, callback);
            default:
                console.log("In the default case - no trigger source");
                return "No trigger source - not sure what to do";

With a combined function, you just attach the same function to each trigger on the user pool:

      upool.addTrigger(
        UserPoolOperation.DEFINE_AUTH_CHALLENGE,
        this.challengeHandler
      );

      upool.addTrigger(
        UserPoolOperation.CREATE_AUTH_CHALLENGE,
        this.challengeHandler
      );

      upool.addTrigger(
        UserPoolOperation.VERIFY_AUTH_CHALLENGE_RESPONSE,
        this.challengeHandler
      );

Thoughts?

Thanks,

Pau

authenticateWithSRP() does not work between 4pm and 5pm Pacific Time

Problem: authenticateWithSRP() does not work between 4pm and 5pm Pacific Time.

Repro steps:

import { usePasswordless } from "amazon-cognito-passwordless-auth/react";
...
const { requestSignInLink, authenticateWithSRP } = usePasswordless();
...
      await authenticateWithSRP({
        username: email,
        password,
      }).signedIn;

fails with the error

TIMESTAMP format should be EEE MMM d HH:mm:ss z yyyy in english.

Because the timestamp is formatted incorrectly in the request to AWS:

TIMESTAMP: "Sat Feb 17 24:03:00 UTC 2024"

It should be:

TIMESTAMP: "Sat Feb 17 00:03:00 UTC 2024"

Access denied when adding url from Netlify to allowed_origins

We have moved from hosting our vue-app in s3 to Netlify, now the passwordless feature fails with the following error:

{"__type":"UnexpectedLambdaException","message":"VerifyAuthChallengeResponse invocation failed due to error AccessDeniedException."}

There are no changes in the app besides moving the hosting

Documentation: Security Posture

Per the FAQ on Security Posture.

Per the advice - in the thought that this is a reference implementation:

Having said that, you should know that this solution was written by Amazon Cognito experts from AWS. We have run it through multiple internal reviews. We've used it for several of our projects. Amazon's application security team has reviewed and pentested it.

If would be great if such review findings, and pertinient details of pen-testing results were shared with applicable remediations tied to commits over time.

Whist we don't plan to use this solution as it stands but porting it to dotnet, inserting an overall API layer (via Lambda Minimal API) and leveraging fido2-net-lib to tie to our use case, it's good to track any changes here as a reference such that we can also make corresponding changes here.

We also note there in some paces scattered across the code there are mentions of security implications. An example of this is:

...
 /**
         * Should users who previously registered FIDO2 credentials be forced to sign in with FIDO2?
         * FIDO2 is a phishing resistant signInMethod. As long as other signInMethods are still available,
         * there is a risk of phishing to the user, e.g. an attacker might trick the user into revealing the magic link.
         * Set to `true` to disallow other custom signInMethods if the user has one or more FIDO2 credentials.
         * @default false
         */
enforceFido2IfAvailable?: boolean;
       

...

It would be good to collect such comments in one place?

At the end of the day its surely valuable that AWS customers can leverage the value-add of this information, whilst of cause performing their own due diligence.

how to override createEmailContent function

hi, this is a great library! wondering if there is a good way to override the createEmailContent function?
we would like to change the content of the email that is being sent for our users. thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.