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

Users being logged out with Invalid Refresh Token long before expiry. #2490

Closed
1 task done
GraemeRG opened this issue Jun 26, 2023 · 11 comments · Fixed by #2614
Closed
1 task done

Users being logged out with Invalid Refresh Token long before expiry. #2490

GraemeRG opened this issue Jun 26, 2023 · 11 comments · Fixed by #2614
Assignees
Labels
auth Related to the Auth category/plugins bug Something isn't working

Comments

@GraemeRG
Copy link

Before opening, please confirm:

Language and Async Model

Kotlin - Coroutines

Amplify Categories

Authentication

Gradle script dependencies

// Put output below this line
    implementation 'com.amplifyframework:aws-auth-cognito:2.8.5'
    implementation 'com.amplifyframework:core-kotlin:2.8.5'
    implementation 'com.amplifyframework:aws-api:2.8.5'

Environment information

# Put output below this line
------------------------------------------------------------
Gradle 7.3.3
------------------------------------------------------------

Build time:   2021-12-22 12:37:54 UTC
Revision:     6f556c80f945dc54b50e0be633da6c62dbe8dc71

Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.7 (Amazon.com Inc. 17.0.7+7-LTS)
OS:           Mac OS X 13.2.1 aarch64


Please include any relevant guides or documentation you're referencing

No response

Describe the bug

We have noticed a large spike in user's being logged out of our application following our upgrade to the Amplify 2.x series of libraries. After some investigation we have found that upon calling Amplify.Auth.fetchAuthSession() the user will receive the following result:

AWSCognitoAuthSession(isSignedIn=false, identityIdResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE}, awsCredentialsResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE}, userSubResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE}, userPoolTokensResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE})

This is occurring as little as 30 minutes after the user is authenticating. We have our auth token expiry set to 120 minutes and our refresh token expiration set to 90 days.

Reproduction steps (if applicable)

  1. Authenticate with application
  2. Wait 30minutes -> 1hr 30 minutes (can also be done by manually setting clock on Android device)
  3. Relaunch application
  4. Observed logged out state

Code Snippet

// The logic for user signing in.
try {
            val isSignedIn = Amplify.Auth.signIn(username, password).isSignedIn
            when {
                isSignedIn -> LoginSuccessEvent(Amplify.Auth.authenticatedUserName())
                else -> LoginFailureEvent(GENERIC_FAILURE)
            }
        } catch (exception: AuthException) {
            if(exception is SignedInException) {
                LoginFailureEvent(ALREADY_SIGNED_IN)
            } else {
                when (exception.cause) {
                    is NotAuthorizedException -> LoginFailureEvent(NOT_AUTHORISED)
                    is UserNotConfirmedException -> LoginFailureEvent(UNVERIFIED)
                    is LimitExceededException -> LoginFailureEvent(LIMIT_EXCEEDED)
                    else -> LoginFailureEvent(GENERIC_FAILURE)
                }
            }
        }

// Later, when fetching the signed in status
Amplify.Auth.fetchAuthSession().isSignedIn

Log output

// Put your logs below this line


amplifyconfiguration.json

