Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expired Token: The Token is Expired - Managing Tokens Properly #12734

Closed
3 tasks done
ChristopherGabba opened this issue Dec 20, 2023 · 12 comments
Closed
3 tasks done

Expired Token: The Token is Expired - Managing Tokens Properly #12734

ChristopherGabba opened this issue Dec 20, 2023 · 12 comments
Assignees
Labels
Auth Related to Auth components/category V5

Comments

@ChristopherGabba
Copy link

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

Authentication

Amplify Categories

auth

Environment information

  System:
    OS: macOS 14.0
    CPU: (10) arm64 Apple M2 Pro
    Memory: 295.61 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.7.0 - /opt/homebrew/bin/node
    npm: 10.1.0 - /opt/homebrew/bin/npm
    Watchman: 2023.09.04.00 - /opt/homebrew/bin/watchman
  Browsers:
    Safari: 17.0
  npmPackages:
    @aws-amplify/ui-react-native: ^1.2.28 => 1.2.28 
    @babel/core: ^7.20.0 => 7.23.6 
    @babel/plugin-proposal-export-namespace-from: ^7.18.9 => 7.18.9 
    @babel/preset-env: ^7.20.0 => 7.23.6 
    @babel/runtime: ^7.20.0 => 7.23.6 
    @config-plugins/ffmpeg-kit-react-native: ^6.0.0 => 6.0.0 
    @expo-google-fonts/m-plus-1p: ^0.2.3 => 0.2.3 
    @expo-google-fonts/montserrat: ^0.2.3 => 0.2.3 
    @expo-google-fonts/space-grotesk: ^0.2.2 => 0.2.3 
    @expo/config-plugins: ^7.2.5 => 7.2.5 (5.0.4)
    @expo/metro-config: ^0.10.7 => 0.10.7 
    @gorhom/bottom-sheet: ^4.4.7 => 4.5.1 
    @likashefqet/react-native-image-zoom: ^2.1.1 => 2.1.1 
    @react-native-async-storage/async-storage: 1.18.2 => 1.18.2 
    @react-native-clipboard/clipboard: ^1.11.2 => 1.13.0 
    @react-native-community/cli-platform-ios: ^8.0.2 => 8.0.6 (11.3.10)
    @react-native-community/netinfo: 9.3.10 => 9.3.10 
    @react-navigation/bottom-tabs: ^6.3.2 => 6.5.11 
    @react-navigation/native: ^6.0.2 => 6.1.9 
    @react-navigation/native-stack: ^6.0.2 => 6.9.17 
    @rnx-kit/metro-config: ^1.3.5 => 1.3.14 
    @rnx-kit/metro-resolver-symlinks: ^0.1.26 => 0.1.34 
    @shopify/flash-list: 1.4.3 => 1.4.3 
    @types/i18n-js: 3.8.2 => 3.8.2 
    @types/jest: ^29.2.1 => 29.5.11 
    @types/react: ~18.2.14 => 18.2.43 
    @types/react-test-renderer: ^18.0.0 => 18.0.7 
    @typescript-eslint/eslint-plugin: ^5.59.0 => 5.62.0 
    @typescript-eslint/parser: ^5.59.0 => 5.62.0 
    HelloWorld:  0.0.1 
    amazon-cognito-identity-js: ^6.3.6 => 6.3.8 
    amazon-cognito-identity-js/internals:  undefined ()
    apisauce: 2.1.5 => 2.1.5 
    aws-amplify: ^5.3.11 => 5.3.13 
    axios: ^1.5.0 => 1.6.2 (1.6.0, 0.21.4)
    babel-jest: ^29.2.1 => 29.7.0 
    babel-loader: 8.2.5 => 8.2.5 
    babel-plugin-root-import: ^6.6.0 => 6.6.0 
    cheerio: ^1.0.0-rc.12 => 1.0.0-rc.12 
    core-util-is:  1.0.1 
    date-fns: ^2.29.2 => 2.30.0 
    eslint: 8.17.0 => 8.17.0 
    eslint-config-prettier: 8.5.0 => 8.5.0 
    eslint-config-standard: 17.0.0 => 17.0.0 
    eslint-plugin-import: 2.26.0 => 2.26.0 
    eslint-plugin-n: ^15.0.0 => 15.7.0 
    eslint-plugin-node: 11.1.0 => 11.1.0 
    eslint-plugin-promise: 6.0.0 => 6.0.0 
    eslint-plugin-react: 7.30.0 => 7.30.0 
    eslint-plugin-react-native: 4.0.0 => 4.0.0 
    expo: ^49.0.7 => 49.0.21 
    expo-application: ~5.3.0 => 5.3.1 
    expo-av: ~13.4.1 => 13.4.1 
    expo-blur: ~12.4.1 => 12.4.1 
    expo-build-properties: ~0.8.3 => 0.8.3 
    expo-clipboard: ~4.3.1 => 4.3.1 
    expo-config-plugin-ios-share-extension: ^0.0.4 => 0.0.4 
    expo-constants: ~14.4.2 => 14.4.2 
    expo-contacts: ~12.2.0 => 12.2.0 
    expo-dev-client: ~2.4.11 => 2.4.12 
    expo-device: ~5.4.0 => 5.4.0 
    expo-file-system: ~15.4.4 => 15.4.5 
    expo-font: ~11.4.0 => 11.4.0 
    expo-image: ^1.5.1 => 1.5.1 
    expo-image-picker: ~14.3.2 => 14.3.2 
    expo-linear-gradient: ~12.3.0 => 12.3.0 
    expo-linking: ~5.0.2 => 5.0.2 
    expo-localization: ~14.3.0 => 14.3.0 
    expo-media-library: ~15.4.1 => 15.4.1 
    expo-notifications: ~0.20.1 => 0.20.1 
    expo-splash-screen: ^0.20.5 => 0.20.5 
    expo-status-bar: ~1.6.0 => 1.6.0 
    expo-store-review: ~6.4.0 => 6.4.0 
    expo-video-thumbnails: ~7.4.0 => 7.4.0 
    fbjs-scripts: 3.0.1 => 3.0.1 
    ffmpeg-kit-react-native: ^6.0.2 => 6.0.2 
    firebase: ^10.3.1 => 10.7.1 
    firebase/analytics:  undefined ()
    firebase/app:  undefined ()
    firebase/app-check:  undefined ()
    firebase/auth:  undefined ()
    firebase/auth/cordova:  undefined ()
    firebase/compat:  undefined ()
    firebase/compat/analytics:  undefined ()
    firebase/compat/app:  undefined ()
    firebase/compat/app-check:  undefined ()
    firebase/compat/auth:  undefined ()
    firebase/compat/database:  undefined ()
    firebase/compat/firestore:  undefined ()
    firebase/compat/functions:  undefined ()
    firebase/compat/installations:  undefined ()
    firebase/compat/messaging:  undefined ()
    firebase/compat/performance:  undefined ()
    firebase/compat/remote-config:  undefined ()
    firebase/compat/storage:  undefined ()
    firebase/database:  undefined ()
    firebase/firestore:  undefined ()
    firebase/firestore/lite:  undefined ()
    firebase/functions:  undefined ()
    firebase/installations:  undefined ()
    firebase/messaging:  undefined ()
    firebase/messaging/sw:  undefined ()
    firebase/performance:  undefined ()
    firebase/remote-config:  undefined ()
    firebase/storage:  undefined ()
    i18n-js: 3.9.2 => 3.9.2 
    inherits:  2.0.1 
    isarray:  0.0.1 
    jest: ^29.2.1 => 29.7.0 
    jest-circus: 29 => 29.7.0 
    jest-environment-node: 29 => 29.7.0 
    jest-expo: ^49.0.0 => 49.0.0 
    jsdom: ^22.1.0 => 22.1.0 (20.0.3)
    jsdom-jscore-rn: ^0.1.8 => 0.1.8 
    lodash: ^4.17.21 => 4.17.21 
    lodash.filter: ^4.6.0 => 4.6.0 
    lottie-react-native: ^5.1.6 => 5.1.6 
    metro-config: 0.76.8 => 0.76.8 
    metro-source-map: 0.75.1 => 0.75.1 (0.76.8)
    mobx: 6.6.0 => 6.6.0 
    mobx-react-lite: 3.4.0 => 3.4.0 
    mobx-state-tree: 5.1.5 => 5.1.5 
    mocha: ^10.2.0 => 10.2.0 
    nwmatcher:  1.4.3 
    patch-package: ^6.4.7 => 6.5.1 
    path-browserify:  0.0.0 
    postinstall-prepare: 1.0.1 => 1.0.1 
    prettier: 2.8.8 => 2.8.8 (3.1.1)
    query-string: ^7.0.1 => 7.1.3 (6.10.1)
    querystring:  0.2.0 
    react: 18.2.0 => 18.2.0 
    react-devtools-core: 4.24.7 => 4.24.7 (4.28.5)
    react-dom: 18.2.0 => 18.2.0 
    react-native: 0.72.7 => 0.72.7 
    react-native-bootsplash: ^5.0.2 => 5.1.4 
    react-native-dots-pagination: ^0.3.1 => 0.3.1 
    react-native-element-dropdown: ^2.9.0 => 2.10.1 
    react-native-fs: ^2.20.0 => 2.20.0 
    react-native-gesture-handler: ~2.12.0 => 2.12.1 
    react-native-get-random-values: ^1.9.0 => 1.10.0 
    react-native-mmkv: ^2.11.0 => 2.11.0 
    react-native-pager-view: 6.2.0 => 6.2.0 
    react-native-reanimated: ~3.3.0 => 3.3.0 
    react-native-receive-sharing-intent: ^2.0.0 => 2.0.0 
    react-native-render-html: ^6.3.4 => 6.3.4 
    react-native-safe-area-context: 4.6.3 => 4.6.3 
    react-native-screens: ~3.22.0 => 3.22.1 
    react-native-share-menu: ^6.0.0 => 6.0.0 
    react-native-static-safe-area-insets: ^2.2.0 => 2.2.0 
    react-native-touchable-scale: ^2.2.0 => 2.2.0 
    react-native-url-polyfill: ^2.0.0 => 2.0.0 (1.3.0)
    react-native-vision-camera: ^3.6.6 => 3.6.16 
    react-native-volume-manager: ^1.10.0 => 1.10.0 
    react-native-web: ~0.19.6 => 0.19.9 
    react-native-webview: 13.2.2 => 13.2.2 
    react-native-youtube-iframe: ^2.3.0 => 2.3.0 
    react-test-renderer: 18.2.0 => 18.2.0 
    reactotron-core-client: ^2.8.10 => 2.8.11 (2.8.10)
    reactotron-mst: 3.1.4 => 3.1.4 
    reactotron-react-js: ^3.3.7 => 3.3.9 
    reactotron-react-native: 5.0.3 => 5.0.3 
    regenerator-runtime: ^0.13.4 => 0.13.11 (0.14.0)
    string_decoder:  0.10.31 
    ts-jest: 29 => 29.1.1 
    typescript: ^4.9.4 => 5.3.3 
    urlmaster:  0.2.15 
  npmGlobalPackages:
    @aws-amplify/cli-internal: 12.8.2
    @aws-amplify/cli: 12.6.0
    @react-native-community/netinfo: 9.4.1
    eas-cli: 5.9.2
    expo-cli: 6.3.10
    firebase-tools: 11.24.1
    n: 9.1.0
    node: 20.6.0
    pod-install: 0.1.39
    react-native-spinkit: 1.5.1


