okta / okta-oidc-android

OIDC SDK for Android
https://github.com/okta/okta-oidc-android
Other
60 stars 45 forks source link

Error: Unable to decrypt ProviderConfiguration the key used may be invalidated. Please clear data and try again. null #173

Open ksenia834 opened 3 years ago

ksenia834 commented 3 years ago

Describe the bug

During implementation new feature, we've faced few issues, that seems related:

  1. getUserProfile, refreshToken requests return error "Unable to decrypt ProviderConfiguration the key used may be invalidated. Please clear data and try again. null"
  2. Exception on getting accessToken in Release mode: "Unable to decrypt TokenResponse the key used may be invalidated. Please clear data and try again. null". In Debug mode access token received successfully.

It seems similar issue was already reported and closed in 1.0.5 version https://github.com/okta/okta-oidc-android/issues/113, but for us it still reproduces on 1.0.13

To Reproduce issue 1

  1. Login with Okta user
  2. Get user profile or try refreshToken

Observed Behavior Error "Unable to decrypt ProviderConfiguration the key used may be invalidated. Please clear data and try again. null" ocured

Expected Behavior Received User's profile information

To Reproduce issue 2

  1. Login with Okta user
  2. Get accessToken

Observed Behavior Application crash with exception:

2020-07-24 19:04:51.723 17647-17831/? E/AndroidRuntime: FATAL EXCEPTION: Thread-19
    AuthorizationException: {"type":5,"code":5006,"error":"Illegal block size. Unable to decrypt TokenResponse the key used may be invalidated. Please clear data and try again. null","errorDescription":"Unable to decrypt TokenResponse the key used may be invalidated. Please clear data and try again. null"}
        at com.okta.oidc.util.AuthorizationException$EncryptionErrors.byEncryptionException(Unknown Source:54)
        at com.okta.oidc.clients.sessions.SyncSessionClientImpl.getTokens(Unknown Source:17)
        at com.okta.oidc.clients.sessions.SessionClientImpl.getTokens(Unknown Source:2)
        at com.testapp.Okta.OktaAuthService.refresh(Unknown Source:13)
        at com.testapp.activities.HomeActivity.c(Unknown Source:2)
        at com.testapp.activities.c.run(Unknown Source:2)
        at java.lang.Thread.run(Thread.java:764)

Expected Behavior User's accessToken (if available) and no crash

Code Snippet

Refresh token:

    sessionClient.refreshToken(object : RequestCallback<Tokens?, AuthorizationException?> {
        override fun onSuccess(result: Tokens) {
            listener?.onTokensRefreshed(result);
        }

        override fun onError(error: String, exception: AuthorizationException?) {
            listener?.onRefreshTokenError(error, exception);
        }
    })

Get user profile:

    sessionClient.getUserProfile(object : RequestCallback<UserInfo, AuthorizationException> {
        override fun onSuccess(result: UserInfo) {
            listener?.onUserProfileReceived(result);

        }

        override fun onError(error: String?, exception: AuthorizationException?) {
            listener?.onUserProfileError(error, exception);
        }
    })

Get access token of successfully signed-in users:

val accessToken = client?.sessionClient?.tokens?.accessToken

Environment

Device Information

FeiChen-okta commented 3 years ago

Hi @ksenia834 It sounds like release is failing but debug works? Can you send your proguard file?

bedirguven commented 3 years ago

@FeiChen-okta I have the same issue in my project

Environment

image

ksenia834 commented 3 years ago

Hi @FeiChen-okta only the exception with token reproduces in Release mode. Method getUserProfile sends error in both Debug and Release modes.

Here the content from proguard.pro:

-ignorewarnings
-keep class * {
    public private *;
}

-keep class yourpackage.** { *; }
-keep class com.shockwave.**
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

here the content from proguard-project.txt:

-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}

# added to prevent runtime errors (kazsato)
-keep class * extends java.util.ListResourceBundle {
protected Object[][] getContents();
}

-dontwarn android.support.v4.**
-keep class java.lang.reflect.**
-keep class com.android.vending.billing.**
#modify for Facebook
-keepattributes Signature
-keep class com.facebook.model.** { *; }
-keep class com.shockwave.**

-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient <fields>;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

Thank you for your help

FeiChen-okta commented 3 years ago

@ksenia834 This doesn't sound like a proguard issue but if you can add -keep class com.okta.oidc.** { *; }.

