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.44k stars 2.13k forks source link

Incorrect Username and password when with aws synthetics canary #9614

Closed abdulrehman135 closed 1 year ago

abdulrehman135 commented 2 years ago

Before opening, please confirm:

JavaScript Framework

Not applicable

Amplify APIs

Authentication

Amplify Categories

auth

Environment information

``` System: OS: Linux 5.8 Ubuntu 20.04.3 LTS (Focal Fossa) CPU: (32) arm64 unknown Memory: 77.88 GB / 248.74 GB Container: Yes Shell: 5.0.17 - /bin/bash Binaries: Node: 14.17.0 - ~/.nvm/versions/node/v14.17.0/bin/node Yarn: 1.22.17 - ~/.nvm/versions/node/v14.17.0/bin/yarn npm: 6.14.13 - ~/.nvm/versions/node/v14.17.0/bin/npm npmPackages: @aws-cdk/assert: 1.130.0 => 1.130.0 @aws-cdk/aws-appsync: 1.130.0 => 1.130.0 @aws-cdk/aws-cloudfront: 1.130.0 => 1.130.0 @aws-cdk/aws-cloudwatch: 1.130.0 => 1.130.0 @aws-cdk/aws-cloudwatch-actions: 1.130.0 => 1.130.0 @aws-cdk/aws-cognito: 1.130.0 => 1.130.0 @aws-cdk/aws-dynamodb: 1.130.0 => 1.130.0 @aws-cdk/aws-events: 1.130.0 => 1.130.0 @aws-cdk/aws-events-targets: 1.130.0 => 1.130.0 @aws-cdk/aws-glue: 1.130.0 => 1.130.0 @aws-cdk/aws-kinesisfirehose: 1.130.0 => 1.130.0 @aws-cdk/aws-lambda: 1.130.0 => 1.130.0 @aws-cdk/aws-lambda-event-sources: 1.130.0 => 1.130.0 @aws-cdk/aws-lambda-nodejs: 1.130.0 => 1.130.0 @aws-cdk/aws-route53: 1.130.0 => 1.130.0 @aws-cdk/aws-s3-deployment: 1.130.0 => 1.130.0 @aws-cdk/aws-sns: 1.130.0 => 1.130.0 @aws-cdk/aws-synthetics: 1.130.0 => 1.130.0 @aws-cdk/aws-wafv2: 1.130.0 => 1.130.0 @aws-cdk/core: 1.130.0 => 1.130.0 @aws-cdk/custom-resources: 1.130.0 => 1.130.0 @azure/identity: 2.0.1 => 2.0.1 @graphql-codegen/cli: 1.20.1 => 1.20.1 @graphql-codegen/introspection: 1.18.1 => 1.18.1 @graphql-codegen/typescript: 1.20.1 => 1.20.1 @graphql-codegen/typescript-apollo-angular: 2.3.0 => 2.3.0 @graphql-codegen/typescript-operations: 1.17.14 => 1.17.14 @lifeomic/attempt: ^3.0.0 => 3.0.0 @microsoft/microsoft-graph-client: 3.0.1 => 3.0.1 @microsoft/microsoft-graph-types: 2.11.0 => 2.11.0 @stryker-mutator/core: ^4.3.1 => 4.6.0 @stryker-mutator/jest-runner: ^4.3.1 => 4.6.0 @stryker-mutator/typescript-checker: ^4.3.1 => 4.6.0 @tsconfig/node14: ^1.0.0 => 1.0.1 @types/aws-lambda: ^8.10.70 => 8.10.79 @types/fs-extra: ^9.0.6 => 9.0.12 @types/jest: ^26.0.20 => 26.0.24 @types/jest-when: ^2.7.3 => 2.7.3 @types/lodash.uniqby: ^4.7.6 => 4.7.6 @types/mock-fs: ^4.13.1 => 4.13.1 @types/node: ^14.14.20 => 14.17.5 (10.17.60, 16.4.0) @types/shelljs: ^0.8.8 => 0.8.9 @typescript-eslint/eslint-plugin: ^4.14.0 => 4.28.4 @typescript-eslint/parser: ^4.13.0 => 4.28.4 amazon-cognito-identity-js: ^5.2.6 => 5.2.6 aws-cdk: 1.130.0 => 1.130.0 aws-sdk: 2.1055.0 => 2.952.0 (2.1006.0) aws-xray-sdk: ^3.2.0 => 3.3.3 axios: 0.24.0 => 0.24.0 (0.21.4) cloc: ^2.7.0 => 2.8.0 copyfiles: ^2.4.1 => 2.4.1 ejs-cli: ^2.2.1 => 2.2.1 esbuild: ^0.12.1 => 0.12.15 eslint: ^7.18.0 => 7.31.0 eslint-config-standard: ^16.0.2 => 16.0.3 (14.1.1) eslint-config-standard-with-typescript: ^19.0.1 => 19.0.1 eslint-plugin-import: ^2.22.1 => 2.23.4 eslint-plugin-jsdoc: ^32.0.1 => 32.3.4 eslint-plugin-node: ^11.1.0 => 11.1.0 eslint-plugin-prefer-arrow: ^1.2.3 => 1.2.3 eslint-plugin-promise: ^4.2.1 => 4.3.1 eslint-plugin-standard: ^4.1.0 => 4.1.0 googleapis: 92.0.0 => 92.0.0 graphql: ^15.4.0 => 15.5.1 isomorphic-fetch: 3.0.0 => 3.0.0 jest: ^26.6.3 => 26.6.3 jest-when: ^3.3.1 => 3.3.1 mock-fs: ^5.1.2 => 5.1.2 npm-run-all: ^4.1.5 => 4.1.5 reflect-metadata: ^0.1.13 => 0.1.13 shelljs: ^0.8.4 => 0.8.4 ts-jest: ^26.4.4 => 26.5.6 ts-node: ^9.1.1 => 9.1.1 tsconfig-paths: ^3.9.0 => 3.10.1 typescript: ^4.2.4 => 4.3.5 ulid: ^2.3.0 => 2.3.0 npmGlobalPackages: aws-cdk: 2.12.0 node-gyp: 8.4.1 npm: 6.14.13 typescript: 4.5.5 yarn: 1.22.17 ```