{
  "UserAgent": "aws-amplify-cli/2.0",
  "Version": "1.0",
  "api": {
    "plugins": {
      "awsAPIPlugin": {
        "XXXXXXXX": {
          "endpointType": "GraphQL",
          "endpoint": "XXXXXXXXXXXXXXX",
          "region": "eu-west-1",
          "authorizationType": "AMAZON_COGNITO_USER_POOLS"
        }
      }
    }
  },
  "analytics": {
    "plugins": {
      "awsPinpointAnalyticsPlugin": {
        "pinpointAnalytics": {
          "appId": "XXXXXXXXXXXXXXXX",
          "region": "eu-west-1"
        },
        "pinpointTargeting": {
          "region": "eu-west-1"
        }
      }
    }
  },
  "auth": {
    "plugins": {
      "awsCognitoAuthPlugin": {
        "UserAgent": "aws-amplify-cli/0.1.0",
        "Version": "0.1.0",
        "IdentityManager": {
          "Default": {}
        },
        "CognitoUserPool": {
          "Default": {
            "PoolId": "XXXXXXXXXXXX",
            "AppClientId": "XXXXXXXXXXXX",
            "Region": "eu-west-1",
            "MigrationEnabled": true
          }
        },
        "CredentialsProvider": {
          "CognitoIdentity": {
            "Default": {
              "PoolId": "XXXXXXXXXXXXXXXXXXXX",
              "Region": "eu-west-1"
            }
          }
        },
        "AppSync": {
          "Default": {
            "ApiUrl": "XXXXXXXXXXXXXXXXXXXXXXX",
            "Region": "eu-west-1",
            "AuthMode": "AMAZON_COGNITO_USER_POOLS",
            "ClientDatabasePrefix": "XXXXXXXXXXXXXXXXXXX"
          }
        },
        "Auth": {
          "Default": {
            "authenticationFlowType": "USER_PASSWORD_AUTH"
          }
        }
      }
    }
  }
}

GraphQL Schema

// Put your schema below this line

Additional information and screenshots

No response

@GraemeRG
Copy link
Author

Hello, as a follow up to the above I have tried adding Amplify.Auth.getCurrentUser() before any Amplify.Auth.fetchAuthSession().isSignedIncalls to see what this returns. In the event where the user is still logged in (as expected), the getCurrentUser() returns the user's AuthUser object as expected. However, in the event where we are logged out and getting the SessionExpiredException as shown above, the getCurrentUser() method throws an exception of the following type:

