amplify-js icon indicating copy to clipboard operation
amplify-js copied to clipboard

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

Open natuan62 opened this issue 1 year ago • 4 comments

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

  • if user confirmed, adminLinkProviderForUser will not return error

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 avatar May 03 '24 04:05 natuan62

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

natuan62 avatar May 03 '24 08:05 natuan62

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?

cwomack avatar May 06 '24 15:05 cwomack

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

natuan62 avatar May 06 '24 15:05 natuan62

@natuan62 Could you please provide the params you're seeing getting passed to await CognitoService.adminLinkProviderForUser(params).promise()? Feel free to redact sensitive values if needed.

haverchuck avatar Jun 25 '24 15:06 haverchuck