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

Incorrect error message from SecureCredentialsManager [SDK-2078] #359

Closed
sproctor opened this issue Oct 12, 2020 · 10 comments
Closed

Incorrect error message from SecureCredentialsManager [SDK-2078] #359

sproctor opened this issue Oct 12, 2020 · 10 comments
Labels
waiting for customer This issue is waiting for a response from the issue or PR author

Comments

@sproctor
Copy link

Description

In my app there are times where I access the credentials many times sequentially. I don't know the root cause of this issue, but the error message given by Auth0 is incorrect.

com.auth0.android.authentication.storage.CredentialsManagerException: A change on the Lock Screen security settings have deemed the encryption keys invalid and have been recreated. Any previously stored content is now lost. Please, try saving the credentials again.
        at com.auth0.android.authentication.storage.SecureCredentialsManager.continueGetCredentials(SecureCredentialsManager.java:250)
        at com.auth0.android.authentication.storage.SecureCredentialsManager.getCredentials(SecureCredentialsManager.java:207)
        at [my application]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
     Caused by: com.auth0.android.authentication.storage.CryptoException: The RSA encrypted input is corrupted and cannot be recovered. Please discard it.
        at com.auth0.android.authentication.storage.CryptoUtil.RSADecrypt(CryptoUtil.java:292)
        at com.auth0.android.authentication.storage.CryptoUtil.getAESKey(CryptoUtil.java:356)
        at com.auth0.android.authentication.storage.CryptoUtil.decrypt(CryptoUtil.java:400)
        at com.auth0.android.authentication.storage.SecureCredentialsManager.continueGetCredentials(SecureCredentialsManager.java:242)
        at com.auth0.android.authentication.storage.SecureCredentialsManager.getCredentials(SecureCredentialsManager.java:207) 
        at [my application]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) 
     Caused by: javax.crypto.IllegalBlockSizeException
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
        at javax.crypto.Cipher.doFinal(Cipher.java:2055)
        at com.auth0.android.authentication.storage.CryptoUtil.RSADecrypt(CryptoUtil.java:262)
        at com.auth0.android.authentication.storage.CryptoUtil.getAESKey(CryptoUtil.java:356) 
        at com.auth0.android.authentication.storage.CryptoUtil.decrypt(CryptoUtil.java:400) 
        at com.auth0.android.authentication.storage.SecureCredentialsManager.continueGetCredentials(SecureCredentialsManager.java:242) 
        at com.auth0.android.authentication.storage.SecureCredentialsManager.getCredentials(SecureCredentialsManager.java:207) 
        at [my application]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) 
     Caused by: android.security.KeyStoreException: Invalid operation handle
        at android.security.KeyStore.getKeyStoreException(KeyStore.java:1301)
        at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:176)
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
        at javax.crypto.Cipher.doFinal(Cipher.java:2055) 
        at com.auth0.android.authentication.storage.CryptoUtil.RSADecrypt(CryptoUtil.java:262) 
        at com.auth0.android.authentication.storage.CryptoUtil.getAESKey(CryptoUtil.java:356) 
        at com.auth0.android.authentication.storage.CryptoUtil.decrypt(CryptoUtil.java:400) 
        at com.auth0.android.authentication.storage.SecureCredentialsManager.continueGetCredentials(SecureCredentialsManager.java:242) 
        at com.auth0.android.authentication.storage.SecureCredentialsManager.getCredentials(SecureCredentialsManager.java:207) 
        at [my application]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

Reproduction

Get the credentials and idToken about 100 times, consecutively.

Environment

auth0 version 1.27.0
android sdk 30

@lbalmaceda
Copy link
Contributor

@sproctor 👋 So the repro steps are to obtain the credentials 100 times in a row? 🤔 what is that use case for? Can you elaborate a bit on how your application works and why you request that many times the credentials?

@lbalmaceda lbalmaceda added the waiting for customer This issue is waiting for a response from the issue or PR author label Oct 13, 2020
@sproctor
Copy link
Author

