Skip to content

certain users are getting auto logged out on production workloads #14373

Open
@asp3

Description

@asp3

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

GraphQL API, Authentication

Amplify Version

v6

Amplify Categories

auth, api

Backend

Other

Environment information



  System:
    OS: macOS 15.4.1
    CPU: (16) arm64 Apple M4 Max
    Memory: 12.90 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.14.0 - ~/.nvm/versions/node/v22.14.0/bin/node
    Yarn: 1.22.5 - ~/.yarn/bin/yarn
    npm: 10.9.2 - ~/.nvm/versions/node/v22.14.0/bin/npm
    Watchman: 2024.01.22.00 - /usr/local/bin/watchman
  Browsers:
    Brave Browser: 114.1.52.130
    Chrome: 135.0.7049.115
    Safari: 18.4
  npmPackages:
    @biomejs/biome: 1.9.4 => 1.9.4 
    @knowt/tsconfig: * => 0.0.0 
    dotenv-cli: latest => 8.0.0 
    husky: ^8.0.0 => 8.0.3 
    lint-staged: ^12.4.0 => 12.5.0 
    turbo: ^1.10.12 => 1.13.4 
  npmGlobalPackages:
    @aws-amplify/cli: 12.14.4
    corepack: 0.31.0
    eas-cli: 16.1.0
    npm: 10.9.2
    vercel: 41.5.0

Describe the bug

We have been long time users of amplify, and it is currently supporting our production workload of 2M+ MAU. Overall, this is a fantastic library and has saved us thousands of hours of writing our own implementation.

We've made our own patches to this for some of our use cases, but I can't seem to figure out why some issues have been happening recently. We have a massive spike in users in the last week, and these problems have started around this time.

To rule out some other things:
Our UserAuthentication requests utilization % is well below the quota, so its not that we are reaching the 120RPS.

Image

From my understanding, I believe the root cause of this is that cognito-identity network call still made even when custom Authorization is passed in

Currently, our setup is as follows:


const getCognitoAuthToken = async () => {
    const cookieMap = Cookies.get();
    const cookies = Object.keys(cookieMap).map(key => ({
        name: key,
        value: cookieMap[key],
    }));

    let idToken = cookies.filter(
        cookie =>
            cookie.name.includes("CognitoIdentityServiceProvider") &&
            cookie.name.includes("idToken") &&
            // on dev, since we switch between dev and prod a lot, we need to make sure we're using the right token
            cookie.name.includes(USERPOOL_CLIENT_ID)
    )?.[0]?.value;

    if (!idToken) {
        return {};
    }

    const parsedToken = jwtDecode(idToken);
    const isExpired = parsedToken.exp && parsedToken.exp + TOKEN_BUFFER <= now();
    try {
        if (isExpired) {
            // get the new token
            idToken = (await fetchAuthSession()).tokens?.idToken?.toString();
        }

        return {
            Authorization: idToken,
        };
    } catch {
        return {};
    }
};

export const amplifyAuthOverride = {
    API: {
        GraphQL: {
            headers: async () => await getCognitoAuthToken(),
        },
    },
};

We noticed some slight differences needed in api-graphql/src/internals/InternalGraphQLAPI.ts, where we made the necessary changes to make sure the fetchAuthSession call here was not made either.

Line 236

if (typeof customHeaders === 'function') {
			customLibraryHeaders = await customHeaders(body) as Record<string, string>
		} else {
			customLibraryHeaders = {}
		}

		// if an authorization header is set, have the explicit authToken take precedence
		if (authToken) {
			additionalCustomHeaders = {
				...additionalCustomHeaders,
				Authorization: authToken,
			};
		}

		const authHeaders = await headerBasedAuth(
			amplify,
			authMode,
			apiKeyOverride ?? apiKey,
			{ ...additionalCustomHeaders, ...customLibraryHeaders },
		);

Along with the changes in headerBasedAuth:

case 'userPool': {
			if (additionalHeaders.Authorization) {
				break;
			}

			let token: string | undefined;

			try {
				token = (
					await amplify.Auth.fetchAuthSession()
				).tokens?.idToken?.toString();
			} catch (e) {
				// pass, call with no authorization header
			}
			if (token) {
				headers = {
					Authorization: token,
				};
			}

			break;
		}

Now, we would expect that the call to fetchAuthSession would be removed. However, we noticed in the network tab that the requests to https://cognito-identity.us-east-1.amazonaws.com are still made!

https://share.cleanshot.com/zQwxfV9n

Here you can see that there were 10 requests to cognito-identity, but 5 GraphQL API calls were made. I added console logs to make sure neither of the fetchAuthSession calls were made, but it seems like its getting called from somewhere else.

I don't expect this to be fixed in the main package, but if you could point me to where in the code this call is made from, that would help quite a bit as well!

I also guess that when the rate limit error happens, it automatically logs the user out and clears all the values from local storage. Is there a way to stop this behavior, and keep those values so that when the user reloads, they are still logged in? Some users are reporting that they are getting logged out every 5-10 minutes, which is not ideal.

We look forward to being able to patch these issues!

Expected behavior

When Authorization is explicitly passed, we should not be making calls to cognito-identify. Again, I'm not fully sure if this is what is causing it, but I assume this is the reason why they are getting logged out.

As per AWS Docs, there is a 10 request/user/second rate limit on these requests, and when making multiple API calls, this limit may get hit.

Reproduction steps

  1. copy the snippet above, and configure amplify with custom overrides.
  2. make a graphql call, and notice that cognito-identity calls are still made.

Code Snippet

// Put your code below this line.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    AuthRelated to Auth components/categorypending-community-responseIssue is pending a response from the author or community.questionGeneral question

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions