aws-sdk-android icon indicating copy to clipboard operation
aws-sdk-android copied to clipboard

Session issue : No cached session

Open lolucosmin opened this issue 1 year ago • 14 comments

Describe the bug Hello, in our project we are using AWS Mobile client version 2.75.0 and lately we have a weird issue on Huawei devices. We have a weird session error: No cached session, the the strange thing is the user has a valid state: User State: SIGNED_IN User Session: will expire at 5/11/24 5:25 PM If I interogate the SDK about user state and sign in state all looks ok, but in logs I see a lot of warnings, I will post here all the details.

@tylerjroach can you help us with this weird error, for us is a major issue.

Environment Information (please complete the following information):

  • AWS Android SDK Version: [2.75.0]
  • Device: [all huawei devices]
  • Android Version: [7 and above]
  • Specific to simulators: [No]

Additional context ----------Log 1---------- Error in decrypting data. javax.crypto.AEADBadTagException at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:517) at javax.crypto.Cipher.doFinal(Cipher.java:2055) at com.amazonaws.internal.keyvaluestore.AWSKeyValueStore.decrypt(AWSKeyValueStore.java:435) at com.amazonaws.internal.keyvaluestore.AWSKeyValueStore.get(AWSKeyValueStore.java:252) at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.readCachedTokens(CognitoUser.java:2762) at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.getCachedSession(CognitoUser.java:1293) at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.getSession(CognitoUser.java:1023) at com.amazonaws.mobile.client.AWSMobileClient$12.run(AWSMobileClient.java:2030) at com.amazonaws.mobile.client.internal.InternalCallback.await(InternalCallback.java:115) at com.amazonaws.mobile.client.AWSMobileClient.getTokens(AWSMobileClient.java:1996) at com.amazonaws.mobile.client.AWSMobileClient.getUserStateDetails(AWSMobileClient.java:1120) at com.amazonaws.mobile.client.AWSMobileClient$5.run(AWSMobileClient.java:900) at com.amazonaws.mobile.client.AWSMobileClient$5.run(AWSMobileClient.java:897) at com.amazonaws.mobile.client.internal.ReturningRunnable.await(ReturningRunnable.java:31) at com.amazonaws.mobile.client.AWSMobileClient.currentUserState(AWSMobileClient.java:879) at com.bfan.sso.logic.services.aws.auth.AmazonAuthService.isSessionExpired(AmazonAuthService.java:132) at com.bfan.sso.logic.server.BaseObsParams.lambda$getSessionStateObserver$1(BaseObsParams.java:52) at com.bfan.sso.logic.server.BaseObsParams$$ExternalSyntheticLambda0.subscribe(D8$$SyntheticClass:0) at io.reactivex.rxjava3.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:41) at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13173) at io.reactivex.rxjava3.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:52) at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13173) at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:644) at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65) at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:929) Caused by: android.security.KeyStoreException: Signature/MAC verification failed at android.security.KeyStore.getKeyStoreException(KeyStore.java:1593) at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:224) at android.security.keystore.AndroidKeyStoreAuthenticatedAESCipherSpi$BufferAllOutputUntilDoFinalStreamer.doFinal(AndroidKeyStoreAuthenticatedAESCipherSpi.java:386) at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506) at javax.crypto.Cipher.doFinal(Cipher.java:2055)  at com.amazonaws.internal.keyvaluestore.AWSKeyValueStore.decrypt(AWSKeyValueStore.java:435)  at com.amazonaws.internal.keyvaluestore.AWSKeyValueStore.get(AWSKeyValueStore.java:252)  at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.readCachedTokens(CognitoUser.java:2762)  at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.getCachedSession(CognitoUser.java:1293)  at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.getSession(CognitoUser.java:1023)  at com.amazonaws.mobile.client.AWSMobileClient$12.run(AWSMobileClient.java:2030)  at com.amazonaws.mobile.client.internal.InternalCallback.await(InternalCallback.java:115)  at com.amazonaws.mobile.client.AWSMobileClient.getTokens(AWSMobileClient.java:1996)  at com.amazonaws.mobile.client.AWSMobileClient.getUserStateDetails(AWSMobileClient.java:1120)  at com.amazonaws.mobile.client.AWSMobileClient$5.run(AWSMobileClient.java:900)  at com.amazonaws.mobile.client.AWSMobileClient$5.run(AWSMobileClient.java:897)  at com.amazonaws.mobile.client.internal.ReturningRunnable.await(ReturningRunnable.java:31)  at com.amazonaws.mobile.client.AWSMobileClient.currentUserState(AWSMobileClient.java:879)  at com.bfan.sso.logic.services.aws.auth.AmazonAuthService.isSessionExpired(AmazonAuthService.java:132)  at com.bfan.sso.logic.server.BaseObsParams.lambda$getSessionStateObserver$1(BaseObsParams.java:52)  at com.bfan.sso.logic.server.BaseObsParams$$ExternalSyntheticLambda0.subscribe(D8$$SyntheticClass:0)  at io.reactivex.rxjava3.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:41)  at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13173)  at io.reactivex.rxjava3.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:52)  at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13173)  at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)  at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:644)  at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)  at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)  at java.util.concurrent.FutureTask.run(FutureTask.java:266)  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)  at java.lang.Thread.run(Thread.java:929) 