InvalidUserPoolConfigurationException{message=The user pool configuration is missing or invalid., cause=null, recoverySuggestion=Please check the user pool configuration in your amplifyconfiguration.json file.}`

I have verified that our amplifyconfiguration.json file is identical to our iOS application, which is not experiencing these issues.

@gpanshu
Copy link
Contributor

gpanshu commented Jun 26, 2023

@GraemeRG something is wiping your credentials on the device? Is it possible the user is doing a clear data ?

The InvalidUserPoolConfigurationException on getCurrentUser is only fired if the accessToken itself is null. Source

@eeatonaws eeatonaws added bug Something isn't working pending-community-response Issue is pending response from the issue requestor auth Related to the Auth category/plugins labels Jun 27, 2023
@GraemeRG
Copy link
Author

There's nothing wiping the user from our side, we only use the sign out method in the library if the user manually logs out, but we don't see this event often via our analytics.
In terms of whether user's are clearing their app data manually, we're seeing a large percentage of our active users get logged out with this event so it doesn't seem like they would all be potentially manually clearing their data that frequently and in that volume.

@eeatonaws eeatonaws removed the pending-community-response Issue is pending response from the issue requestor label Jun 28, 2023
@gpanshu
Copy link
Contributor

gpanshu commented Jun 29, 2023

Hi @GraemeRG I was able to reproduce your scenario only when I do a signout before either a fetchAuthSession or a getCurrentUser. In my test I signed in (access token expiry is 125 minutes and refresh token expiry was set to 90 days) and then I closed the app overnight and opened the app the next day and did a fetchAuthSession (to ensure it was not automatically I made the fetchAuthSession with forceRefresh on a button click). The force refresh successfully refreshed the access token after they would have been undoubtedly expired since my refresh token was still valid.

Some troubleshooting questions for you:

  1. Is it at all possible that you are calling signout somewhere before fetchAuthSession that is clearing the tokens out?
  2. Can you reproduce this on your device?
  3. If (2) is true can you add Amplify.addPlugin(AndroidLoggingPlugin(LogLevel.DEBUG)) as the first plugin before others that you have added in your application class and add your logs here so that I can see the sequence of events.

@gpanshu gpanshu self-assigned this Jun 29, 2023
@gpanshu gpanshu added the pending-community-response Issue is pending response from the issue requestor label Jun 29, 2023
@GraemeRG
Copy link
Author

GraemeRG commented Jul 3, 2023

Hello,
To verify I have removed our logic to sign the user out and confirmed that I still see the above issue.
I can recreate this on my own device yes, and have added the AndroidLoggingPlugin to capture information. I can post a follow up with anything that would be of interest.

One thing I have noticed that I was hoping you could assist with. I have noticed in the loggers output that the signInMethod is recorded as ApiBased(authType=USER_SRP_AUTH). We are using USER_PASSWORD_AUTH and when verifying this same log on an iOS device we see an auth type of USER_PASSWORD_AUTH.
To see if I could mimic this behaviour on Android I tried removing the Auth config from the amplify_configuration.json and also tried passing the following code as sign in options when signing the user in:

val options = AWSCognitoAuthSignInOptions.builder()
                .authFlowType(AuthFlowType.USER_PASSWORD_AUTH)
                .build()
Amplify.Auth.signIn(username, password, options)

However the authType still reads USER_SRP_AUTH. I am unsure if these are connected and if this is necessary?

@gpanshu
Copy link
Contributor

gpanshu commented Jul 3, 2023

That is quite strange, can you paste the relevant logs that you are seeing as I am not seeing the same on my end. I have CUSTOM_AUTH in my configuration but I am hardcoding it to USER_PASSWORD_AUTH in the authentication flow inside sign in options similar to what you have above.

@GraemeRG
Copy link
Author

GraemeRG commented Jul 3, 2023

I have split the logs into the three different parts of the journey, from before a user authenticates, once a user signs in and then after the user comes back to the app with an invalid token. These files are availble here:
Pre-Login
Post Login
Invalid Token

@GraemeRG
Copy link
Author

GraemeRG commented Jul 4, 2023

Hello there, I have created a demo project that mimics the necessary parts of our set up to replicate this issues. You can get access to this here.

@sameera26
Copy link

sameera26 commented Jul 6, 2023

I also have the same issue. I have configured my token validity durations as below:
access token: 15 min
id token: 1 hr
refresh token: 30 days

If access token is expired(after 16 min) I call Amplify.Auth.fetchAuthSession() to get the renewed access token. But it gives me this exception.

cognitoAuthSession: AWSCognitoAuthSession(isSignedIn=false, identityIdResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE}, awsCredentialsResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE}, userSubResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE}, userPoolTokensResult=AuthSessionResult{value=null, error=SessionExpiredException{message=Your session has expired., cause=NotAuthorizedException(message=Invalid Refresh Token.), recoverySuggestion=Please sign in and reattempt the operation.}, type=FAILURE})

Here is my implementation code:

//mobile OTP login code
val options = AWSCognitoAuthSignInOptions.builder()
            .authFlowType(AuthFlowType.CUSTOM_AUTH_WITHOUT_SRP)
            .build()
Amplify.Auth.signIn(mobileNumber, null, options)
  `val options = AuthFetchSessionOptions.builder().forceRefresh(true).build()

    Amplify.Auth.fetchAuthSession(options,
    {
        val cognitoAuthSession: AWSCognitoAuthSession = it as AWSCognitoAuthSession
        Log.i("AuthQuickstart", "cognitoAuthSession: $cognitoAuthSession")

        if (cognitoAuthSession.isSignedIn) {
            Log.i("AuthQuickstart", "user is already signed in")
            when (cognitoAuthSession.userPoolTokensResult.type) {
                AuthSessionResult.Type.SUCCESS -> {

                }
                AuthSessionResult.Type.FAILURE -> {
                    Log.e("AmplifyQuickstart", "Failed to fetch auth session")
                }
            }
        } else {
            Log.e("AmplifyQuickstart", "user is not signed in")
        }
    }, { error ->
        Log.e("AmplifyQuickstart", "Failed to fetch auth session", error)
    })

`

@sameera26
Copy link

@gpanshu I can reproduce the issue when device tracking is enabled. Tagging similar issue ticket #2506

@github-actions
Copy link
Contributor

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth Related to the Auth category/plugins bug Something isn't working
Projects
None yet
4 participants