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.4k stars 2.11k forks source link

fetchAuthSession (server) not refreshing token after expiration #13456

Closed KiryuuLight closed 1 month ago

KiryuuLight commented 1 month ago

Before opening, please confirm:

JavaScript Framework

Next.js

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

CDK

Environment information

``` # Put output below this line ```

Describe the bug

I have setup amplify to work with ssr on nextjs 14.1.3. I'm not using a backend resource , the cognito configuration is managed by cdk. When using the client api to sign-in/sign-up everything works as expected. Also the cookies are being set after the user sign-in. But when the token expires the method fetchAuthSession is not able to refresh the token. I've checked if the cookies are being passed to the function (it does).I tried with the option forceRefresh : true but i didn't work. I saw on another issue that could because i'm running it on localhost. So i've deployed my application on cloudfront but it still doesn't work.

Also if i try to log-in again i've received UserAlreadyAuthenticatedException.

Expected behavior

Tokens are refreshed after they expire

Reproduction steps

  1. Import Cognito Configuration coming from CDK.
  2. Set up Amplify on Both Client/Server using ssr : true
  3. Sign-in
  4. Wait until the token expires
  5. fetchAuthSession will return tokens undefined

Code Snippet

Amplify Config

const authConfig: ResourcesConfig = {
  Auth: {
    Cognito: {
      userPoolId: process.env.NEXT_PUBLIC_USER_POOL_ID as string,
      userPoolClientId: process.env.NEXT_PUBLIC_USER_POOL_CLIENT_ID as string,
      userAttributes: {
        email: {
          required: true,
        },
        website: {
          required: true,
        },
      },
    },
  },
};

Server Runner

const { runWithAmplifyServerContext } = createServerRunner({
  config: authConfig,
});

isAuthenticated Helper

const isAuthenticated = async () =>
  await runWithAmplifyServerContext({
    nextServerContext: { cookies },
    async operation(contextSpec) {
      try {
         // User.tokens are undefined after tokens were expired
        const user = await fetchAuthSession(contextSpec, { forceRefresh: true });

        return {
          isAuth: !!user.tokens,
        };
      } catch (e) {
        console.log(e);
        return {
          isAuth: false,
        };
      }
    },
  });

Cognito Construct

this.userPool = new UserPool(scope, 'UserPool', {
      userPoolName: generateResourceId('UserPool'),
      standardAttributes: {
        email: {
          required: true,
          mutable: true,
        },
        website: {
          required: true,
          mutable: false,
        },
      },
      passwordPolicy: {
        minLength: 8,
        requireLowercase: true,
        requireUppercase: true,
        requireDigits: true,
        requireSymbols: true,
      },
      accountRecovery: AccountRecovery.EMAIL_ONLY,
      selfSignUpEnabled: true,
      signInAliases: { email: true },
      removalPolicy: getRemovalPolicy(DEPLOY_ENV),
    });

    this.client = this.userPool.addClient('UserPool-Client', {
      userPoolClientName: generateResourceId('UserPool-Client'),
      authFlows: {
        userSrp: true,
      },
    });

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

cwomack commented 1 month ago

Hello, @KiryuuLight 👋. Are you able to log or share the error that you're receiving? Can you confirm if fetchAuthSession works on the client side when you attempt to refresh tokens (rather than server side)? And if you're able to share what's in your package.json so we can see dependencies and version, that would be appreciated!

KiryuuLight commented 1 month ago

Hello, @KiryuuLight 👋. Are you able to log or share the error that you're receiving? Can you confirm if fetchAuthSession works on the client side when you attempt to refresh tokens (rather than server side)? And if you're able to share what's in your package.json so we can see dependencies and version, that would be appreciated!

Hey @cwomack , on client side it works. And this is what i get when i call the method:

const isAuthenticated = async () =>
  // eslint-disable-next-line no-return-await
  await runWithAmplifyServerContext({
    nextServerContext: { cookies },
    async operation(contextSpec) {
      try {
        const tokens = await fetchAuthSession(contextSpec, { forceRefresh: true });

        console.log('🚀 ~ file: utils.ts:17 ~ operation ~ tokens:', tokens);

        return {
          isAuth: !!user,
          user,
        };
      } catch (e) {
        console.log(e);
        return {
          isAuth: false,
          user: undefined,
        };
      }
    },
  });

export default isAuthenticated;

Response

🚀 ~ file: utils.ts:17 ~ operation ~ tokens: {
  tokens: undefined,
  credentials: undefined,
  identityId: undefined,
  userSub: undefined
}

If i add the getCurrentUser method before call fetchAuthSession i got :

