aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

PreAuthentication trigger not triggered in Custom Auth Flow #5318

Open armandabric opened 4 years ago

armandabric commented 4 years ago

Which Category is your question related to? Auth

What AWS Services are you utilizing? Cognito

Provide additional details e.g. code snippets

I have an issue with the Cognito PreAuthentication trigger not triggered when an user sign-in

We are using a Cognito user pool with only CUSTOM_AUTH_FLOW_ONLY auth to do a passwordless authentication system.

I my CloudFormation template I have configured a lambda to handle the trigger :

  # ...

  PreAuthentication:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: build/lambda-triggers/pre-authentication/
      Handler: pre-authentication.handler
      Runtime: nodejs12.x

  # ...

PreAuthenticationInvocationPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt PreAuthentication.Arn
      Principal: cognito-idp.amazonaws.com
      SourceArn: !GetAtt UserPool.Arn

  # ... 

  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: !Ref UserPoolName
      Schema: # ...
      Policies: # ...
      UsernameAttributes:
        - email
      MfaConfiguration: "OFF"
      LambdaConfig:
        CreateAuthChallenge: !GetAtt CreateAuthChallenge.Arn
        DefineAuthChallenge: !GetAtt DefineAuthChallenge.Arn
        PreSignUp: !GetAtt PreSignUp.Arn
        PreAuthentication: !GetAtt PreAuthentication.Arn # Here 
        VerifyAuthChallengeResponse: !GetAtt VerifyAuthChallengeResponse.Arn
        PostAuthentication: !GetAtt PostAuthentication.Arn

# ...

  UserPoolClient:
    Type: "AWS::Cognito::UserPoolClient"
    Properties:
      ClientName: email-auth-client
      GenerateSecret: false
      UserPoolId: !Ref UserPool
      ExplicitAuthFlows:
        - CUSTOM_AUTH_FLOW_ONLY

For now the lambda implementation is useless:

import { CognitoUserPoolTriggerHandler } from "aws-lambda";

export const handler: CognitoUserPoolTriggerHandler = async (
  event,
  context
) => {
  console.log(event, context);

  // return event;
  throw new Error("Is this really executed?");
};

When I deploy the lambda it appear correctly in the Cognito UI as the registered trigger.

But impossible to have the function to be executed on sign in (nor sign up):

const newCognitoUser = await AmplifyAuth.signIn({
    username: email,
    password: "",
    validationData: { some_data: "foo_bar" },
});

# No PreAuthentication lambda triggered
# Others triggers works perfectly.

Is this an issue Aplify or is that an issue with my understanding of the auth process?

iartemiev commented 4 years ago

Have you tried utilizing the Amplify CLI to configure Cognito with a Lambda trigger?

armandabric commented 4 years ago

I did not have try the Amplify Cli but the Cognito GUI and Cloud Formation deployment template. Is the CLI is mandatory?

mauerbac commented 4 years ago

Hi @Spy-Seth - the CLI flow is recommended. You could spin up a sample project with the CLI and let us know if that works? This will help determine if it's a potential issue. This doc will help you get started -> https://docs.amplify.aws/cli/usage/lambda-triggers#cognito-lambda-triggers

shouze commented 4 years ago

@mauerbac I work with @Spy-Seth and we have an example repository to reproduce the issue.

BTW we've not used Amplify CLI here but terraform. At the end it's the very same I guess. We've also used a cognito cloudformation stack in another try and every time we have this same issue with the never invoked pre authentication lambda.

Could it be related to auth flows we allow?

elorzafe commented 4 years ago

@shouze can you see the validationData as part of the data request?

ashika01 commented 4 years ago

@shouze : I dont see how the validationData is being passed in. From the code, I only see email is sent in.

For pre authentication or pre sign up, it is important to pass in validationData - https://docs.amplify.aws/lib/auth/advanced/q/platform/js#pre-authentication-and-pre-sign-up-lambda-triggers

shouze commented 4 years ago

@ashika01 ok yes will try that, If I remember well we've already tried and that didn't works but I prefer to double check.

ashika01 commented 4 years ago

@shouze yeah let us know how it goes.