Describe the bug

Cognito Login works fine in the local environment but fails with in canary.

Expected behavior

Should work fine on Canary too

Reproduction steps

Call code on Canary

Code Snippet

const loginTest = async function (user: string, password: string): Promise<void> {
  const cognito = CognitoService.instance(
    process.env.CLIENT_ID ?? '',
    process.env.USER_POOL_ID ?? ''
  )
  log.error('loginTest')
  await synthetics.executeStep('Check login', async () => await cognito.login(user, password))
}

Cognito Service

import 'cross-fetch/polyfill';
import {
  AuthenticationDetails,
  ClientMetadata,
  CognitoUser,
  CognitoUserPool,
  CognitoUserSession
} from 'amazon-cognito-identity-js'
// // @ts-expect-error
// import log from 'SyntheticsLogger'
const log = console

interface CognitoToken {
  accessToken: string
  refreshToken: string
  groups: string[]
}

const wait = async (interval: number): Promise<void> => await new Promise(resolve => setTimeout(resolve, interval))

export async function retry<T> (fn: () => Promise<T>, retriesLeft = 3, retryDelay = 200): Promise<T> {
  try {
    return await fn()
  } catch (error) {
    await wait(retryDelay)
    if (retriesLeft === 0) {
      throw error
    }
    return await retry(fn, --retriesLeft, retryDelay)
  }
}

export class CognitoService {
  private readonly cognitoUserPool: CognitoUserPool

  private readonly authRetryCount = 0
  private readonly authRetryDelay = 200

  private static service: CognitoService

  static instance (ClientId: string, UserPoolId: string): CognitoService {
    this.service = this.service !== undefined
      ? this.service
      : new CognitoService({
        ClientId,
        UserPoolId
      })
    return this.service
  }

  private constructor (params: { UserPoolId: string, ClientId: string }) {
    log.error(JSON.stringify(params))
    this.cognitoUserPool = new CognitoUserPool(params)
  }

  async authenticate (userId: string, password: string): Promise<CognitoToken> {
    log.error(JSON.stringify({ userId, password }))
    return await retry(async () => await this._authenticate(userId, password), this.authRetryCount, this.authRetryDelay)
  }

  private async _authenticate (userId: string, password: string): Promise<CognitoToken> {
    const authenticationUser = new CognitoUser({ Username: userId, Pool: this.cognitoUserPool })
    return await new Promise((resolve, reject) => {
      log.error(JSON.stringify({ password }))
      authenticationUser.authenticateUser(new AuthenticationDetails({
        Username: userId,
        Password: password,
        ClientMetadata: {
          Username: userId
        } as ClientMetadata
      }), {
        onSuccess: (session) => {
          log.error(JSON.stringify(session))
          resolve(this.extractTokenInformation(session))
        },
        onFailure: (error) => {
          log.error(JSON.stringify(error))
          reject(error)
        },
        newPasswordRequired: (_userAttributes, requiredAttributes) => {
          log.error(JSON.stringify({ _userAttributes, requiredAttributes }))
          authenticationUser.completeNewPasswordChallenge(password, requiredAttributes, {
            onSuccess: (session) => {
              log.error(JSON.stringify(session))
              resolve(this.extractTokenInformation(session))
            },
            onFailure: (error) => {
              log.error(JSON.stringify(error))
              reject(error)
            }
          })
        }
      })
    })
  }

  private extractTokenInformation (session: CognitoUserSession): CognitoToken {
    const accessToken = session.getAccessToken()
    const refreshToken = session.getRefreshToken().getToken()
    const payload = accessToken.decodePayload()
    const groups = payload['cognito:groups']

    return {
      accessToken: accessToken.getJwtToken(),
      refreshToken,
      groups: groups
    }
  }

  async login (userId: string, password: string): Promise<CognitoToken> {
    return await this.authenticate(userId, password)
  }

  async logout (userId: string): Promise<void> {
    const authenticationUser = new CognitoUser({ Username: userId, Pool: this.cognitoUserPool })
    return await new Promise((resolve, reject) => {
      authenticationUser.signOut(() => {
        resolve()
      })
    })
  }
}

Log output

``` // Put your logs below this line Start Canary INFO: Event: {"canaryName":"abdul-3-basic","s3BaseFilePath":"abdul-3-sococo-analytics-storage/canary/us-east-1/abdul-3-sococo-execution-stack/basic-functionality","customerCanaryHandlerName":"index.handler","customerCanaryCodeLocation":"arn:aws:lambda:us-east-1:880344435608:layer:cwsyn-abdul-3-basic-ba2c4207-d295-44c3-9a08-12f941e145ce:16","invocationTime":1645194971439,"runtimeVersion":"syn-nodejs-puppeteer-3.1","activeTracing":false,"canaryRunId":"3f6dcd3c-108a-4991-9b54-6b3606a78c89"} INFO: Context: {"callbackWaitsForEmptyEventLoop":true,"functionVersion":"17","functionName":"cwsyn-abdul-3-basic-ba2c4207-d295-44c3-9a08-12f941e145ce","memoryLimitInMB":"960","logGroupName":"/aws/lambda/cwsyn-abdul-3-basic-ba2c4207-d295-44c3-9a08-12f941e145ce","logStreamName":"2022/02/18/[17]4d2dd26a76484db69f17d60481a632ea","invokedFunctionArn":"arn:aws:lambda:us-east-1:880344435608:function:cwsyn-abdul-3-basic-ba2c4207-d295-44c3-9a08-12f941e145ce:17","awsRequestId":"545f6c6a-0988-474a-a70f-665569d837b5"} INFO: Configuring tracing: canaryName: abdul-3-basic canaryArn: arn:aws:synthetics:us-east-1:880344435608:canary:abdul-3-basic canaryRunId: 3f6dcd3c-108a-4991-9b54-6b3606a78c89 INFO: Setting ActiveTracing to: false INFO: Recording configuration: INFO: Canary execution start time: Fri Feb 18 2022 14:36:11 GMT+0000 (Coordinated Universal Time) INFO: canaryName: abdul-3-basic INFO: s3BaseFilePath: abdul-3-sococo-analytics-storage/canary/us-east-1/abdul-3-sococo-execution-stack/basic-functionality INFO: awsAccountId: 880344435608 INFO: region: us-east-1 INFO: canaryArn: arn:aws:synthetics:us-east-1:880344435608:canary:abdul-3-basic INFO: memoryLimitInMB: 960 INFO: awsRequestId: 545f6c6a-0988-474a-a70f-665569d837b5 INFO: timeRemainingInMillis: 119998 INFO: Launching Puppeteer browser with options: {"args":["--autoplay-policy=user-gesture-required","--disable-background-networking","--disable-background-timer-throttling","--disable-backgrounding-occluded-windows","--disable-breakpad","--disable-client-side-phishing-detection","--disable-component-update","--disable-default-apps","--disable-dev-shm-usage","--disable-domain-reliability","--disable-extensions","--disable-features=AudioServiceOutOfProcess","--disable-hang-monitor","--disable-ipc-flooding-protection","--disable-notifications","--disable-offer-store-unmasked-wallet-cards","--disable-popup-blocking","--disable-print-preview","--disable-prompt-on-repost","--disable-renderer-backgrounding","--disable-setuid-sandbox","--disable-speech-api","--disable-sync","--disk-cache-size=33554432","--hide-scrollbars","--ignore-gpu-blacklist","--metrics-recording-only","--mute-audio","--no-default-browser-check","--no-first-run","--no-pings","--no-sandbox","--no-zygote","--password-store=basic","--use-gl=swiftshader","--use-mock-keychain","--single-process"],"defaultViewport":{"deviceScaleFactor":1,"hasTouch":false,"height":1080,"isLandscape":true,"isMobile":false,"width":1920},"headless":true,"executablePath":"/tmp/chromium"} INFO: Creating a new page. INFO: Setting up page events. INFO: Adding CloudWatchSynthetics/arn:aws:synthetics:us-east-1:880344435608:canary:abdul-3-basic to user agent header sent with each outbound request. INFO: Creating Puppeteer HAR object. INFO: Starting HAR file logging. INFO: Start executing customer steps INFO: Customer canary entry file name: "index" INFO: Customer canary entry function name: "handler" INFO: Calling customer canary: /opt/nodejs/node_modules/index.handler() ERROR: loginTest INFO: Step starting: Check login URL: about:blank INFO: Request: https://cognito-idp.us-east-1.amazonaws.com/ INFO: Response: 400 Request: https://cognito-idp.us-east-1.amazonaws.com/ ERROR: Step failed: Check login URL: about:blank Error: NotAuthorizedException: Password attempts exceeded INFO: Publishing result and duration CloudWatch metrics with timestamp: Fri Feb 18 2022 14:36:11 GMT+0000 (Coordinated Universal Time) for canaryName: abdul-3-basic stepName: Check login result: FAILED startDateTimeInUTC: Fri Feb 18 2022 14:36:11 GMT+0000 (Coordinated Universal Time) endDateTimeInUTC: Fri Feb 18 2022 14:36:12 GMT+0000 (Coordinated Universal Time) ERROR: Canary error: NotAuthorizedException: Password attempts exceeded Stack: NotAuthorizedException: Password attempts exceeded at /opt/nodejs/node_modules/index.js:1:79334 at runMicrotasks () at processTicksAndRejections (internal/process/task_queues.js:97:5) for step: Check login ERROR: 1 step failed while executing the canary INFO: Har generation stopped INFO: Browser closed INFO: Publishing result and duration CloudWatch metrics with timestamp: Fri Feb 18 2022 14:36:11 GMT+0000 (Coordinated Universal Time) for canaryName: abdul-3-basic stepName: null result: FAILED startDateTimeInUTC: Fri Feb 18 2022 14:36:11 GMT+0000 (Coordinated Universal Time) endDateTimeInUTC: Fri Feb 18 2022 14:36:12 GMT+0000 (Coordinated Universal Time) INFO: Getting list of files under /tmp to upload to S3. INFO: Checking list of files under /tmp to see if they are actual files. INFO: List of actual files under /tmp ["/tmp/2022-02-18T14-36-11-471Z-log.txt","/tmp/HttpRequestsReport.json","/tmp/SyntheticsReport-FAILED.json"] INFO: Uploading files to S3 ["/tmp/2022-02-18T14-36-11-471Z-log.txt","/tmp/HttpRequestsReport.json","/tmp/SyntheticsReport-FAILED.json"] INFO: Getting S3 location for uploading files INFO: S3 destination for uploading artifacts determined: {"s3Bucket":"abdul-3-sococo-analytics-storage","s3Key":"canary/us-east-1/abdul-3-sococo-execution-stack/basic-functionality/abdul-3-basic/2022/02/18/14/36-11-439"} INFO: S3 Bucket location determined: us-east-1 ```

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

cwomack commented 1 year ago

@abdulrehman135, it looks like there's a NotAuthorizedException: Password attempts exceeded error in your log output. Do you know how many attempts to login your canary is trying in this scenario? After 5 failed sign in attempts, Cognito will begin a lockout duration (which increases after each subsequent failure after the initial 5).

Can you provide any more context on what your canary test is doing?

cwomack commented 1 year ago

Closing this issue as we have not heard back from you. If you are still experiencing this or have more questions, please feel free to reply back and provide any information previously requested and we'd be happy to re-open the issue.

Thank you!