const user = await getCurrentUser(contextSpec);
UserUnAuthenticatedException: User needs to be authenticated to call this API.
    at assertAuthTokens (webpack-internal:///(rsc)/./node_modules/@aws-amplify/auth/dist/esm/providers/cognito/utils/types.mjs:29:15)
    at getCurrentUser (webpack-internal:///(rsc)/./node_modules/@aws-amplify/auth/dist/esm/providers/cognito/apis/internal/getCurrentUser.mjs:16:71)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async operation (webpack-internal:///(rsc)/./lib/amplify/utils.ts:19:30)
    at async runWithAmplifyServerContext (webpack-internal:///(rsc)/./node_modules/aws-amplify/dist/esm/adapter-core/runWithAmplifyServerContext.mjs:25:24)
    at async isAuthenticated (webpack-internal:///(rsc)/./lib/amplify/utils.ts:13:5)
    at async authenticateUser (webpack-internal:///(rsc)/./lib/amplify/authenticateUser.tsx:10:30)
    at async HomePage (webpack-internal:///(rsc)/./app/page.tsx:13:18) {
  underlyingError: undefined,
  recoverySuggestion: 'Sign in before calling this API again.',
  constructor: [class AuthError extends AmplifyError]
}

And if i try to sign-in again i got :

error signing in UserAlreadyAuthenticatedException: There is already a signed in user.
    AmplifyError AmplifyError.mjs:13
    AuthError AuthError.mjs:7
    assertUserNotAuthenticated signInHelpers.mjs:678
    signIn signIn.mjs:22
    handleSubmit index.tsx:53
    onSubmit use-form.mjs:147
    callCallback react-dom.development.js:20498
    invokeGuardedCallbackImpl react-dom.development.js:20547
    invokeGuardedCallback react-dom.development.js:20622
    invokeGuardedCallbackAndCatchFirstError react-dom.development.js:20636
    executeDispatch react-dom.development.js:31986
    processDispatchQueueItemsInOrder react-dom.development.js:32018
    processDispatchQueue react-dom.development.js:32031
    dispatchEventsForPlugins react-dom.development.js:32042
    dispatchEventForPluginEventSystem react-dom.development.js:32232
    batchedUpdates$1 react-dom.development.js:24843
    batchedUpdates react-dom.development.js:28703
    dispatchEventForPluginEventSystem react-dom.development.js:32231
    dispatchEvent react-dom.development.js:29999
    dispatchDiscreteEvent react-dom.development.js:29970
    addEventBubbleListener react-dom.development.js:30219
    addTrappedEventListener react-dom.development.js:32135
    listenToNativeEvent react-dom.development.js:32074
    listenToAllSupportedEvents react-dom.development.js:32085
    listenToAllSupportedEvents react-dom.development.js:32080
    hydrateRoot$1 react-dom.development.js:37768
    hydrateRoot react-dom.development.js:38354
    hydrateRoot client.js:20
    hydrate app-index.js:248
    startTransition react.development.js:2661
    hydrate app-index.js:248
    <anonymous> app-next-dev.js:10
    appBootstrap app-bootstrap.js:57
    loadScriptsInSequence app-bootstrap.js:23
    appBootstrap app-bootstrap.js:56
    <anonymous> app-next-dev.js:8
    NextJS 7

This is my package.json :

{
  "name": "web-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@aws-amplify/adapter-nextjs": "^1.2.1",
    "@mantine/core": "7.9.2",
    "@mantine/form": "^7.10.1",
    "@mantine/hooks": "7.9.2",
    "@tabler/icons-react": "^3.5.0",
    "@typescript-eslint/eslint-plugin": "^7.10.0",
    "aws-amplify": "^6.3.4",
    "next": "14.1.3",
    "prettier": "^3.2.5",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-toastify": "^10.0.5",
    "zod": "^3.23.8",
    "zustand": "^4.5.2"
  },
  "devDependencies": {
    "@types/eslint": "^8",
    "@types/node": "20.11.26",
    "@types/react": "18.2.65",
    "@types/react-dom": "18.2.21",
    "eslint": "8.57.0",
    "eslint-config-airbnb": "^19.0.4",
    "eslint-config-next": "14.1.3",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.1.3",
    "postcss": "^8.4.35",
    "postcss-preset-mantine": "1.15.0",
    "postcss-simple-vars": "^7.0.1",
    "typescript": "5.4.2"
  }
}
cwomack commented 1 month ago

@KiryuuLight, thank you for the additional context and response. We're marking this as a bug as we've reproduce this on our side in v6.3.4. Can you try downgrading your Amplify version to v6.3.3 to see if the issue is resolved?

HuiSF commented 1 month ago

Hi @KiryuuLight , We made a tag release to test the fix for this issue.

@aws-amplify/adapter-nextjs@1.2.3-token-validation.9526c29.0+9526c29
aws-amplify@6.3.6-token-validation.9526c29.0+9526c29

Could you give it a try?

HuiSF commented 1 month ago

Hi @KiryuuLight we've released the fix to resume the token refresh on the server side when it's necessary.

Please upgrade @aws-amplify/adapter-nextjs to version 1.2.4.

KiryuuLight commented 1 month ago

It worked , thanks for the help everyone!

Hi @KiryuuLight we've released the fix to resume the token refresh on the server side when it's necessary.

Please upgrade @aws-amplify/adapter-nextjs to version 1.2.4.