shouze commented 4 years ago

@ashika01 I've tried with no more luck (I've tried both variants with username as a string and validation data as third argument, validation data in signinOpts as first argument, nor more arguments) https://github.com/Spy-Seth/cognito-passwordless-bug/commit/d0be033d9f987d125ffed5344a955e684223f7f0#diff-da5998deda0a56bb613b4fe12ae4eef9R22-R29

Moreover I can confirm you that my browser network inspector as the proof that the payload effectively contains validation data (but, transformed as client metadata): image

as I have it as validationData until initiateAuth get called into the debugger: image

transformed/normalized later on: image

Here it was just about the signin part, same thing with signup.

shouze commented 4 years ago

According to the doc this normalization sounds OK https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html#API_InitiateAuth_RequestSyntax

Chewbee commented 4 years ago

On my side, impossible to have preAuth Working It prevents the logging leaving no trace in cognito, no log log for the Lambda like if it was never called I enable preAuthTrigger through CloudFormation the code now is the sample from the doc :

exports.handler = async (event, context, callback) => {

    if (event.callerContext.clientId === "user-pool-app-client-id-to-be-blocked") {

        var error = new Error("Cannot authenticate users from this user pool app client");

        // Return error to Amazon Cognito

        callback(error, event);

    }

    // Return to Amazon Cognito

    callback(null, event);
};

The lambda is defined and exported in a CDK Stack The Userpool is defined and its triigered attached in cloud Formation PosAuth works fine BTW the lambda works fine tested from lambda console

Very annoying

armandabric commented 4 years ago

After contacting the AWS support it's a know bug: the pre-authentification hook is not called on custom auth flow

shouze commented 4 years ago

Would be nice to see it fixed though

elorzafe commented 4 years ago

I can confirm this is not an issue with Amplify library, is a restriction by Cognito User Pools.

Why do you need validation data and custom challenge, those seems to be similar to me. Maybe I am missing another use case.

Thanks

rennehir commented 4 years ago

@Spy-Seth have you heard more regarding the bug? I am also finding that the pre-authentication trigger is not called while using custom auth flow. This was the only thread I could on the topic so far.

armandabric commented 4 years ago

@reedyrm We have no news about this bug. We are waiting to see if this bug is resolved

@elorzafe I miss your message sorry. We a are looking to inject validation data (a token in our case) in the login process to be able to open the passwordless link in another device than the one than generate it: start to use you mobile, check your mail on the desktop and click on it, and get back back to the same point.

shouze commented 3 years ago

here is the way, how I did multienv support:


  "Conditions": {

    "CurrentEnvIsLive": {

      "Fn::Equals": [

        {

          "Ref": "env"

        },

        "live"

      ]

    }

    ....

  },

.....

        "VpcConfig": {

          "Fn::If": [

            "CurrentEnvIsLive",

            {

              "SecurityGroupIds": [

                "sg-xx"

              ],

              "SubnetIds": [

                "subnet-xxx"

              ]

            },

            {

              "SecurityGroupIds": [

                "sg-yy"

              ],

              "SubnetIds": [

                "subnet-yyy"

              ]

            }

          ]

        },

Are you sure this is related to current topic

mmeylan commented 3 years ago

This bug is killing us. We use a custom cognito authentication flow (magic links) and we wanted to allow our users to choose to get their magic link via email or sms. We wanted to use ClientMetadata from Auth.signIn in order to convey the information {sendLinkViaSms: boolean} so that our custom lambdas know wether to send an email or a SMS.

The only trigger called with ClientMetadata on InitiateAuth is PreAuthentication (documented here). Our plan was to update a user custom attribute with this information, so that the trigger responsible to send the magic link (CreateAuthChallenger) could read that info and send the message on the approriate channel.

But with this bug, we have no way of transmitting business logic information from the user to the cognito triggers, seriously limiting our feature development capabilities.

shouze commented 3 years ago

This bug is killing us. We use a custom cognito authentication flow (magic links) and we wanted to allow our users to choose to get their magic link via email or sms. We wanted to use ClientMetadata from Auth.signIn in order to convey the information {sendLinkViaSms: boolean} so that our custom lambdas know wether to send an email or a SMS.

