Contact salesSign inSign up
AuthsignalAuthsignal
Product
Passwordless / multi-factor authentication (MFA)
Drop-in authentication
Risk-based authentication
Passkeys
Biometric authentication
WhatsApp OTP
Authenticator apps (TOTP)
Push authentication
SMS OTP
Email OTP
Magic links
See all authenticators
See less authenticators
Palm biometrics
Contactless payments & identity verification
Flexible integration modes
Pre-built UI
Low code
UI components
Customizable
Custom UI
Flexible
Digital credentials API Beta
Authenticate customers instantly using digital credentials
Session management
Keep users signed in across web and mobile after authentication
Fraud Controls
Rules and policies engine
Step-up authentication
No-code rule creation
Risk alerts
User observability
Audit trails
Dynamic linking
Why Authsignal?
Complete authentication infrastructure from enrollment to step-up auth, modular by design
Solutions
By USE CASE
View All
Account takeovers (ATO)
Go passwordless
Call center
SMS cost optimization
Existing apps
QR code payments
Step-up MFA
Palm biometrics payments
By INDUSTRY
View All
Financial services
Marketplace
e-Commerce
FinTech
Crypto
Healthcare
By Integration (identity provider)
Amazon Cognito
Azure AD B2C
Duende IdentityServer
Keycloak
Auth0
NextAuth.js
Custom identity provider
By ROLe
Engineers
Product
Passwordless / Multi-factor Authentication (MFA)
Flexible Integration Modes
Pre-built UI · Low code
UI Components · Customizable
Custom UI · Flexible
Digital credentials API Beta
Authenticate customers instantly using digital credentials
Session management
Issue JWT access and refresh tokens
Why Authsignal?
Plug in Authsignal to elevate your IDP — effortless integration with any architecture.
Drop-in Authentication
Risk-based authentication
Passkeys
Biometric authentication
WhatsApp OTP
SMS OTP
Email OTP
Magic links
Authenticator apps (TOTP)
Push notifications
Palm Biometrics
Contactless payments & identity verification
Fraud Controls
Rules and Policies Engine
Step-up Authentication
No Code Rule Creation
Risk Alerts
User Observability
Audit Trails
Use Cases
Financial services
Account takeovers (ATO)
Marketplace
Go passwordless
e-Commerce
Solutions
By Use Case
Account takeovers (ATO)
Go passwordless
Call center
SMS cost optimization
Existing apps
QR code payments
Step-up MFA
Palm Biometric Payments
View all Use Cases
By Industry
Financial services
Marketplace
e-Commerce
FinTech
Crypto
Healthcare
View all Industries
By Integration (identity provider)
Amazon Cognito
Azure AD B2C
Duende IdentityServer
Keycloak
Auth0
NextAuth.js
Custom identity provider
By Role
Engineers
PricingAboutDocsBlog
Schedule a call
Try Authsignal
AUS Flag

Authsignal secures millions of passkey transactions out of our hosted Sydney region.

AUS Flag

Authsignal secures millions of passkey transactions out of our hosted Sydney region.

Join us today!
Right icon
Blog
/
Current article
AWS
AWS Cognito
SDKs
Passwordless authentication

Supercharge Passwordless Authentication with AWS Cognito, Authsignal's Web SDK, and React

Ashutosh Bhadauriya
⬤
May 19, 2025
Share
Supercharge Passwordless Authentication with AWS Cognito and Authsignal's Web SDK, and React

In this guide, we'll explore how you can pair Cognito with Authsignal's MFA capabilities using the AWS SDK, Authsignal Web SDK, and React. This combination gives you the complete freedom to build your own custom UI while maintaining all the security standards.

Repository Structure

You can access the complete source code with all implementation details on our GitHub repository and also experience it firsthand through our live demo.

The repo consists of two main components:

  1. Backend Lambda Functions — Handles the Cognito integration with Authsignal
    • Create-auth-challenge  lambda for initiating custom challenges via Authsignal
    • Verify-auth-challenge-response lambda for validating Authsignal challenges
    • Define-auth-challenge and Pre-sign-up lambdas required for Cognito custom auth
  2. React Frontend — A complete implementation of the user-facing authentication experience
    • Sign-up flow with Authsignal MFA
    • Sign-in flow with Authsignal MFA

The code samples in this guide are extracted directly from this implementation.

Understanding the Integration Architecture

Here's a high-level overview of how the two services work together:

  1. User authentication starts with AWS Cognito as the identity provider
  2. MFA verification is handled by Authsignal for both sign-in and sign-up flows
  3. Custom authentication flows connect the two services through AWS Lambda triggers

Setting Things Up

Authsignal Portal Settings

  1. Enable and configure the authenticators you want to use (We’ll be using Email OTP initially, then will show how you can add other Authenticator options like Passkey, Magic Link and TOTP)

‍

  1. Grab your Tenant ID, region URL and API Key

‍

  1. Add these environment variables to both your frontend and Lambda functions. You’ll also need your User Pool ID, User Pool Client ID, and Region from your AWS Console.
    • Add these credentials to your frontend .env file (for client-side SDK)
    • Create separate environment variables for your Lambda functions (for server-side validation)

‍

// From .env

VITE_USER_POOL_ID=
VITE_USER_POOL_CLIENT_ID=
VITE_AWS_REGION=
VITE_AUTHSIGNAL_TENANT_ID=
VITE_AUTHSIGNAL_URL=
AUTHSIGNAL_SECRET=

‍

// From lambdas/.env

AUTHSIGNAL_SECRET=
AUTHSIGNAL_URL=
USER_POOL_ID=
USER_POOL_CLIENT_ID=

‍

User Pool Settings on AWS Console

For this example, there are several important settings to configure when creating a Cognito User Pool

  1. Choose 'Email’ as the sign-in option.

‍

  1. Choose ’No MFA’ as the MFA option. This can be implemented with Authsignal instead.

‍

  1. Enable ’self-registration’ so we can let users sign up directly from the SPA without going through a backend.

‍

  1. Disable the ’Cognito Hosted UI’. The SPA replaces this.

