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.36k stars 2.1k forks source link

fetchAuthSession(): credentials / sessionToken Not Persisted Across Restarts #13323

Open brianlenz opened 2 weeks ago

brianlenz commented 2 weeks ago

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

Authentication, GraphQL API, Storage

Amplify Version

v6

Amplify Categories

auth, storage, api

Backend

Amplify CLI

Environment information

``` # Put output below this line System: OS: macOS 14.4.1 CPU: (10) arm64 Apple M1 Max Memory: 643.94 MB / 64.00 GB Shell: 5.2.26 - /opt/homebrew/bin/bash Binaries: Node: 21.7.1 - /opt/homebrew/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 9.2.0 - /usr/local/bin/npm Watchman: 2024.04.01.00 - /opt/homebrew/bin/watchman Browsers: Brave Browser: 124.1.65.123 Chrome: 124.0.6367.94 Safari: 17.4.1 npmGlobalPackages: @aws-amplify/cli: 10.0.0 @graphql-inspector/cli: 3.4.0 appcenter-cli: 2.11.0 expo-cli: 5.4.12 gulp: 4.0.2 ios-deploy: 1.11.4 localtunnel: 1.9.1 lodash: 4.17.10 node-gyp: 3.6.2 npm-check-updates: 3.1.1 npm: 9.2.0 pm2: 5.1.2 serve: 10.0.2 serverless: 3.24.1 yarn-deduplicate: 1.0.2 ```

Describe the bug

We're using React Native, and whenever the app restarts, a call to fetchAuthSession() results in new credentials (accessKeyId, secretAccessKey, & sessionToken) being created, even if a session had been established on previous app run. This means that a network connection is required in order to start up the app and get the session.

To complicate things further, when the network connection is offline, fetchAuthSession() will hang indefinitely. It doesn't appear that there is any way to configure a timeout for it?

In our case, we need to be able to detect these offline scenarios so that we can fall back to offline behavior. I realize that we can use the NetInfo package to try to detect when the app is offline, but we've found it not be the most reliable, so we prefer to make GraphQL requests and handle errors if those fail.

It's interesting to note that the idToken (JWT) is retained through an app restart. In our case, all we're trying to fetch is the idToken (for GraphQL client authentication), not the credentials. But it doesn't seem there's any way for us to get the idToken without refreshing the credentials.

Expected behavior

fetchAuthSession() should persist the session so that it can be re-used on subsequent app runs until expiration. Amplify v5 worked this way via the Auth.currentSession() call, which no longer exists in v6. It appears that due to lack of session persistence, a call to fetchAuthSession() on app launch will require a new session be obtained.

I realize that, even with session persistence, it's possible that on launch the session token may have expired and require a new session to be obtained. It would be great if there was some v6 equivalent to the v5 Auth.currentSession() so that we could get the current session, if any, without requiring a call to the server.

Reproduction steps

  1. In a React Native app, call fetchAuthSession() on startup.
  2. Launch the app to establish an auth session.
  3. Quit the app.
  4. Take the network connection offline.
  5. Launch the app again.
  6. fetchAuthSession() will attempt to fetch a session (credentials) from the server, but since there is no network connection, it will hang indefinitely.

Code Snippet

// Put your code below this line.
await fetchAuthSession();

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 2 weeks ago

Hello, @brianlenz 👋. While this was opened as a bug, it looks like it's very similar to the feature request in #10393 regarding improved offline session management. Also, the behavior described here for the fetchAuthSession() API after the app goes offline and gets restarted seems expected. When loading up (or reloading/restarting) an app, fetchAuthSession() will attempt to get the credentials again due to them being persisted in-memory.

I'll mark this as a duplicate of #10393, but let us know if you have additional questions. Feel free to add any context, upvotes, etc. on that issue and track it for updates on this feature request too. Thanks!

brianlenz commented 2 weeks ago

@cwomack I appreciate the response! I can see how #10393 is related, but that's an old feature request from 2022 in the context of v5, whereas this is v6-specific. v5 supported what we're trying to do here, but v6 does not, so this feels more like a regression than a feature request.

I've been doing more testing, and it looks like the issue also applies to getCurrentUser(). In v5, you could invoke Auth.currentAuthenticatedUser() to get the current user without needing to hit the server, even if the session had expired. getCurrentUser(), however, requires a session in order to complete, which means that if the session/token has expired, it will attempt a request to the server, so there is no way in v6 to ensure we can check a user is signed in to Amplify without an internet connection. This feels like a bug to me since it was possible in v5?

We are going to work around these limitations by reworking our logic, leveraging NetInfo and some internal state to determine if a user is signed in since we can no longer reliably rely on Amplify for that. To me, it feels like these improvements would still be warranted from an Amplify framework standpoint, but I'll leave it to you to determine how best to handle within the issues here since you know the project much better than I do 👍 Thanks!

irfancnk commented 2 weeks ago

Hi @cwomack @brianlenz , I am investigating an issue that might be related to the one currently being discussed. I've noticed in the code at this section: https://github.com/aws-amplify/amplify-js/blob/main/packages/auth/src/providers/cognito/credentialsProvider/credentialsProvider.ts#L119-L148 that when guest user credentials expire, the SDK sends a request with the existing Cognito Identity ID as a parameter to getCredentialsForIdentity. Interestingly, the identity ID is replaced with the one that is returned. In my local tests, however, the same identity ID is returned even after the guest user token expires (after 1 hour). Yet, the identity ID is replaced in the if condition on line 142. Can you clarify if the guest Cognito ID can change and under what circumstances? How can we reproduce this behavior? Please let me know if you need more information, or another issue should be opened.