The only trigger called with ClientMetadata on InitiateAuth is PreAuthentication (documented here). Our plan was to update a user custom attribute with this information, so that the trigger responsible to send the magic link (CreateAuthChallenger) could read that info and send the message on the approriate channel.

But with this bug, we have no way of transmitting business logic information from the user to the cognito triggers, seriously limiting our feature development capabilities.

@mmeylan we are exactly in the same situation! You've made a very good summary of the situation :heart:. As we're not live in production yet on our side... it's ok-ish. We have alternatives in mind like Auth0 or NextAuth or even Symfony 5.2 incoming magic link.

@elorzafe is it planned at some point to fix it? Is it in any roadmap? Documentation should be at least updated to warn that it's a dead end for every people that try to implement magic links no? Especially since this post has been published more than 1 year ago. The related github repository had some mark of interest (> 100 stars & probably a magnitude of interested people that have not starred it).

sammartinez commented 3 years ago

All thank you for the feedback in regards to this. As @elorzafe has callout, this issue is not related to Amplify and more so for Amazon Cognito. Amplify is used as a proxy to AWS resources such as Amazon Cognito. We will look to talk to them internally about this however, I do recommend reaching out to Amazon Cognito via their forums.

shouze commented 3 years ago

@sammartinez copy that, so we can close this issue @Spy-Seth. We are agree, this is pure Cognito issue, not Amplify related.

I've found a related thread on Cognito forum, other people that reproduces but not answered. So if you can internally do something :pray: passwordless is a big trend at the moment (as WebAuthn will probably be the next one).

R-iskey commented 3 years ago

Do you have some updates guys?

campidelli commented 2 years ago

I got this from AWS support today (almost 2 years after this issue was open):

Thanks for your patience. I can confirm that the service team is still working on the request to invoke preAuth trigger when custom auth flow is used. I have added your case to the request to better prioritize the development. I am checking if there is a workaround on this.

I see that the preAuth Lambda will be triggered only if PASSWORD_VERIFIER challenge is used as the first challenge in the custom auth flow.

richmengsix commented 2 years ago

Hit this issue as well.. Would love to see this issue get fixed as it's also impacting my passwordless workflow.

lukasburns commented 2 years ago

Same here, any updates on when this could be fixed?

ayushman2001 commented 1 year ago

Is there any development on this issue?

WillUK99 commented 1 year ago

This bug is killing us. We use a custom cognito authentication flow (magic links) and we wanted to allow our users to choose to get their magic link via email or sms. We wanted to use ClientMetadata from Auth.signIn in order to convey the information {sendLinkViaSms: boolean} so that our custom lambdas know wether to send an email or a SMS.

The only trigger called with ClientMetadata on InitiateAuth is PreAuthentication (documented here). Our plan was to update a user custom attribute with this information, so that the trigger responsible to send the magic link (CreateAuthChallenger) could read that info and send the message on the approriate channel.

But with this bug, we have no way of transmitting business logic information from the user to the cognito triggers, seriously limiting our feature development capabilities.

any news on this? :)

ghost commented 1 year ago

any update on it?

I implemented custom challenge mode to add passwordless login with sms-code and wanted to prevent fraud sms sending in case recaptcha is missing (sms is sent in create challenge trigger) . the only trigger that seems to fit this purpose is pre auth since it does have access to metadata that client can send, but this trigger is not called with custom challenge mode...

kelvin-iimmpact commented 1 year ago

Hey @mordechai-dror i have the same issue, did you figure out a workaround?

ghost commented 1 year ago

@kelvin-iimmpact The current solution is that the client first goes to dynamodb table, where each user has its own account record, updates there the required metadata, and only after it sends auth request, so the custom challenge lambda can query dynamodb by cognito user sub/username and retrieve the metadata. it means that the init auth process is split into two separate requests, which is not perfect, but at least it works

talesporto commented 11 months ago

Same issue here guys. Any news on it?

olivier-keeper commented 11 months ago

Same issue here, this bug has been a problem for three years and we still have no resolution in sight.