Describe the bug

I have my application submitted into TestFlight on iOS and my friend is beta testing the app and there is an issue appearing for him that I have not seen before. When he tries to upload an item to DynamoDB he is getting this error "Expired Token: The Token is Expired".

After much research, I stumbled across this particular Github issue:
#2560

And it shed some light on managing refresh tokens, etc. I built my own custom hook to determine whether or not the user is authenticated based on that issue and the Amplify docs. I figured I had it figured out and he got the alert again when trying to upload something to Dynamo DB again.

Expected behavior

This error should not appear if managed correctly.

Reproduction steps

  1. Set up a simple app with two screens (one before the authentication wall and one after
  2. Use the custom hook below to determine authentication status in the main App.js

Code Snippet

/**
 * This is a custom hook to look for:
 * 1. Existing auth state
 * 2. Future changes in the auth state
 * isAuthenticated is determined by whether or not the authToken exists in the MobXStore
 */

export type AWSUser = {
  username: string
  attributes: {
    sub: string
    birthdate: string
    family_name: string
    given_name: string
    picture?: string
    phone_number: string
  }
}

export function useAWSAuthentication(): { isAuthenticated: boolean; isVerified: boolean } {
  const {
    authenticationStore: { isAuthenticated, setProp },
    userStore,
  } = useStores()

  const [authToggled, setAuthToggled] = useState(false) // Just used as a useEffect trigger to double check authentication
  const [isVerified, setIsVerified] = useState(false)
  const isForeground = useIsForeground()


  useEffect(() => {
    ;(async function manageAuthSession() {
       // https://github.com/aws-amplify/amplify-js/issues/2560
       // Every time the user re-enters the app, do a token check
      if (isForeground) {
          await Auth.currentSession().then((session) => {
            const idTokenExpire = session.getIdToken().getExpiration()
            const refreshToken = session.getRefreshToken()
            const currentTimeSeconds = Math.round(+new Date() / 1000)
            if (idTokenExpire < currentTimeSeconds) {
              console.log('previous token expired, need to get a new one')
              Auth.currentAuthenticatedUser().then((res) => {
                res.refreshSession(refreshToken, (err, data: CognitoUserSession) => {
                  if (err) {
                    console.log("cognito user error, signing out")
                    Auth.signOut()
                    setProp("authToken", "")
                  } else {
                    setProp("authToken", data.getIdToken().getJwtToken())
                  }
                })
              })
            } else {
              console.log("previous token appears to be in good condition here so reusing")
              setProp("authToken", session.getIdToken().getJwtToken())
            }
          }).catch((err) => {
          console.log("Auth current session error: ", err)
          setProp("authToken", "")
        })
      }
    })()
  }, [isForeground])

  useEffect(() => {

  // Used to listen to any changes in the authentication status via the login process
    const authUnsubscribe = Hub.listen("auth", (data) => {
      setAuthToggled((prev) => !prev)
      const authChange = data?.payload?.event
      switch (authChange) {
        case "autoSignIn":
          console.log("User entered in correct verification code")
          setProp("authToken", data?.payload?.data?.signInUserSession?.accessToken?.jwtToken)
          break
        case "signIn":
          console.log("user signed in normally")
          setProp("authToken", data?.payload?.data?.signInUserSession?.accessToken?.jwtToken)
          break
        case "signOut":
          console.log("user signed out")
          setProp("authToken", "")
          break
      }
    })
    return () => {
      authUnsubscribe()
    }
  }, [])

  useEffect(() => {
    ;(async function fetchCurrentUserInfo() {
     // Used to manage the current user's information in the userStore for use throughout the app
      if (isAuthenticated) {
        const {
          username,
          attributes: {
            sub,
            birthdate,
            family_name,
            given_name,
            picture,
            phone_number,
            phone_number_verified,
          },
        } = await Auth.currentUserInfo()

        setIsVerified(phone_number_verified)

        const currentUser = {
          ...userStore.currentUser,
          id: sub,
          username,
          birthdate,
          phoneNumber: phone_number,
          profileImage: picture,
          firstName: given_name,
          lastName: family_name,
        }

        userStore.setProp("currentUser", currentUser)
      }
    })()
  }, [isAuthenticated, authToggled])

  return { isAuthenticated, isVerified }
}

/**
 * This next section is the simple implementation:
 */

const AppStack = observer(function AppStack()Ï {

  const { isAuthenticated } = useAWSAuthentication()

  return (
    <Stack.Navigator
      screenOptions={{ headerShown: false, navigationBarColor: colors.background }}
      initialRouteName={isAuthenticated ? "LoginSignUp" : "Tab"} 
    >
      {isAuthenticated ? (
        <>
          <Stack.Screen name="Tab" component={TabNavigator} />
        </>
      ) : (
        <>
          <Stack.Screen name="LoginSignUp" component={Screens.LoginSignUpScreen} />
        </>
      )}
    </Stack.Navigator>
  )
})

Log output

// Put your logs below this line


aws-exports.js

/* eslint-disable */
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

const awsmobile = {
"aws_project_region": "us-east-1",
"aws_appsync_graphqlEndpoint": "https://5tvnvesrgjfjdclupp5rzrh5gi.appsync-api.us-east-1.amazonaws.com/graphql",
"aws_appsync_region": "us-east-1",
"aws_appsync_authenticationType": "API_KEY",
"aws_appsync_apiKey": "REDACTED",
"aws_cognito_identity_pool_id": "us-east-1:a2d5d29b-d69d-46d6-81c3-c6dc71323225",
"aws_cognito_region": "us-east-1",
"aws_user_pools_id": "us-east-1_eDAImGHL9",
"aws_user_pools_web_client_id": "4uj0jgf4p8m3au9h8u6g429111",
"oauth": {},
"aws_cognito_username_attributes": [],
"aws_cognito_social_providers": [],
"aws_cognito_signup_attributes": [
"GIVEN_NAME",
"FAMILY_NAME",
"BIRTHDATE",
"PHONE_NUMBER"
],
"aws_cognito_mfa_configuration": "OFF",
"aws_cognito_mfa_types": [],
"aws_cognito_password_protection_settings": {
"passwordPolicyMinLength": 8,
"passwordPolicyCharacters": [
"REQUIRES_LOWERCASE",
"REQUIRES_UPPERCASE",
"REQUIRES_NUMBERS",
"REQUIRES_SYMBOLS"
]
},
"aws_cognito_verification_mechanisms": [
"PHONE_NUMBER"
],
"aws_user_files_s3_bucket": "reelfeelmedia",
"aws_user_files_s3_bucket_region": "us-east-1"
};

export default awsmobile;

Manual configuration

No response

Additional configuration

No response

Mobile Device

iPhone 15

Mobile Operating System

iOS 17

Mobile Browser

N/A

Mobile Browser Version

N/A

Additional information and screenshots

F5EBF04A-955E-403F-9BC2-89607FAA1E96_4_5005_c

@ChristopherGabba ChristopherGabba added the pending-triage Issue is pending triage label Dec 20, 2023
@cwomack cwomack added the Auth Related to Auth components/category label Dec 21, 2023
@cwomack cwomack self-assigned this Dec 21, 2023
@cwomack
Copy link
Member

cwomack commented Dec 21, 2023

Hello @ChristopherGabba, and thank you for opening this issue. Working to reproduce this and wanted to confirm, are you seeing this behavior in both dev environment and prod when deployed/built?

@cwomack cwomack added investigating This issue is being investigated and removed pending-triage Issue is pending triage labels Dec 21, 2023
@ChristopherGabba
Copy link
Author

I've never experienced this on a development build, but then again I'm the only one using the development build because my partner is using a TestFlight build. The error has only appeared for him and not me.

@ChristopherGabba
Copy link
Author

ChristopherGabba commented Dec 21, 2023

Good afternoon @cwomack ,
I made some improvements to the refresh token checker to essentially refresh every time it expires and never lose track of the existing expiration time. I will keep you posted the error shows up again. Kind of frustrating that we as Amplify users have to maintain our own refresh tokens, I would have thought the Auth system manages that accordingly. Anyway, hopefully this solves our problem and never lets the token expire.

export function useAWSAuthentication(): { isAuthenticated: boolean; isVerified: boolean } {
  const {
    authenticationStore: { isAuthenticated, setProp },
    userStore,
  } = useStores()

  const [authToggled, setAuthToggled] = useState(false)
  const [tokenRefresher, setTokenRefresher] = useState(false)
  const [isVerified, setIsVerified] = useState(false)
  const isForeground = useIsForeground()

  async function refreshUserSession(refreshToken: CognitoRefreshToken) {
    Auth.currentAuthenticatedUser().then((res) => {
      res.refreshSession(refreshToken, (err, data: CognitoUserSession) => {
        if (err) {
          alert("got an auth error so signing out:" + err)
          Auth.signOut()
          setProp("authToken", "")
        } else {
          console.log("Successfully refreshed token session")
          setProp("authToken", data.getIdToken().getJwtToken())
        }
      })
    })
  }

  // https://github.com/aws-amplify/amplify-js/issues/2560
  useEffect(() => {
    let tokenExpirationTimer: NodeJS.Timeout | undefined
    ;(async function manageUserAuthSession() {
      // Every time we enter the foreground, clear the existing timer (useEffect return)
      // Start a new timer the runs exactly when the idTokenExpires. Once it expires
     // refresh the session and trigger the useEffect again to start another timer
      if (isForeground) {
        await Auth.currentSession()
          .then((session) => {
            const idTokenExpire = session.getIdToken().getExpiration()
            const refreshToken = session.getRefreshToken()
            const currentTimeSeconds = Math.round(+new Date() / 1000)
            const timeRemainingOnToken = idTokenExpire - currentTimeSeconds
            console.log("Token expires in", timeRemainingOnToken * 0.0166667, "minutes")

            tokenExpirationTimer = setTimeout(() => {
              refreshUserSession(refreshToken) 
              setTokenRefresher((prev) => !prev)
            }, timeRemainingOnToken * 1000)
          })
          .catch((err) => {
            console.log("Auth current session error: ", err)
            setProp("authToken", "")
          })
      }
    })()

    return () => {
      if (tokenExpirationTimer) clearTimeout(tokenExpirationTimer)
    }
  }, [isForeground, tokenRefresher])

  useEffect(() => {
    const authUnsubscribe = Hub.listen("auth", (data) => {
      setAuthToggled((prev) => !prev)
      const authChange = data?.payload?.event
      switch (authChange) {
        case "autoSignIn":
          console.log("User entered in correct verification code")
          setProp("authToken", data?.payload?.data?.signInUserSession?.accessToken?.jwtToken)
          break
        case "signIn":
          console.log("user signed in normally")
          setProp("authToken", data?.payload?.data?.signInUserSession?.accessToken?.jwtToken)
          break
        case "signOut":
          console.log("user signed out")
          setProp("authToken", "")
          break
      }
    })
    return () => {
      authUnsubscribe()
    }
  }, [])

  useEffect(() => {
    ;(async function fetchCurrentUserInfo() {
      if (isAuthenticated) {
        const {
          username,
          attributes: {
            sub,
            birthdate,
            family_name,
            given_name,
            picture,
            phone_number,
            phone_number_verified,
          },
        } = await Auth.currentUserInfo()

        setIsVerified(phone_number_verified)

        const currentUser = {
          ...userStore.currentUser,
          id: sub,
          username,
          birthdate,
          phoneNumber: phone_number,
          profileImage: picture,
          firstName: given_name,
          lastName: family_name,
        }

        userStore.setProp("currentUser", currentUser)
      }
    })()
  }, [isAuthenticated, authToggled])

  return { isAuthenticated, isVerified }
}

It does appear to be function correctly at least:
Screenshot 2023-12-21 at 4 30 41 PM

@ChristopherGabba
Copy link
Author

ChristopherGabba commented Dec 25, 2023

@cwomack Hello Chris,
After further beta testing, my partner using the production version is still experiencing this error, even with my updated code/algorithm attempts. It appears that it is only hitting during S3 uploads, but I'm not certain. But clearly this is only happening in Prod. It's working the majority of the time, but the error just seems to hit every now and then for him. Here is our upload code that has worked without error in dev and most of the time in prod, with exception to this one error:

  async function uploadVideoToS3() {
    // Push video to s3

    // Do I need to refresh the token right here in order to avoid this bug??

    setIsUploading(true)

    try {
      // Upload main phone media to S3
      const compressedVideo = await compressVideo(videoUri) // this function uses ffmpeg to reduce the size of the video
      const video = await fetchResourceFromLocalURI(compressedVideo)
      const response = await Storage.put(
        `${post.id}/videoUri.mp4`,
        video,
        {
          level: "public",
          contentType: "video/mp4",
        },
      )
     
      const videoUri = await Storage.get(response.key, { level: "public" })
     ...

    } catch (err) {
      alert("Error during upload process:" + err)
    }

    setIsUploading(false)
  }

@ChristopherGabba
Copy link
Author

@cwomack

Hey Chris another update. I hard coded directly before any upload to S3 a complete refresh of the token like so:

async function upload() {
    // Push reactionVideoUrl to Amazon S3 storage
    setIsUploading(true)

    try {
      // Upload main phone media to S3
      await Auth.currentSession().then((session) => {
        const refreshToken = session.getRefreshToken()
        Auth.currentAuthenticatedUser().then((res) => {
          res.refreshSession(refreshToken, (err, data: CognitoUserSession) => {
            if (err) {
              console.log("Error refreshing token before upload:" + err)
            } else {
              console.log("Successfully refreshed token before upload")
            }
          })
        })
      })

      const videoBlob = await fetchResourceFromLocalURI(videoUrl)
      const response = await Storage.put(
        `video.mp4`,
        videoBlob,
        {
          level: "public",
          contentType: "video/mp4",
        },
      )
    
      }

    } catch (err) {
      alert("Error during upload process:" + err)
    }
    setIsUploading(false)
    setSuccess(true)
  }

