Skip to content

Token refresh uses stale userPoolClientId after Amplify.configure() called multiple times #14620

@eabbott2b

Description

@eabbott2b

Before opening, please confirm:

JavaScript Framework

Vue

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

CDK

Environment information

  System:
    OS: macOS 15.7.1
    CPU: (12) arm64 Apple M2 Pro
    Memory: 60.03 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.18.0 - /Users/ericaabbott/.nvm/versions/node/v22.18.0/bin/node
    Yarn: 3.5.0 - /Users/ericaabbott/.nvm/versions/node/v22.18.0/bin/yarn
    npm: 10.9.3 - /Users/ericaabbott/.nvm/versions/node/v22.18.0/bin/npm
    pnpm: 10.20.0 - /Users/ericaabbott/.nvm/versions/node/v22.18.0/bin/pnpm
    Watchman: 2025.02.10.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome: 142.0.7444.60
    Firefox: 132.0.2
    Safari: 26.0.1
  npmPackages:
    @tsconfig/node22: ^22.0.2 => 22.0.2 
    @types/node: ^22.18.11 => 22.19.0 
    @vitejs/plugin-vue: ^6.0.1 => 6.0.1 
    @vue/tsconfig: ^0.8.1 => 0.8.1 
    aws-amplify: ^6.15.8 => 6.15.8 
    aws-amplify/adapter-core:  undefined ()
    aws-amplify/adapter-core/internals:  undefined ()
    aws-amplify/analytics:  undefined ()
    aws-amplify/analytics/kinesis:  undefined ()
    aws-amplify/analytics/kinesis-firehose:  undefined ()
    aws-amplify/analytics/personalize:  undefined ()
    aws-amplify/analytics/pinpoint:  undefined ()
    aws-amplify/api:  undefined ()
    aws-amplify/api/internals:  undefined ()
    aws-amplify/api/server:  undefined ()
    aws-amplify/auth:  undefined ()
    aws-amplify/auth/cognito:  undefined ()
    aws-amplify/auth/cognito/server:  undefined ()
    aws-amplify/auth/enable-oauth-listener:  undefined ()
    aws-amplify/auth/server:  undefined ()
    aws-amplify/data:  undefined ()
    aws-amplify/data/server:  undefined ()
    aws-amplify/datastore:  undefined ()
    aws-amplify/in-app-messaging:  undefined ()
    aws-amplify/in-app-messaging/pinpoint:  undefined ()
    aws-amplify/push-notifications:  undefined ()
    aws-amplify/push-notifications/pinpoint:  undefined ()
    aws-amplify/storage:  undefined ()
    aws-amplify/storage/s3:  undefined ()
    aws-amplify/storage/s3/server:  undefined ()
    aws-amplify/storage/server:  undefined ()
    aws-amplify/utils:  undefined ()
    npm-run-all-test:  0.0.0 
    npm-run-all2: ^8.0.4 => 8.0.4 
    typescript: ~5.9.0 => 5.9.3 
    vite: ^7.1.11 => 7.2.2 
    vite-plugin-vue-devtools: ^8.0.3 => 8.0.3 
    vue: ^3.5.22 => 3.5.24 
    vue-tsc: ^3.1.1 => 3.1.3 
  npmGlobalPackages:
    corepack: 0.33.0
    npm: 10.9.3


Describe the bug

When Amplify.configure() is called multiple times to switch the Auth.Cognito.userPoolClientId, the Auth.signIn() method correctly uses the new clientId.

However, the internal TokenProvider's TokenOrchestrator is not updated. It retains the stale, original clientId. This causes any subsequent call to refresh the token, either by fetchAuthSession({ forceRefresh: true }) or by the internal Amplify token refresh functionality to fail, as it sends the stale clientId to Cognito, which doesn't match the user's session.

To reproduce this, you need:

A single Cognito User Pool.

Two App Clients in that User Pool (e.g., CLIENT_ID_A and CLIENT_ID_B).

A test user that can log in to both.

Expected behavior

When the userPoolClientId is changed by calling Amplify.configure() multiple times, this userPoolClientId should be used when attempting to refresh the token

Reproduction steps

https://github.com/eabbott2b/amplify-configure-bug

Code Snippet

import { ref } from 'vue';
import { Amplify } from 'aws-amplify';
import { signIn, fetchAuthSession, confirmSignIn, type ConfirmSignInInput, type SignInOutput, type ConfirmSignInOutput } from 'aws-amplify/auth';

