Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWSMobileClient Cognito custom passwordless email auth - escape hatch from PreSignUpTriggerHandler to sign in flow #5437

Closed
ghost opened this issue Sep 13, 2024 · 1 comment

Comments

@ghost
Copy link

ghost commented Sep 13, 2024

Describe the bug
Note: I originally opened this as bug report, but since found some workarounds. This is now likely more of a general question or a feature request.

I'm attempting to implement a custom auth passwordless flow where the user signs in using a 6-digit code emailed to them.

It's largely based off this example from the docs. I'm using AWSMobileClient, not Amplify. This is within an iOS app.

The current problem is providing a single-flow sign in where if an account exists, the user is signed in, otherwise an account is created and then signed in. Unfortunately, this isn't the default behavior.

To handle this, I set up a PreSignUpTriggerHandler that uses listUsers to check if the account exists - if it does exist, do not allow the sign up. If it doesn't exist, allow the sign up.

How can I modify the pre-sign up handler so if the account does exist, it does not allow the sign up but jumps into the sign in flow? That way, I could always call AWSMobileClient.signUp() and handle it accordingly on the server.

The Cognito stack:

import {
    AccountRecovery,
    Mfa,
    UserPool,
    UserPoolClient
} from 'aws-cdk-lib/aws-cognito'
import {
    App,
    CfnOutput,
    Stack,
    StackProps
} from 'aws-cdk-lib'
import { join } from 'path'
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'
import { RetentionDays } from 'aws-cdk-lib/aws-logs'
import { Runtime } from 'aws-cdk-lib/aws-lambda'
import { PolicyStatement } from 'aws-cdk-lib/aws-iam'



const cognitoEmailAddress = '[email protected]'
const cognitoEmailAddressArn = 'arn:xxxxxx'



export class CognitoStack extends Stack
{
    public userPool: UserPool
    public userPoolClient: UserPoolClient

    

    constructor(
        scope: App,
        id: string,
        props: StackProps
    )
    {
        super(scope, id, props)

        // Create Lambda functions.
        ...

        // Create a Cognito User Pool.
        this.userPool = new UserPool(this, 'CustomAuthUserPool',
        {
            accountRecovery: AccountRecovery.EMAIL_ONLY,
            autoVerify:
            {
                email: true,
                phone: false
            },
            lambdaTriggers:
            {
                preSignUp: preSignUpLambda,
                createAuthChallenge: createAuthLambda,
                defineAuthChallenge: defineAuthLambda,
                verifyAuthChallengeResponse: verifyAuthLambda
            },
            mfa: Mfa.REQUIRED,
            mfaSecondFactor:
            {
                otp: true,
                sms: false
            },
            selfSignUpEnabled: true,
            signInAliases:
            {
                email: true,
                phone: false,
                preferredUsername: false,
                username: false
            },
            signInCaseSensitive: false,
            standardAttributes:
            {
                email:
                {
                    required: true,
                    mutable: false
                }
            }
        })

        // Create a User Pool Client.
        this.userPoolClient = new UserPoolClient(this, 'UserPoolClient',
        {
            userPool: this.userPool,
            authFlows:
            {
                adminUserPassword: false,
                custom: true,
                userPassword: false,
                userSrp: false
            },
            preventUserExistenceErrors: true
        })

        new CfnOutput(this, 'UserPoolId', { value: this.userPool.userPoolId })
        new CfnOutput(this, 'UserPoolClientId', { value: this.userPoolClient.userPoolClientId })
    }
}

The preSignUp Lambda:

import {
    CognitoIdentityProviderClient,
    ListUsersCommand,
    ListUsersCommandInput,
    ListUsersCommandOutput
} from '@aws-sdk/client-cognito-identity-provider'
import {
    PreSignUpTriggerEvent,
    PreSignUpTriggerHandler
} from 'aws-lambda'



export const handler: PreSignUpTriggerHandler = async (
    event: PreSignUpTriggerEvent
) =>
{
    console.log('preSignUp event.request: ', JSON.stringify(event.request, null, 2))

    const email = event.request.userAttributes.email

    if (!email)
    {
        throw new Error("Email attribute is required.")
    }

    const input: ListUsersCommandInput =
    {
        UserPoolId: 'us-east-2_xxxxxx',
        Filter: `email = \"${email}\"`
    }

    try
    {
        const client = new CognitoIdentityProviderClient()
        const command = new ListUsersCommand(input)
        const response: ListUsersCommandOutput = await client.send(command)

        if (
            typeof response.Users !== 'undefined'
            && response.Users.length > 0
        )
        {
            // TODO: How to sign in the user instead?
            console.log("User with this email already exists.")
            throw new Error("Email already registered.")
        }
        else
        {
            console.log("User not found, allowing sign-up.")
            event.response.autoConfirmUser = true
            return event
        }
    }
    catch (error)
    {
        console.error("Error in PreSignUp Lambda: ", error)
        throw new Error("Error checking email existence.")
    }
}

To Reproduce
Steps to reproduce the behavior:
See the above explanation please.

Observed Behavior
The email attribute is not available in the event sent to the Lambda trigger.

Expected Behavior
The email attribute should be present.

Stack Trace
There is no stack trace specifically related to this. The Lambda will crash when attempting to send an email, but that is because email is undefined, so the stack trace is not directly relevant to the issue, but rather a side-effect.

Code Snippet
See the above explanation please.

Unique Configuration
If you are reporting an issue with a unique configuration or where configuration can make a difference in code execution (i.e. Cognito) please provide your configuration. Please make sure to obfuscate sensitive information from the configuration before posting.

Areas of the SDK you are using (AWSMobileClient, Cognito, Pinpoint, IoT, etc)?
AWSMobileClient, Cognito

Screenshots
N/A

Environment(please complete the following information):

  • SDK Version: AWSiOSSDKV2 2.37.1, "@aws-sdk/client-sesv2": "3.650.0", "aws-cdk-lib": "2.156.0"
  • Dependency Manager: Swift Package Manager

Device Information (please complete the following information):

  • Device: iOS Simulator
  • iOS Version: 17.5

Additional context
N/A

Relevant Console Output
N/A

Logs
N/A

@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify team member labels Sep 13, 2024
@ghost ghost changed the title AWSMobileClient custom passwordless email auth - email is missing from user attributes AWSMobileClient Cognito custom passwordless email auth - email is missing from user attributes Sep 13, 2024
@ghost ghost changed the title AWSMobileClient Cognito custom passwordless email auth - email is missing from user attributes AWSMobileClient Cognito custom passwordless email auth - escape hatch from PreSignUpTriggerHandler to sign in flow Sep 13, 2024
@ghost ghost closed this as completed Sep 14, 2024
@github-actions github-actions bot removed pending-triage Issue is pending triage pending-maintainer-response Issue is pending response from an Amplify team member labels Sep 14, 2024
Copy link

This issue is now closed. Comments on closed issues are hard for our team to see.
If you need more assistance, please open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

0 participants