Downloading more than 100 files that I'm using the id token to authenticate. Turns out downloading those files was a bad idea, so it's not an urgent issue for me, still seems like a bug though.

@lbalmaceda
Copy link
Contributor

@sproctor OK but the ID token is meant to be consumed locally, not to be attached to any request. What you typically want to send in that API request is the access token, with the proper audience, of course.

In any case, if you know you are going to make 100 requests in the next 5 minutes, there's no need to obtain the tokens from the credentials manager 100 times. You could obtain it once and use it 100 times. The credential manager classes are not thread-safe, so calling them that many times in such a short period would probably be the reason for these errors.

@sproctor
Copy link
Author

Thanks for letting me know about the thread-safety issue. I hadn't considered that. I'll make sure to synchronize my calls to the credentials manager regardless.

The reason it was happening 100 times is due to how things were structured and it was a rare enough occurrence that it didn't really make sense to cache the tokens.

I am using the id token because there are custom claims that need to be passed to the server. The user's permissions are in the claims and the API needs to verify the user has the proper permissions. I thought I needed to use the id token for that.

@sproctor
Copy link
Author

I'm closing this issue since synchronizing the calls to credential manager was indeed the correct solution. I didn't notice any indication that credentials manager is not thread-safe. I don't know if there's an reasonable way to warn about it, but it caught me off-guard. Thank you for your help.

@lbalmaceda
Copy link
Contributor

You authenticate in the mobile app and get an access token. You should request the audience of your API. Then pass the received access token to your API, verify the right aud is contained in the token, and do the operations the token is authorized to do, typically checking the scope claim to tell that. If you need specific claims in that access token, I know that using "Auth0 Rules" you could add claims to an access token after a user authenticates. But you will have to check if that fits your use case. If you have doubts about this I suggest you raise a ticket in https://support.auth0.com as they will be best suited to help you.

Regarding thread-safety: I never heard that the android keystore is thread-safe, and the official docs also lack that. As a general rule, I tend to assume every class is not thread-safe unless explicitly called out. We could add a line in the class-level java doc telling the thread-safety of each of them regardless of what everyone assumes.

@sproctor
Copy link
Author

I think the hasura auth0 guide (https://hasura.io/learn/graphql/android/apollo-client/) is leading people (or maybe just me) in the wrong direction. I'll start an issue over there. I've updated my app to use an api/access token. Thanks again for your help.

The reason I was surprised by the thread-safety is that I was only reading the key. It's pretty rare for reads to not be thread-safe with other reads in my experience.

@lbalmaceda
Copy link
Contributor

I'd agree but in the case of credential managers, when a token has expired and needs to be refreshed with the refresh token, if you as dev don't take care of the concurrency you might end up making several "renew token" calls to the server. And if in addition you have "refresh token rotation" enabled, that means that only the first request will succeed as the refresh tokens are not reusable in that scenario.

What part in specific about that linked page should I look for? I can't find anything related to Auth0 on that link. Cheers

@lbalmaceda lbalmaceda changed the title Incorrect error message from SecureCredentialsManager Incorrect error message from SecureCredentialsManager [SDK-1028] Oct 19, 2020
@lbalmaceda lbalmaceda changed the title Incorrect error message from SecureCredentialsManager [SDK-1028] Incorrect error message from SecureCredentialsManager [SDK-2078] Oct 19, 2020
@sproctor
Copy link
Author

Sorry, it's the last code block. network.setApolloClient(credentials.idToken!!, application) It's not super obvious that it's Auth0 from just that page. They know about the issue, judging from a comment on hasura/graphql-engine#4847 but they haven't fixed it or their config generation for Auth0.

@poovamraj
Copy link
Contributor

Hi everyone, if you are still looking for a thread-safe version of this method, we have released it in 2.7.0. Do take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for customer This issue is waiting for a response from the issue or PR author
Projects
None yet
Development

No branches or pull requests

3 participants