That way every single time a user uploads anything to S3, they for sure have a brand new refreshed session. Today, even after doing this, we got the error again:

"The provided token is expired"

I have no idea what else I could try on my end to fix this. My friend can immediately send another one right after this error and he can successfully upload it. So it literally appears to be happening at random.

@ChristopherGabba
Copy link
Author

@cwomack okay I was able to reproduce the bug reliably. What it seems like is occurring is that if the app is in the background for a long time without swiping out of the app (dismissing it on the iPhone), and I open it up after x amount of hours, I get this token error.

What's bizarre is that I hard code a token refresh before every upload to S3 and I still get the error. So basically refreshing the token is not the solution to it.

@ChristopherGabba
Copy link
Author

ChristopherGabba commented Jan 7, 2024

This issue is continuing to plague us. After reading a few other Github threads and issues, perhaps this is related to my Cognito configuration:

This is how it was set up:

Screenshot 2024-01-06 at 6 56 37 PM

I can't tell for sure. I'm not an expert in these tokens, but these refresh tokens were set to expire in 30 days, and the idToken and accessToken were set to 60 minutes, so I upped them to 1 day in the configuration setup for the access and id tokens.

Screenshot 2024-01-06 at 6 52 57 PM

Any advice on our setup would be fantastic.

@ChristopherGabba
Copy link
Author

@cwomack.

I've rewritten my entire authentication system in several ways and this error just keeps coming, its driving us crazy and it is actually one of the last errors that is stopping us from going to production. Essentially right when you open up the application after its been in the background for about a couple of hours and you try to upload something to S3, this error pops up. It's very replicable now.

What's even weirder is that it allows us to write to DynamoDB perfectly immediately before the S3 upload.

Could it be related to our bucket policy for S3:

{
    "Version": "2012-10-17",
    "Id": "Policy1697807526487",
    "Statement": [
        {
            "Sid": "AuthenticatedAccess",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::media/public/*",
                "arn:aws:s3:::media/protected/*"
            ]
        },
        {
            "Sid": "PublicAccess",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::media/public/*",
                "arn:aws:s3:::media/protected/*"
            ]
        }
    ]
}

Thanks in advance for any response... Really frustrated with this one.

@ChristopherGabba
Copy link
Author

All, upgrading to Amplify v6 somehow fixed this issue, and I have not experienced it anymore. I will close it, with the comment that upgrading to v6 solved the problem. The ECONNABORTED error still comes though with v6 on occasion.

@SuperSuccessTalent
Copy link

ECONNABORTED

Hi @ChristopherGabba. I am facing exactly the same issue.
Updating V5 to V6 resolved the "Token expired" issue properly?
You said you still sometimes get ECONNABORTED error on V6.

@uzaymacar
Copy link

I'm facing the exact same issue as well on v5... Any updates on this @cwomack?

@ChristopherGabba
Copy link
Author

ChristopherGabba commented Apr 11, 2024

@SuperSuccessTalent @uzaymacar This issue was (and still is) awful. I couldn't get rid of it for months. Finally I upgraded to V6 from V5 (which has an enormous amount of breaking changes btw, you'll basically have to redo every function altogether) and I basically replaced it with ECONNABORTED. They both come through the exact same -- during the S3 upload.

I suspect that this issue and ECONNABORTED are directly related, if not the same with a different code. Occasionally on upload, it's like the server from AWS rejects the user and throws these errors, particularly during upload to S3. I apologize that you are getting them too, but I'm kind of glad that someone else is that way we can get pressure to fix these errors--they literally kill the user experience. My app relies heavily on uploads and this sometimes locks the app up and forces the user to completely swipe out and reset the app. The most annoying part about them is that you can't really make a reproducible example unless you are in production and using the app a bit. Follow the ECONNABORTED thread and you'll see that I've pinpointed it coming directly from the library. I've been in Beta on my app for about 6 months and we have gotten one of these two errors every single day. I have no idea how no one else is experiencing them unless others just aren't using Amplify Storage w/ React Native.

Sounds like they may need to re-open this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auth Related to Auth components/category V5
Projects
None yet
Development

No branches or pull requests

5 participants