----------Log 2----------

Tokens are invalid, please sign-in again. java.lang.Exception: No cached session. at com.amazonaws.mobile.client.AWSMobileClient$12$1.signalTokensNotAvailable(AWSMobileClient.java:2079) at com.amazonaws.mobile.client.AWSMobileClient$12$1.getAuthenticationDetails(AWSMobileClient.java:2049) at com.amazonaws.mobileconnectors.cognitoidentityprovider.CognitoUser.getSession(CognitoUser.java:1036) at com.amazonaws.mobile.client.AWSMobileClient$12.run(AWSMobileClient.java:2030) at com.amazonaws.mobile.client.internal.InternalCallback.await(InternalCallback.java:115) at com.amazonaws.mobile.client.AWSMobileClient.getTokens(AWSMobileClient.java:1996) at com.amazonaws.mobile.client.AWSMobileClient.getUserStateDetails(AWSMobileClient.java:1120) at com.amazonaws.mobile.client.AWSMobileClient$5.run(AWSMobileClient.java:900) at com.amazonaws.mobile.client.AWSMobileClient$5.run(AWSMobileClient.java:897) at com.amazonaws.mobile.client.internal.ReturningRunnable.await(ReturningRunnable.java:31) at com.amazonaws.mobile.client.AWSMobileClient.currentUserState(AWSMobileClient.java:879) at com.bfan.sso.logic.services.aws.auth.AmazonAuthService.isSessionExpired(AmazonAuthService.java:132) at com.bfan.sso.logic.server.BaseObsParams.lambda$getSessionStateObserver$1(BaseObsParams.java:52) at com.bfan.sso.logic.server.BaseObsParams$$ExternalSyntheticLambda0.subscribe(D8$$SyntheticClass:0) at io.reactivex.rxjava3.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:41) at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13173) at io.reactivex.rxjava3.internal.operators.observable.ObservableFlatMap.subscribeActual(ObservableFlatMap.java:52) at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13173) at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96) at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:644) at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65) at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:929)

lolucosmin avatar May 11 '24 13:05 lolucosmin

If I interogate the SDK like this after I get that error and warnings I have this results:

AWSMobileClient.getInstance().getUsername() - same like before getting the error AWSMobileClient.getInstance().isSignedIn() - true AWSMobileClient.getInstance().getIdentityId() - another identity

lolucosmin avatar May 11 '24 13:05 lolucosmin

I have more information about this issue: -appear after 1H when the aws should refresh the session. -in UserStateDetails there is a exception but that method is protected and you can not interogate the skd to see if is null or not.

Screenshot 2024-05-13 160750

As you can see in image the state for user is SIGNED_IN but there no good session also if I as for AWSMobileClient.getInstance().getIdentityId() now I have a new one instead the real one. What I see here the SDK is not changing the user state into GUEST or SIGNED_OUT.

I tried also to intercept that exception but is not working:

public boolean isSessionExpired() { UserStateDetails userState; try { userState = AWSMobileClient.getInstance().currentUserState(); } catch (Exception ex) { return true; } ........

Is weird because in SDK there is a catch @WorkerThread public UserStateDetails currentUserState() { try { return _currentUserState().await(); } catch (Exception e) { throw new RuntimeException("Failed to retrieve user state.", e); } } but is not going into catch block.

So I have no idea how to manage this situation @tylerjroach, do you have a suggestion for me?

lolucosmin avatar May 13 '24 13:05 lolucosmin

Hi @lolucosmin ,

Thanks for reaching out, we will take a look into the issue and provide updates here.

yuhengshs avatar May 13 '24 17:05 yuhengshs

@lolucosmin Huewei devices may be tricky.Are these older devices with Google Play Services, or the newer Huewei devices withouth Google support.

I'm afraid these devices look like they do not appear to have proper Android KeyStore implementations.

Error in decrypting data. javax.crypto.AEADBadTagException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:517)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)```

This crash is coming directly from OS KeyStore implementation.

tylerjroach avatar May 13 '24 17:05 tylerjroach

@lolucosmin Huewei devices may be tricky.Are these older devices with Google Play Services, or the newer Huewei devices withouth Google support.

I'm afraid these devices look like they do not appear to have proper Android KeyStore implementations.

Error in decrypting data. javax.crypto.AEADBadTagException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:517)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)```

This crash is coming directly from OS KeyStore implementation.

Indeed, there are 2 types with Google Services and without, I tested on devices(4 devices) without Google Services and on all of them I have same issue, the sign in is successful but after 1h no session refresh.

But I have a question related to AWSMobileClient SDK. Why there int this function is a catch if you want to throw that exception? Also why that exception from my image above is not in state of user? At least if it will be in state of user we can check that variable. @WorkerThread public UserStateDetails currentUserState() { try { return _currentUserState().await(); } catch (Exception e) { throw new RuntimeException("Failed to retrieve user state.", e); } }

lolucosmin avatar May 14 '24 05:05 lolucosmin

@tylerjroach any news related to this issue?

lolucosmin avatar Jun 18 '24 09:06 lolucosmin

@lolucosmin Are you only seeing this on Huewei devices? And if so, is it all Huewei devices you have tested. We have a Huewei device we can attempt to test with to see if we can replicate the issue.

tylerjroach avatar Jun 18 '24 13:06 tylerjroach

@lolucosmin Are you only seeing this on Huewei devices? And if so, is it all Huewei devices you have tested. We have a Huewei device we can attempt to test with to see if we can replicate the issue.

Hi @tylerjroach , so our app is using aws login system and we have a lot of users for our app which has Huawei devices, almost every day we are getting complains about this issue. This issue appear on both types of devices: devices without google services which is very easy to reproduce it, because appear every 1H. For the other types looks like this issue is randomly. But main purpose is to find a fix for that devices which has no google services to make a better experience for our users.

Thx.

lolucosmin avatar Jun 18 '24 13:06 lolucosmin

@tylerjroach any news about this issue? I see "No one assigned" and is a old one and is important.

lolucosmin avatar Aug 08 '24 06:08 lolucosmin

We do not have a path forward for a fix at the moment.

AWS Android SDK and Amplify both use the Android KeyStore, although they each have different implementations. Amplify v2 uses EncryptedSharedPreferences library from Google.

These devices appear to have problematic implementations of the Android KeyStore. A functioning KeyStore is critical to securely encrypt data at rest on the device. We have no way to work around it.

tylerjroach avatar Aug 14 '24 16:08 tylerjroach

We do not have a path forward for a fix at the moment.

AWS Android SDK and Amplify both use the Android KeyStore, although they each have different implementations. Amplify v2 uses EncryptedSharedPreferences library from Google.