@bedirguven @ksenia834 Does clearing the cache help?

ksenia834 commented 3 years ago

@FeiChen-okta Thanks for the advice, unfortunately modifying the proguard didn't help.

I've found another step to reach this case:

  1. Login into the app with "remember me"
  2. Remove the app from the device
  3. Install the app again
  4. Check user isAuthenticated:
    val session = client?.sessionClient
    if (session != null) {
    return session.isAuthenticated
    }
  5. If isAuthenticated call refreshToken or try to get the idToken. The call result is error: {"type":5,"code":5006,"error":"Illegal block size. Unable to decrypt ProviderConfiguration the key used may be invalidated. Please clear data and try again. null","errorDescription":"Unable to decrypt ProviderConfiguration the key used may be invalidated. Please clear data and try again. null"}

If try to logout and login again - the token then received correctly.

FeiChen-okta commented 3 years ago

Hi @ksenia834 It looks like your app has allowBackup set to true in AndroidManifest. If you uninstall the app and install again then session.isAuthenticated should return false

If it is returning true then it still have encrypted data from the previous install. The private keys from the previous install is invalidated. No way of recovering that encrypted data so you should clear the data once that error happens.

If you want to allowBackup of your apps data and not the SDK data please refer to the following https://developer.android.com/guide/topics/data/autobackup#IncludingFiles

gopalp1709 commented 3 years ago

Hi @FeiChen-okta ,I am also getting same exception, in my case I am using bio-metric and refresh token. It is working fine for first day but on next day it is throwing exception and I am using allowbackup true. Please suggest how to handle this exception and avoid login again for bio-metric with refresh token. Thanks Amol

FeiChen-okta commented 3 years ago

Hi @gopalp1709 Any changes to the device the next day? For example are you adding/removing fingerprint or ping? And what devices is this failing on?

gopalp1709 commented 3 years ago

Hi @FeiChen-okta , I am using following dependencies and Realme XT device (Android 10 ,API 29). implementation 'com.okta.android:oidc-androidx:1.0.11' implementation "androidx.browser:browser:1.0.0"

Also I am setting bio-metric by using below function public void setBiometric() { try { EncryptionManager defaultEncryptionManager = ServiceLocator.provideEncryptionManager(MainActivity.this); defaultEncryptionManager.recreateCipher(); GuardedEncryptionManager guardedBaseEncryptionManager = ServiceLocator.createGuardedEncryptionManager(MainActivity.this); guardedBaseEncryptionManager.recreateCipher(); getWebAuth().migrateTo(guardedBaseEncryptionManager); EncryptionManager encryptionManager = guardedBaseEncryptionManager; ServiceLocator.setEncryptionManager(encryptionManager); //encryptionManager.recreateCipher(); //mEncryptionManager = guardedBaseEncryptionManager; } catch (AuthorizationException exception) { getSessionClient().clear(); navigateToOKTALogin(false); } }

And then on biometric success executing these lines: EncryptionManager encryptionManager = ServiceLocator.provideEncryptionManager(MainActivity.this); encryptionManager.recreateCipher();

Thanks Amol

FeiChen-okta commented 3 years ago

Hi @gopalp1709

When is setBiometric method called? It should only be called when the user confirms to use it. EncryptionManager defaultEncryptionManager = ServiceLocator.provideEncryptionManager(MainActivity.this); Should be done outside of this method and should have a boolean to see if biometrics was turned on or not.

In the samples it uses a CheckBox to turn on/off biometric and saves this option in sharedprefs. You should do the following:

gopalp1709 commented 3 years ago

Hi @FeiChen-okta , I have verified your suggested steps and I have aligned my code with respect to it. But still I am getting authorization exception as follows : AuthorizationException: {"type":5,"code":5004,"error":"User not authenticated and try to use private key. User not authenticated and try to decrypt data: User was authenticated 11 seconds ago"}

Above exception occured while fetching getProfile after biometric success.Even my refresh token is not working to get new session unless I clear storage. Thanks Amol

anantrao07 commented 3 years ago

Were you able to reach any solution? My scenario is exactly the same as yours @gopalp1709

satyajitvure91 commented 2 years ago

Hey all. I'm also trying to integrate biometric authentication and I'm getting the same issue. sessionClient.isAuthenticated returns true but I'm facing the same issue when trying to get RefreshToken. How to fix this?