nadetastic commented 11 months ago

Hi all,

Due to the activity on this issue, I'm gonna re-open this in order to provide communication regarding support for Cognito Triggers with Custom Auth Flows. As mentioned in some of the above comments( here and here), this is a limitation from Cognito. Will provide any information on this issue once there is an update form Cognito.

nadetastic commented 11 months ago

Hi @olivier-keeper @talesporto I would like to understand your use case better to see if there are any work arounds - could you add context to what you are looking to achieve here?

ayushman2001 commented 11 months ago

I suggest curating the requirements and solutions/hacks/workaround for them discussed in this thread, in a doc/blog. It will to people looking for a solution.

huss3in commented 10 months ago

@ayushman2001 @nadetastic Many use cases here requires sending clientMetadata/ValidationDate with the sign-in in the CUSTOM_AUTH flow for implementing passwordless signin. We are also looking for a solution for this problem if you have any suggestion? Thanks

flodaniel commented 10 months ago

The problem is really the restriction of clientMetadata not being passed to all lambda triggers. I have looked around and could not find a logical reason why the cognito team restricts this.

olivier-keeper commented 10 months ago

Hi @olivier-keeper @talesporto I would like to understand your use case better to see if there are any work arounds - could you add context to what you are looking to achieve here?

I appreciate the opportunity to delve deeper into this issue. We've been grappling with a significant limitation in AWS Cognito's custom authentication flow, specifically concerning the pre auth Lambda trigger's inability to discern which custom flow is being initiated. This is not just an inconvenience; it's a critical bottleneck that's been persisting for three years.

Amazon documentation indicates that clientMetadata should be passed to the lambda handler for the following triggers (Define Auth challenge , Create Auth challenge and Verify Auth challenge) but in reality the content of clientMetadata is never passed to the lambda.

Despite this bug, we've successfully implemented passwordless authentication. However, the rigidity of the system that result from this bug precludes us from adding additional authentication flows, such as one that would allow admin users to impersonate customers for support purposes, without convoluting our architecture. In effect, only one custom_flow can be implemented for any given pool until this issue is resolved.

This constraint has compelled us to rely on an external database for storing authentication metadata. Not only has this approach been cost-intensive to implement and deploy, but it has also introduced unnecessary complexity into our infrastructure, complicating our deployment process significantly.

Ideally, we would leverage clientMetadata to streamline this process, but the current bug hinders us. This would not only simplify our authentication flow but also enhance our system's security and performance by reducing the dependency on external databases, which could be a potential vector for vulnerabilities.

Could your team, @nadetastic , consider providing an enhancement where the aforementioned triggers can receive information about the chosen authentication flow, or could you suggest an alternative approach that does not involve external databases?

Thank you for addressing this long-standing issue. Your guidance on potential workarounds or planned updates would be greatly appreciated, as it would significantly impact our development strategy and resource allocation.

ayushman2001 commented 10 months ago

Thanks for highlighting this in detail @olivier-keeper . we have been too hamstrung with these similar issues.

mateusbase commented 7 months ago

Guys, I have a passwordless authentication flow with otp, and I need to implement a flow coming from a custom SSO to log in with the token.

The solution I found was: Since preAuth is only triggered in the password flow, we will have the frontend send a fixed password; so in preAuth I update it in cognito, that is, it will always pass PASSWORD_VERIFIER due to the fact that we are forcing the update, and then just continue with the other challenges to validate the rest of the flow.

// Frontend
const password = "DoesntReallyMatter00@!"
Auth.signIn({ username, password, validationData: {token}})

// Trigger PreAuth
const success = validateToken(event.request.validationData.token)

success && await cognito.adminSetUserPassword({
    Username: userName,
    Password: password,
    UserPoolId: userPoolId,
    Permanent: true,
  }).promise();
picks-1 commented 6 months ago

Any updates on this?

sandhyashris commented 1 month ago

Any updates on this?

sandhyashris commented 1 month ago

How about federated logins? How does Amplify work with pre-authentication triggers?

Auth.federatedSignIn({provider: {CognitoHostedUIIdentityProvider.Google})