These devices appear to have problematic implementations of the Android KeyStore. A functioning KeyStore is critical to securely encrypt data at rest on the device. We have no way to work around it.

Hi @tylerjroach , OK I see the problem, but at least we can do something to fix the status for user? Ex 1: when we have this exception

Tokens are invalid, please sign-in again. java.lang.Exception: No cached session.

there is no way to change the user state from SIGNED_IN to SIGNED_OUT or GUEST?

Ex2: or can we make that exceprion variable public in UserStateDetails to have access to check it? In this way I can interogatte the user state and if there is a error I can signout the users. It will be a simple check, if state==SIGNED_IN and error!=null means bad state.

With this 2 suggestions at leat the Huawei users can use the app 1h with a correct state and then after 1H when the sdk is trying to refresh the session it will fail, I can intercept the error/new user state and I can show a relogin pop up into the app. Right now I can not do this because the state is correct and the error can not be interogated.

lolucosmin avatar Aug 16 '24 05:08 lolucosmin

@lolucosmin Can you give me an estimate on number of unique devices that are experiencing this issue? Is it only showing on Huawei devices? Are some Huawei devices functioning properly?

Can you also share what (if any) inner exception exists for "No cached session"? After this PR (https://github.com/aws-amplify/aws-sdk-android/pull/3518), the exception should have an inner exception that provides an additional reason for the failure.

The trouble here is that we don't seem to be able to decrypt data stored in SharedPreferences. The only reason The SDK is working at all is because the access token is stored in memory.

With this 2 suggestions at leat the Huawei users can use the app 1h with a correct state and then after 1H when the sdk is trying to refresh the session it will fail, I can intercept the error/new user state and I can show a relogin pop up into the app.

If this is happening on all Huawei devices, couldn't you already do this by checking if exception message is "No cached session" AND Device manufacturer is Huawei?

tylerjroach avatar Aug 19 '24 20:08 tylerjroach

@lolucosmin Can you give me an estimate on number of unique devices that are experiencing this issue? Is it only showing on Huawei devices? Are some Huawei devices functioning properly?

Can you also share what (if any) inner exception exists for "No cached session"? After this PR (#3518), the exception should have an inner exception that provides an additional reason for the failure.

The trouble here is that we don't seem to be able to decrypt data stored in SharedPreferences. The only reason The SDK is working at all is because the access token is stored in memory.

With this 2 suggestions at leat the Huawei users can use the app 1h with a correct state and then after 1H when the sdk is trying to refresh the session it will fail, I can intercept the error/new user state and I can show a relogin pop up into the app.

If this is happening on all Huawei devices, couldn't you already do this by checking if exception message is "No cached session" AND Device manufacturer is Huawei?

Hi @tylerjroach , I don't have a specific number, but we have a lot of users which contact the app support and told us about this issue. When this users contact us we collect information about device and all of them had Huawei devices. After I saw this issue I start make some tests with this type of devices and I found that behavior which we already talk. So if you have a Huawei device which has Google Play Services on it all is fine, but when on the device is missing the refresh session after 1h is not working. As you know in Europe there are a lot of Huawei devices but Google restric them.

"Huawei was banned from Google's Android operating system because of spying concerns. The company has been accused of installing hardware on its devices that could be used to spy or intercept data, which would violate U.S. privacy laws and undermine the security at stake in our cyber world."

To answer at your question : "If this is happening on all Huawei devices, couldn't you already do this by checking if exception message is "No cached session" AND Device manufacturer is Huawei?". Yes I want to make this check and it will be fine, but check SDK , in this class UserStateDetails the exception is private so I can not access it. So if you will change the access for this variable indeed I can make that check on my side. Screenshot 2024-08-21 082924

https://support.google.com/android/thread/29434011/answering-your-questions-on-huawei-devices-and-google-services?hl=en

lolucosmin avatar Aug 21 '24 05:08 lolucosmin

@lolucosmin That sounds reasonable. Will review this with the team and keep you updated.

tylerjroach avatar Aug 21 '24 13:08 tylerjroach