// --- 1. SET YOUR PREREQUISITES HERE ---
const REGION = 'your-region'; // e.g., 'us-east-1'
const USER_POOL_ID = 'your-user-pool-id';
const CLIENT_ID_A = 'your-first-app-client-id';  // The "stale" client
const CLIENT_ID_B = 'your-second-app-client-id'; // The "active" client
// -------------------------------------

const username = ref('test-user');
const verificationCode = ref('TestPassword123!');
const log = ref('');

const logMsg = (msg: string) => {
  log.value += `[${new Date().toLocaleTimeString()}] ${msg}\n`;
};

/**
 * STEP 1: Configure Amplify with Client A.
 * This sets the internal TokenOrchestrator's config.
 */
function configureClientA() {
  const configA = {
    Auth: {
      Cognito: {
        userPoolId: USER_POOL_ID,
        userPoolClientId: CLIENT_ID_A,
        region: REGION,
      },
    },
  };
  Amplify.configure(configA);
  logMsg(`Configured with CLIENT_ID_A: ${CLIENT_ID_A}`);
}

/**
 * STEP 2: Re-configure with Client B and sign in.
 * This proves that signIn() correctly uses the new config.
 */
async function configureClientB_and_SignIn() {
  const configB = {
    Auth: {
      Cognito: {
        userPoolId: USER_POOL_ID,
        userPoolClientId: CLIENT_ID_B,
        region: REGION,
      },
    },
  };
  Amplify.configure(configB);
  logMsg(`Configured with CLIENT_ID_B: ${CLIENT_ID_B}`);


    let signInOutput: SignInOutput | undefined
    let confirmSignInOutput: ConfirmSignInOutput | undefined

    logMsg('Attempting signIn with CLIENT_ID_B...');
    try {
      signInOutput = await signIn({
        username: username.value,
        options: {
          authFlowType: 'CUSTOM_WITHOUT_SRP'
        }
      })
    } catch (err) {
      console.info('Sign in error:', err)
      return
    }

    if (signInOutput?.nextStep?.signInStep === 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE') {
      let confirmSignInInput: ConfirmSignInInput = { challengeResponse: verificationCode.value }
      try {
        confirmSignInOutput = await confirmSignIn(confirmSignInInput)
      } catch (err) {
        console.info('Confirm sign in error:', err)
      }
    }

    if (signInOutput?.isSignedIn || confirmSignInOutput?.isSignedIn) {
      logMsg('✅ signIn with CLIENT_ID_B SUCCEEDED.')
    } else {
      logMsg(`❌ signIn with CLIENT_ID_B FAILED.`);
    }

}

/**
 * STEP 3: Force a token refresh.
 * This will fail because the internal TokenOrchestrator
 * will use CLIENT_ID_A from Step 1.
 */
async function forceRefresh() {
  logMsg('Attempting forceRefresh...');
  logMsg('Open your Network tab to see the "GetTokensFromRefreshToken" request.');
  try {
    const session = await fetchAuthSession({ forceRefresh: true });
    if (session.tokens?.accessToken) {
      logMsg(`Access Token after refresh: ${session.tokens.accessToken}`);
    } else {
      logMsg('❌ No Access Token found after refresh.');
      logMsg('\n--- FAILURE ANALYSIS ---');
      logMsg(`The API call failed because the default TokenProvider ` +
           `sent CLIENT_ID_A ("${CLIENT_ID_A}") in its refresh request, ` +
           `but the refresh token was issued to CLIENT_ID_B ("${CLIENT_ID_B}").`);
    }
  } catch (e) {
    logMsg(`❌ fetchAuthSession FAILED: ${e}`);

  }
}

Log output

[15:41:06] Configured with CLIENT_ID_A: [redacted]
[15:41:10] Configured with CLIENT_ID_B: [redacted]
[15:41:10] Attempting signIn with CLIENT_ID_B...
[15:41:11] ✅ signIn with CLIENT_ID_B SUCCEEDED.
[15:41:18] Attempting forceRefresh...
[15:41:18] Open your Network tab to see the "GetTokensFromRefreshToken" request.
[15:41:18] ❌ No Access Token found after refresh.
[15:41:18] 
--- FAILURE ANALYSIS ---
[15:41:18] The API call failed because the default TokenProvider sent CLIENT_ID_A ("[redacted]") in its refresh request, but the refresh token was issued to CLIENT_ID_B ("[redacted]").


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

This has only been an issue since updating from Amplify version 5.3.6 to 6.15.7/6.15.8. I haven't been able to the release where the issue was introduced

Metadata

Metadata

Assignees

No one assigned

    Labels

    AuthRelated to Auth components/categoryquestionGeneral question

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions