aws-amplify / amplify-flutter

A declarative library with an easy-to-use interface for building Flutter applications on AWS.
https://docs.amplify.aws
Apache License 2.0
1.31k stars 241 forks source link

Token refresh does not work when USER_PASSWORD_AUTH is used with Device Tracking #4476

Open pdguru81 opened 6 months ago

pdguru81 commented 6 months ago

Description

We configured amplify flutter with the settings below. However, ID/Access Tokens are still not refreshed after they expire. It's quite strange because the docs say Amplify should do this automatically. What are we missing and how do we fix?

Categories

Steps to Reproduce

No response

Screenshots

Settings

Screenshot 2024-02-23 at 09 38 48

Sample code we use per this doc:

Amplify Flutter

Screenshot 2024-02-23 at 09 39 47

Platforms

Flutter Version

3.3.10

Amplify Flutter Version

1.6.1

Deployment Method

Custom Pipeline

Schema

No response

Jordan-Nelson commented 6 months ago

Hello @pdguru81 - Thanks for taking the time to open the issue and provide the info above.

Can you share reproduction steps so that we can attempt to produce this? See below for an example of reproduction steps:

  1. Sign in with username and password using Amplify.signIn()
  2. Call Amplify.fetchAuthSession() and observe that access/id token are valid.
  3. Wait more than 5 minutes for access/id token to expire
  4. Call Amplify.fetchAuthSession() and observe an exception

I see you linked the v1 upgrade guide. Were you previously using Amplify v0.x and are now upgrading to v1?

pdguru81 commented 6 months ago

Yes, the reproduction step is pretty much what you have described. We started out with V0.X, and then migrated to V1? However, the problem persists. In our case, we end up with SessionExpiredException after the session expires.

Jordan-Nelson commented 6 months ago

We started out with V0.X, and then migrated to V1? However, the problem persists

Okay. It sounds like you are saying that upgrading from v0 to v1 isn't required as part of the reproduction steps. I would like to confirm that. Are you able top reproduce this if you follow these steps:

  1. Reset the device (on an iOS simulator you can go to Device -> Erase all Contents and Settings)
  2. Install a version of the app using Amplify v1.6
  3. Sign in with username and password using Amplify.signIn()
  4. Call Amplify.fetchAuthSession() and observe that access/id token are valid.
  5. Wait more than 5 minutes for access/id token to expire
  6. Call Amplify.fetchAuthSession() and observe an exception
pdguru81 commented 6 months ago

Yes, I am confirming that we already use V1 and don't need to upgrade. We also just tested again following step 1 to 6. After 5 minutes, what we get is actually SignedOutException. This is shown in the screenshot below where we signed in using AmplifyV1.6 and then waited 5 minutes. Subsequent calls to Amplify.fetchAuthSession() returned SignedOutException

Screenshot shows the error we log. Screenshot 2024-02-23 at 10 32 53 PM

pdguru81 commented 6 months ago

To add, we just checked cloudtrail logs and are seeing an error about the refreshToken being invalid. It's unclear why this happens since we don't modify or manage the refresh tokens directly.

    "userAgent": "amplify-flutter/1.6.2 ios/17.0.1  aws-sdk-dart/0.3.1",
    "errorCode": "NotAuthorizedException",
    "errorMessage": "Invalid Refresh Token."
pdguru81 commented 6 months ago

We've narrowed this issue down to the fact that device tracking prevents valid refresh tokens from being sent. Had to look at Cloudtrail logs for an idea of the problem. Why do token refreshes fail when device tracking is enabled? 🤔

pdguru81 commented 5 months ago

I haven't heard back. I am updating this ticket with the steps to reproduce for amplify-flutter sdk:

Steps to Reproduce

  1. Create a user pool with Username and Password as the authentication method (Use email for usernames)
  2. Configure a client whose ID Token and Access Token expire after 5 minutes.
  3. Enable device Tracking (can set it to Opt-in)
  4. Enable USER_PASSWORD_AUTH
  5. Use amplify-auth. flutter sdks to connect to Cognito using the Client ID details from above
  6. Sign up a new user - "TestUser". Note that the username in Cognito is "test@example.com"
  7. Manually confirm the user account
  8. Sign in with USER_PASSWORD_AUTH with the username as "test@example.com" Remember the device

Login to cognito and observe that sessions don't auto-refresh after 5 minutes when device tracking is on - even in cases where refreshToken is set to expire after 30 days.

Jordan-Nelson commented 5 months ago

@pdguru81 Thanks for the detailed reproduction steps. I believe this is related to https://github.com/aws-amplify/amplify-flutter/issues/4501

This is a known issue with USER_PASSWORD_AUTH that we are looking into.

Jordan-Nelson commented 5 months ago

@pdguru81 I wanted to confirm that you are using USER_PASSWORD_AUTH to migrate users from a legacy user management system as described in the Cognito docs here: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html#amazon-cognito-user-pools-user-migration-authentication-flow

pdguru81 commented 5 months ago

Just seeing this: No, we are not migrating users from a legacy system. We have been using cognito from day 1 and started encountering this problem when we used flutter sdks to build our mobile app and manage user logins. All the users we have so far were created on cognito

Jordan-Nelson commented 5 months ago

@pdguru81 is there another reason that you are using the USER_PASSWORD_AUTH flow? This is typically only used when you are migrating users from another auth provider.

USER_SRP_AUTH, which is the default in Amplify, is the recommended flow by Cognito unless you have a specific use case that requires a different need. This bug seems to only be present when using USER_PASSWORD_AUTH. I believe if you could switch to USER_SRP_AUTH to resolve this.

pdguru81 commented 5 months ago

Interesting, we will test that out and see.

Jordan-Nelson commented 4 months ago

@pdguru81 - Have you had a chance to test switching to the SRP flow?

Jordan-Nelson commented 3 months ago

@pdguru81 - Just following up on this. I wanted to see if you have had a chance to switch to the SRP flow.

michael-joseph-payne commented 3 months ago

I am definitely seeing similar behaviour, and have confirmed that we are not using USER_PASSWORD_AUTH.

We had been using a very old (v0) version of the flutter package, and were forced to upgrade to avoid deprecation. Post migration, we're now receiving reports from production customers that they can not stay logged in.

We see lots of SessionExpiredException (telling us to sign in to fix) (~500 occurrences) and some NotAuthorizedExceptions (~7)

I have not found a reliable way to reproduce this, but it is definitely happening in production and is causing major headaches for our customers. Please let me know what I can do to provide more information - this is extremely critical for us.

Jordan-Nelson commented 3 months ago

@michael-joseph-payne There was a known issue (see: https://github.com/aws-amplify/amplify-flutter/issues/3995) that prevented access/id tokens from being refreshed when device tracking was enabled after upgrading from v0 to v1. End users would be signed out after the access/id expired. After re-authentication tokens would be refreshed as expected. This was resolved in Amplify Flutter v1.7.2.

michael-joseph-payne commented 3 months ago

@Jordan-Nelson Ok, sounds good - the release in question used amplify_flutter 1.6.1 so I believe it should be addressed by upgrading to 1.7.2+

I'll check back in after we canary a bit

michael-joseph-payne commented 3 months ago

As a follow up question - does setting forceRefresh in FetchAuthSessionOptions work around this issue?

Jordan-Nelson commented 3 months ago

@michael-joseph-payne - it would not.

The issue prior to v1.7.2 was that the device meta data was not migrated from v0 to v1. When a user signs in with device tracking enabled, tokens cannot be refreshed unless the device ID is included as part of the refresh request.