‍

  1. Select ’Public client’ for the app type. Also, select `Don’t generate a client secret’.

‍

Setting Up AWS Cognito with Authsignal

Backend Components

The integration requires several Lambda functions that handle different parts of the authentication flow:

1. Create Auth Challenge (Lambda)‍

This Lambda function creates a challenge when a user attempts to sign in or completes a sign-up. It communicates with Authsignal to generate a token:

// From lambdas/create-auth-challenge/handler.ts

import {Authsignal} from "@authsignal/node";
import {CreateAuthChallengeTriggerHandler} from "aws-lambda";

const authsignal = new Authsignal({
  apiSecretKey: process.env.AUTHSIGNAL_SECRET!,
  apiUrl: process.env.AUTHSIGNAL_URL!,
});

export const handler: CreateAuthChallengeTriggerHandler = async (event) => {
  const userId = event.request.userAttributes.sub;
  const email = event.request.userAttributes.email;

  const {token} = await authsignal.track({
    userId,
    action: "cognitoAuth",
    attributes: {
      email,
    },
  });

  event.response.publicChallengeParameters = {token};

  return event;
};

This Lambda function uses Authsignal's track method to create a challenge token that's passed back to the client.

‍

2. Verify Auth Challenge Response (Lambda)

This function verifies the challenge response received from the client:

// From lambdas/verify-auth-challenge-response/handler.ts

import {Authsignal} from "@authsignal/node";
import {VerifyAuthChallengeResponseTriggerHandler} from "aws-lambda";

const authsignal = new Authsignal({
  apiSecretKey: process.env.AUTHSIGNAL_SECRET!,
  apiUrl: process.env.AUTHSIGNAL_URL!,
});

export const handler: VerifyAuthChallengeResponseTriggerHandler = async (event) => {
  const userId = event.request.userAttributes.sub;
  const token = event.request.challengeAnswer;

  const {state} = await authsignal.validateChallenge({
    userId,
    action: "cognitoAuth",
    token,
  });

  event.response.answerCorrect = state === "CHALLENGE_SUCCEEDED";

  return event;
};

This function validates the token returned from the client-side Authsignal challenge to determine if the authentication challenge was successfully completed.

‍

3. Additional Required Cognito Lambda FunctionsThe following Lambda functions don't integrate directly with Authsignal but are required for Cognito custom authentication flows:

Define Auth Challenge:

Used to define when a challenge has been completed successfully and tokens should be issued.

Pre-Sign-Up:

Used to auto-confirm users during sign-up, since we are verifying their email as part of the sign-up flow itself.

‍

Frontend Implementation

Setting Up the AWS SDK

The frontend uses the AWS SDK to communicate with Cognito:

‍

// From src/lib/aws-auth.ts

import {
  CognitoIdentityProviderClient,
  InitiateAuthCommand,
  SignUpCommand,
  ConfirmSignUpCommand,
  RespondToAuthChallengeCommand,
  // Other imports...
} from "@aws-sdk/client-cognito-identity-provider";

const client = new CognitoIdentityProviderClient({
  region: import.meta.env.VITE_AWS_REGION,
});

‍

Authentication Flows

Sign-Up Flow

The sign-up process begins when a user enters their email:

‍

// From src/routes/sign-up/sign-up.tsx

const onSubmit = form.handleSubmit(async ({email}) => {
  setIsLoading(true);

  try {
    // Create user in Cognito with a random password (not actually used)
    await signUp({
      username: email,
      password: Math.random().toString(36).slice(-16) + "X", // Dummy value
      userAttributes: {
        email,
      },
    });

    // Immediately sign in to trigger MFA
    const signInResult = await signIn({
      username: email,
    });

    if (signInResult.nextStep === "CUSTOM_CHALLENGE") {
      // Obtain the Authsignal token returned by the Create Auth Challenge lambda
      const token = signInResult.challengeParameters?.token;
      
      if (!token) {
        throw new Error("Token is required to initiate challenge.");
      }
      
      // Set the Authsignal token on the client SDK
      authsignal.setToken(token);
      
      // Initiate an email OTP challenge
      await authsignal.email.enroll({email});
      
      // Navigate to OTP verification page
      navigate("/confirm-sign-up", {
        state: {
          email,
          session: signInResult.session,
          token
        }
      });
    }
  } catch (ex) {
    // Error handling
  }

  setIsLoading(false);
});

‍

Sign-In Flow

The sign-in process also begins with the user entering their email:

// From src/routes/login/login.tsx

const onSubmit = form.handleSubmit(async ({email}) => {
  setIsLoading(true);

  try {
    const signInResult = await signIn({
      username: email,
    });

    if (signInResult.nextStep === "CUSTOM_CHALLENGE") {
      // Obtain the Authsignal token returned by the Create Auth Challenge lambda
      const token = signInResult.challengeParameters?.token;
      
      if (token) {
        // Set the Authsignal token on the client SDK
        authsignal.setToken(token);
        
        // Initiate an email OTP challenge
        await authsignal.email.challenge();
      }
      
      navigate("/mfa", {
        state: {
          token, 
          email, 
          session: signInResult.session
        }
      });
    }
  } catch (ex) {
    // Error handling
  }

  setIsLoading(false);
});

‍

MFA Verification Flow

Both sign-up and sign-in processes use Authsignal for verification. While they use different routes (/confirm-sign-up for new users and /mfa for existing users), the verification mechanism is essentially the same:

// Verification process (used in both confirm-sign-up and mfa components)

// 1. The token is already set and challenge already sent from the login/signup page
// The component just sets the token again for subsequent operations
useEffect(() => {
  authsignal.setToken(token);
}, [token]);

// 2. When the user submits the verification code
const verifyResponse = await authsignal.email.verify({code});

if (verifyResponse.data?.token) {
  // 3. Complete the Cognito challenge with the Authsignal token
  const challengeResult = await respondToChallenge(
    email,
    session,
    verifyResponse.data.token
  );

  if (challengeResult.nextStep === "SIGN_IN_COMPLETE" && challengeResult.tokens) {
    // 4. Set the tokens and navigate to the secured area
    setTokens(challengeResult.tokens);
    navigate("/account/security");
  }
}

‍

Adding Different Authenticator Types

You can also add multiple types of authenticators. This example implementation includes four  options:

1. Email OTP

Email-based one-time passwords are used for both sign-in and sign-up verification.

2. Passkeys

Integrate modern, passwordless authentication with passkeys for a seamless login experience.

// From login page

const handlePasskeySignIn = useCallback(
  async ({autofill = false}: {autofill: boolean}) => {
    try {
      const signInResponse = await authsignal.passkey.signIn({action: "cognitoAuth", autofill});

      if (signInResponse.data?.token && signInResponse.data?.username) {
        const signInResult = await signIn({
          username: signInResponse.data.username,
        });

        if (signInResult.nextStep === "CUSTOM_CHALLENGE") {
          const challengeResult = await respondToChallenge(
            signInResponse.data.username,
            signInResult.session!,
            signInResponse.data.token
          );

          if (challengeResult.nextStep === "SIGN_IN_COMPLETE" && challengeResult.tokens) {
            setTokens(challengeResult.tokens);
            navigate("/account/security");
          }
        }
      }
    } catch {
      // Handle errors
    }
  },
  [navigate, toast],
);

‍

3. Email Magic Link

Implement passwordless magic link authentication:

// From src/routes/account/security/add-email-magic-link-dialog.tsx

const sendEmailMagicLink = async () => {
  const [{authsignalToken}, user] = await Promise.all([addAuthenticator(), getCurrentUser()]);

  authsignal.setToken(authsignalToken);

  const email = user.Username;

  if (!email) {
    signOut();
    return;
  }

  // Send the magic link email
  await authsignal.emailML.enroll({email});

  // Check if the user has clicked the link
  const verificationStatusResponse = await authsignal.emailML.checkVerificationStatus();

  if (verificationStatusResponse.data?.isVerified) {
    queryClient.invalidateQueries({queryKey: ["authenticators"]});
    setIsOpen(false);
  }
};

‍

4. TOTP (Authenticator Apps)

Add support for time-based one-time passwords with popular authenticator apps:

const handleGenerateQRCode = async () => {
  const {authsignalToken} = await addAuthenticator();
  authsignal.setToken(authsignalToken);
  const totpEnrollResponse = await authsignal.totp.enroll();
  // Display QR code to user
};

const handleSubmit = form.handleSubmit(async ({code}) => {
  const verifyResponse = await authsignal.totp.verify({code});
  // Handle verification result
});

‍

Conclusion

This implementation demonstrates how AWS SDK with Authsignal Web SDK gives you complete control over your authentication flows and UI design. By leveraging Lambda triggers and custom challenge flows, you can create a tailored authentication experience. This approach provides the flexibility to implement exactly what your application requires without compromising on user experience or security.

Question icon
Have a question?
Talk to an expert
NewsletterDemo PasskeysView docs
AWS
AWS Cognito
SDKs
Passwordless authentication

You might also like

How to add push authentication to your app with Authsignal and React Native
Push authentication
React native
Node.js
Multi-factor authentication
Guides

How to add push authentication to your app with Authsignal and React Native

March 27, 2026
BSP Circular 1213: Philippine banks must replace SMS OTPs by June 2026
BSP Circular 1213
Philippine banking
SMS OTP
Risk based authentication

BSP Circular 1213: Philippine banks must replace SMS OTPs by June 2026

March 18, 2026
How to add adaptive MFA and passkeys to any web app with Authsignal and Lambda@Edge
AWS
Authentication
Security

How to add adaptive MFA and passkeys to any web app with Authsignal and Lambda@Edge

March 10, 2026

Secure your customers’ accounts today with Authsignal

Passkey demoCreate free account

Authsignal delivers passwordless and multi-factor authentication as a service. Focused on powering mid-market and enterprise businesses to rapidly deploy optimized good customer flows that enable a flexible and risk-based approach to authentication.

AICPA SOCFido Certified
LinkedInTwitter
Passwordless / multi-factor authentication (MFA)
Pre-built UI (low code)UI components (customizable)Custom UI (flexible)
Why Authsignal?
Drop-in authentication
Risk-based authentication PasskeysBiometric authenticationWhatsApp OTPSMS OTPEmail OTPMagic linksAuthenticator apps (TOTP)Push authenticationPalm biometricsDigital Credential Verification API
Rules and policies engine
User observability
Industries
Financial services
Marketplace
e-Commerce
FinTech
Crypto
View all industries
Teams
Engineers
Use cases
Account takeovers (ATO)
Go passwordless
Call center
SMS cost optimization
Existing apps
View all use cases
Identity providers (IDPs)
Amazon Cognito
Auth0
Azure AD B2C
Custom identity provider
Duende IdentityServer
Keycloak
NextAuth.js
Integrations
ASP.NET
C#
Java
Node.js
Open ID Connect (OIDC)
PHP
Python
React
Ruby
Ruby on Rails
Compare
Twilio Verify vs AuthsignalAuth0 vs AuthsignalAWS Cognito vs Authsignal + AWS Cognito
Resources
BlogDeveloper docsFree Figma mobile passkeys templateFree Figma desktop passkeys templateFree Figma webapp passkeys template
Company
About usWhy AuthsignalCareersPress releasesPartnersContact us
What is
SMS OTP
Risk Based Authentication
IP Spoofing
Passwordless authentication
Multi-Factor Authentication (MFA)
United States
+1 214 974-4877
Ireland
+353 12 676529
Australia
+61 387 715 810
New Zealand
+64 275 491 983
© 2026 Authsignal - All Rights Reserved
Terms of servicePrivacy policySecuritySystem statusCookies