amplify-js: ClientMetadata not passing to Cognito Trigger, with `Auth.signIn({... , validationData: {foo: 'bar'}})`

Describe the bug ClientMetadata not passing to Cognito Trigger, when using Amplify Custom Ui components and Auth.signIn() Auth.signIn({... , validationData: {foo: 'bar'}}), even with static preconfigured ClientMetadata, Auth.configure({ authenticationFlowType: 'CUSTOM_AUTH', clientMetadata: {foo: 'bar'} }) or Amplify.configure({ Auth: { authenticationFlowType: 'CUSTOM_AUTH', clientMetadata: {foo: 'bar'} } })

To Reproduce Steps to reproduce the behavior:

browser js code

Auth.configure({ clientMetadata: {foo: 'bar'}  })
await Auth.signIn({ username: 'someuser', validationData: {xyz: 'abc'} })

lambda trigger

exports.handler = (event, ctx, callback) => {
  console.log(event)
}

Expected behavior

Expect to see log of foo , xyz ClientMetadata in lambda cloudwatch logs , this are missing.

Additional context Amplify versions

  • “aws-amplify”: “^3.1.1”,
  • “aws-amplify-react”: “^4.2.2”,

You can turn on the debug mode to provide more info for us by setting window.LOG_LEVEL = ‘DEBUG’; in your app.

browser_debug_log

Screenshot_20200905_014143 (1)

cloudwatch log*

2020-09-04T22:19:05.727Z	****	INFO	{
  version: '1',
  region: 'us-west-2',
  userPoolId: '****',
  userName: '****',
  callerContext: {
    awsSdkVersion: 'aws-sdk-unknown-unknown',
    clientId: '****'
  },
  triggerSource: 'DefineAuthChallenge_Authentication',
  request: {
    userAttributes: {
      sub: '****',
      email_verified: 'true',
      'cognito:user_status': 'CONFIRMED',
      'cognito:email_alias': '****',
      given_name: '****',
      family_name: '****',
      email: '****'
    },
    session: [],
    userNotFound: false
  },
  response: { challengeName: null, issueTokens: null, failAuthentication: null }
}

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 21
  • Comments: 34 (5 by maintainers)

Most upvoted comments

@sammartinez, thanks for getting back to us on this issue. Do you have any idea when this may be resolved? We are in the progress of rewriting our authentication backend, but if this is resolved within a reasonable timeframe we will skip that rewrite.

The main thing for us is that the clientMetadata should be made available in all the available Cognito lambda triggers. It is also important that if the clientMetadata has been set during Auth.signIn in the js client, it should be set automatically if the Auth framework does an automatic refresh.

Amplify JS library has this problem solved. Latest version is sending ClientMetadata for token refresh.

We are expecting now from Cognito service to fix the problem on the server side.

Please +1 on one of this issues on AWS Forums to keep more feedback for this internally https://forums.aws.amazon.com/thread.jspa?messageID=946444&#946444 https://forums.aws.amazon.com/thread.jspa?messageID=910021&#910021

or feel free to create an issue on AWS Forums as well

Thanks for your feedback and patience on this issue. We have identified the issue and we are talking to the Amazon Cognito team internally about this. We will provide an update once we have one.

Any word from the Cognito team? It seems Amplify is now handling the clientMetadata correctly, however, Cognito only includes it sometimes.

The clientMetadata is included in the PreTokenGeneration Lambda event during login, but not on token refresh. This causes our app to break whenever the token refreshes because the token’s claims change as a result of the PreTokenGeneration Lambda running without any of the clientMetadata we sent.

Note: Amplify is including the clientMetadata in the token refresh request as expected, Cognito just does not include it in the event provided to the Lambda trigger.

I’ve gotten the validationData to show up in event.request, but only in the migrate user lambda trigger. I have not been successful in passing clientMetadata to any lambda trigger.

This is pretty limiting, and I need it for several use cases as we migrate our users into Cognito.

It’s not clear to me if this is entirely an Amplify bug or Cognito limitations, at this point. For example, careful reading of the Cognito docs, along with some googling, indicates that clientMetaData is never passed to the “Define Auth Challenge” trigger for some unstated reason. However, clientMetadata should be available in the “Create Auth Challenge” and “Verify Auth Challenge” triggers, but I’ve had no luck getting it to work with Amplify.

At this point, I’m considering abandoning Amplify and just using the aws-sdk. Not sure the benefits of Amplify outweigh the limitations.

Hi, any update on this?

clientMetadata is not received in lambda at all on second and subsequent call to Auth.signIn… this is really a deal breaker for us.

I’ve been trying work arounds like calling signOut first, etc but to no avail…

I have a working code snippet to pass data as parameter to the lambda function with Cognito Pre authentication trigger:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		metaDataKey: "metaDataValue"
	}
});

You can fetch the passed data value from “event.request.validationData.metaDataKey” on the Lambda side (Node.js).

An example for email validation to check if email unconfirmed or not: Client-side:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		email: email
	}
});

Lambda function (Node.js):

var AWS = require('aws-sdk');

exports.handler = async (event, context, callback) => {
    console.log("Received event");
    console.log(event);
    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
        apiVersion: "2016-04-18",
        region: event.region
    });
    let params = {
        UserPoolId: event.userPoolId,
        AttributesToGet: ["email"],
        Filter: "email='"+event.request.validationData.email+"'"
    };

    try {
        let response = await cognitoidentityserviceprovider.listUsers(params).promise();
        console.log(response);
        let users = response.Users;
        if (users.length > 0) {
            let user = users[0];
            if (user.UserStatus == "UNCONFIRMED") {
                console.log("user is unconfirmed");
                let customError = new Error("error_user_not_confirmed");
                callback(customError, event);
            }
        }
    }
    catch (e) {
        console.log(e);
    }
    callback(null, event);
};

@sammartinez The following is also supposed to work, but in this case does not pass the clientMetaData to Lambda function as validationData:

await Auth.signIn(username, password, {
	metaDataKey: "metaDataValue"
});

@sammartinez:

Please see my original SO question mentioned by @obonyojimmy: https://stackoverflow.com/questions/62548852/aws-cognito-and-amplify-clientmetadata-not-sent-when-session-is-refreshed

This is how it works in our current implementation: it is possible to set a clientMetadata object in the Auth.signIn request, like this:

Auth.signIn(auth.email, auth.password, {"metadataKey1": "metadataValue1"})
    .then(response => {
        // Sign in OK
    })
    .catch(error => {
        // Something went wrong
    });

In our manually configured lambda triggers, we can read this metadata from two trigger points: Pre authentication and Pre Token Generation. In the Pre authentication trigger, it can be read from request.validationData. In the Pre Token Generation trigger, it can be read from request.clientMetadata.

Problem 1: we see that during the Auth.signIn process, the Amplify library automatically does a session refresh where the clientMetadata object is not included. I have not found a way how to make sure it gets set.

Problem 2: when we do a manual session refresh, by calling Auth.currentAuthenticatedUser() and then doing a user.refreshSession call, it is possible to send a clientMetadata object but it is not possible to read it in the Pre Token Generation trigger.

What we expect: if it is possible to set a clientMetadata object during Auth.signIn, the same clientMetadata object should be sent with the automatic session refresh and it should also be possible to send the clientMetadata when manually refreshing the user session.

I have a working code snippet to pass data as parameter to the lambda function with Cognito Pre authentication trigger:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		metaDataKey: "metaDataValue"
	}
});

You can fetch the passed data value from “event.request.validationData.metaDataKey” on the Lambda side (Node.js).

An example for email validation to check if email unconfirmed or not: Client-side:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		email: email
	}
});

Lambda function (Node.js):

var AWS = require('aws-sdk');

exports.handler = async (event, context, callback) => {
    console.log("Received event");
    console.log(event);
    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
        apiVersion: "2016-04-18",
        region: event.region
    });
    let params = {
        UserPoolId: event.userPoolId,
        AttributesToGet: ["email"],
        Filter: "email='"+event.request.validationData.email+"'"
    };

    try {
        let response = await cognitoidentityserviceprovider.listUsers(params).promise();
        console.log(response);
        let users = response.Users;
        if (users.length > 0) {
            let user = users[0];
            if (user.UserStatus == "UNCONFIRMED") {
                console.log("user is unconfirmed");
                let customError = new Error("error_user_not_confirmed");
                callback(customError, event);
            }
        }
    }
    catch (e) {
        console.log(e);
    }
    callback(null, event);
};

@sammartinez The following is also supposed to work, but in this case does not pass the clientMetaData to Lambda function as validationData:

await Auth.signIn(username, password, {
	metaDataKey: "metaDataValue"
});

It works for me

I have a working code snippet to pass data as parameter to the lambda function with Cognito Pre authentication trigger:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		metaDataKey: "metaDataValue"
	}
});

You can fetch the passed data value from “event.request.validationData.metaDataKey” on the Lambda side (Node.js).

An example for email validation to check if email unconfirmed or not: Client-side:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		email: email
	}
});

Lambda function (Node.js):

var AWS = require('aws-sdk');

exports.handler = async (event, context, callback) => {
    console.log("Received event");
    console.log(event);
    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
        apiVersion: "2016-04-18",
        region: event.region
    });
    let params = {
        UserPoolId: event.userPoolId,
        AttributesToGet: ["email"],
        Filter: "email='"+event.request.validationData.email+"'"
    };

    try {
        let response = await cognitoidentityserviceprovider.listUsers(params).promise();
        console.log(response);
        let users = response.Users;
        if (users.length > 0) {
            let user = users[0];
            if (user.UserStatus == "UNCONFIRMED") {
                console.log("user is unconfirmed");
                let customError = new Error("error_user_not_confirmed");
                callback(customError, event);
            }
        }
    }
    catch (e) {
        console.log(e);
    }
    callback(null, event);
};

@sammartinez The following is also supposed to work, but in this case does not pass the clientMetaData to Lambda function as validationData:

await Auth.signIn(username, password, {
	metaDataKey: "metaDataValue"
});

It works for me, thank you save my life, my co-worker would kick me if I couldn’t make it work!

I have a working code snippet to pass data as parameter to the lambda function with Cognito Pre authentication trigger:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		metaDataKey: "metaDataValue"
	}
});

You can fetch the passed data value from “event.request.validationData.metaDataKey” on the Lambda side (Node.js).

An example for email validation to check if email unconfirmed or not: Client-side:

await Auth.signIn({
	username: username,
	password: password,
	validationData: {
		email: email
	}
});

Lambda function (Node.js):

var AWS = require('aws-sdk');

exports.handler = async (event, context, callback) => {
    console.log("Received event");
    console.log(event);
    var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
        apiVersion: "2016-04-18",
        region: event.region
    });
    let params = {
        UserPoolId: event.userPoolId,
        AttributesToGet: ["email"],
        Filter: "email='"+event.request.validationData.email+"'"
    };

    try {
        let response = await cognitoidentityserviceprovider.listUsers(params).promise();
        console.log(response);
        let users = response.Users;
        if (users.length > 0) {
            let user = users[0];
            if (user.UserStatus == "UNCONFIRMED") {
                console.log("user is unconfirmed");
                let customError = new Error("error_user_not_confirmed");
                callback(customError, event);
            }
        }
    }
    catch (e) {
        console.log(e);
    }
    callback(null, event);
};

@sammartinez The following is also supposed to work, but in this case does not pass the clientMetaData to Lambda function as validationData:

await Auth.signIn(username, password, {
	metaDataKey: "metaDataValue"
});

It works for me!

Solved this by implementing a two-step custom challenge flow as described here:

https://github.com/aws-samples/amazon-cognito-passwordless-email-auth/issues/9

@chrisbonifacio there is any place where I can create an issue for cognito to analyze? I am having the same problem. I am implementing passwordless authentication on my application, but i need to distinguish between a sign-in made with the email address and a sign-in made with a phone number. The client metadata woud come in handy as i can mark the type of authentication made.

@ricardoribas I’m trying to achieve a very similar flow, did you find any workaround? Thanks!

This really needs to be prioritized. This nearly caused our production users to be able to see each others data!!!