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.36k stars 2.1k forks source link

InvalidParameterException: Invalid SourceUser: Cognito users with a username/password may not be passed in as a SourceUser, only as a DestinationUser #13334

Open natuan62 opened 2 weeks ago

natuan62 commented 2 weeks ago

Before opening, please confirm:

JavaScript Framework

Vue

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

Amplify CLI

Environment information

``` # Put output below this line System: OS: macOS 14.4.1 CPU: (8) arm64 Apple M1 Memory: 143.50 MB / 16.00 GB Shell: 5.9 - /bin/zsh Binaries: Node: 20.11.1 - ~/.nvm/versions/node/v20.11.1/bin/node Yarn: 1.22.19 - ~/.yarn/bin/yarn npm: 7.20.0 - ~/.config/yarn/global/node_modules/.bin/npm pnpm: 8.15.7 - ~/Library/pnpm/pnpm Browsers: Chrome: 124.0.6367.119 Safari: 17.4.1 npmPackages: fast-xml-parser: ^4.3.2 => 4.3.2 mime-types: ^2.1.35 => 2.1.35 sharp: ^0.32.6 => 0.32.6 svg-parser: ^2.0.4 => 2.0.4 typescript: ^5.3.2 => 5.3.2 unzipper: ^0.10.14 => 0.10.14 npmGlobalPackages: @angular/cli: 10.1.2 @aws-amplify/cli: 12.10.1 corepack: 0.23.0 graphql-schema-utilities: 1.1.8 npm: 10.2.4 ```

Describe the bug

  1. Create an cognito account by amplify-js and not confirm this account
  2. Login with social (google) with amplify-js
  3. With preSignUp lambda, confirm user and set email_verified = true
  4. adminLinkProviderForUser return this error message (if user confirmed, adminLinkProviderForUser works fine)
InvalidParameterException: Invalid SourceUser: Cognito users with a username/password may not be passed in as a SourceUser, only as a DestinationUser
    at Request.extractError (/var/task/cognito-pre-signup.js:82837:27)
    at Request.callListeners (/var/task/cognito-pre-signup.js:86191:20)
    at Request.emit (/var/task/cognito-pre-signup.js:86163:10)
    at Request.emit (/var/task/cognito-pre-signup.js:84785:14)
    at Request.transition (/var/task/cognito-pre-signup.js:84121:10)
    at AcceptorStateMachine.runTo (/var/task/cognito-pre-signup.js:91267:12)
    at /var/task/cognito-pre-signup.js:91279:10
    at Request.<anonymous> (/var/task/cognito-pre-signup.js:84137:9)
    at Request.<anonymous> (/var/task/cognito-pre-signup.js:84787:12)
    at Request.callListeners (/var/task/cognito-pre-signup.js:86201:18) {
  code: 'InvalidParameterException',
  '[__type]': 'See error.__type for details.',
  time: 2024-05-03T04:17:02.634Z,
  requestId: '5451e01d-2b33-4742-99e1-22148499fe4a',
  statusCode: 400,
  retryable: false,
  retryDelay: 65.69485045768356
}

Expected behavior

adminLinkProviderForUser works fine and not return error

Reproduction steps

1/ Create account with amplif-js and not confirm code

image

2/ with preSignUp lamda, confirm user and set email_verified = true before adminLinkProviderForUser

3/ Although adminLinkProviderForUser return error but this account seem link success image

Code Snippet

// google social login
import { signInWithRedirect, signOut } from 'aws-amplify/auth';

await signInWithRedirect({
          provider: 'Google',
        });

// preSignUp lambda trigger

import AWS from 'aws-sdk';

export const handler = async (
  event: PreSignUpTriggerEvent,
  context: Context,
  callback: Callback
) => {
  const CognitoService = new AWS.CognitoIdentityServiceProvider();

  const getUser = async (userPoolId, email) => {
    // get user from cognito
    const users = await CognitoService.getListUsers(userPoolId, email);
    const userNotExternalProvider = users.filter((u) => u.UserStatus !== 'EXTERNAL_PROVIDER');
    return userNotExternalProvider[0];
  };

  const confirmUser = async (userPoolId, username) => {
    const params = {
      UserPoolId: userPoolId,
      Username: username,
    };
    await CognitoService.adminConfirmSignUp(params).promise();
    await CognitoService.adminUpdateUserAttributes({
      UserAttributes: [
        {
          Name: 'email_verified',
          Value: 'true',
        },
      ],
      UserPoolId: userPoolId,
      Username: username,
    }).promise();
  };

  const linkProviderUser = async (Username, event) => {
    let destinationProvider = 'Cognito';
    let destinationSub = Username;
    if (Username.includes('_')) {
      const indexOf = Username.indexOf('_');
      destinationProvider = Username.slice(0, indexOf);
      destinationSub = Username.slice(indexOf + 1);
    }

    const params = {
      DestinationUser: {
        ProviderName: destinationProvider,
        ProviderAttributeValue: destinationSub,
      },
      SourceUser: {
        ProviderName: 'Google',
        ProviderAttributeValue: event.userName.slice(event.userName.indexOf('_') + 1),
        ProviderAttributeName: 'Cognito_Subject',
      },
      UserPoolId: event.userPoolId,
    };

    const result = await CognitoService.adminLinkProviderForUser(params).promise();
  };

  if (event.triggerSource === 'PreSignUp_ExternalProvider') {
    const userExist = await getUser(event.userPoolId, event.request.userAttributes.email); // get user created before (this user not confirm)

    if (userExist?.UserStatus === 'UNCONFIRMED') {
      await confirmUser(event.userPoolId, userExist.Username);
    }

    await linkProviderUser(userExist.Username, event);
  }
};

Log output

``` // Put your logs below this line ```

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

natuan62 commented 2 weeks ago

Seem this error occur because my lambda run time > 5 seconds. And it trigger twice image

And in second trigger, adminLinkProviderForUser() cause error because in first time, it linked success

cwomack commented 1 week ago

Hey, @natuan62 👋. While there are some lambdas that are not supported by the Social Sign In (noted here in docs), it looks like your sign-in is working and lambda is being triggered. I'm wondering if the issue is on the CDK API side for Cognito and will try to reproduce this/dig further. Just to make sure we can reproduce this properly and understand the use case, are you just trying to link your users together that have used multiple social providers?

natuan62 commented 1 week ago

@cwomack Yes, I'm trying link current account with multi social (google, facebook...)