-
Notifications
You must be signed in to change notification settings - Fork 166
Description
Checklist
- The issue can be reproduced in the Auth0.Android sample app (or N/A).
- I have looked into the Readme, Examples, and FAQ and have not found a suitable solution or answer.
- I have looked into the API documentation and have not found a suitable solution or answer.
- I have searched the issues and have not found a suitable solution or answer.
- I have searched the Auth0 Community forums and have not found a suitable solution or answer.
- I agree to the terms within the Auth0 Code of Conduct.
Description
I am encountering an “invalid_grant Unknown or invalid refresh token” error when calling .awaitCredentials() once a token has expired after a valid login on a fresh install of my app.
The expected behavior is that when .awaitCredentials() is called after the token has expired the auth0 SDK will implicitly handle token refresh successfully.
Reproduction
The steps I follow to reproduce this error are as follows:
- I uninstall my app
- I reinstall my app
- The webAuthProvider.logIn() is called
- I enter my credentials and am successfully authenticated
- The success can be seen on the auth0 dashboard logs
- In the .onSuccess() of the webAuthProvider.logIn() I use my getCredentialsManager() helper function
- The getCredentialsManager() helper function creates a CredentialsManager the first time it is called (subsequent calls will return this credentials manager not create a new one)
- I call the .saveCredentials() of the newly created CredentialsManager
- I call .awaitCredentials() and print the result to my logCat and see that there is a token, refresh token, and expiresAt
- As the app is used every api call uses .awaitCredentials() to retrieve the token that will be passed up in the api header
- After the expiresAt time I trigger another api call
- .awaitCredentials() is called and fails with an invalid_grant Unknown or invalid refresh token CredentialsManagerException
- There is no corresponding error in the Auth0 dashboard
Additional context
After a valid log in I print to logCat the token, refresh token, and expires at properties and they all seem valid. Checking .hasValidCredentials() returns true and until the token expires awaitCredentials() works as expected and is able to retrieve the locally stored credentials successfully so I believe the refresh token is being received and stored correctly.
This error is only encountered on a fresh install, it will persist through valid logins until the app has been closed and re opened, at which point the refresh process will proceed as expected.
There are no corresponding errors on the Auth0 dashboard logs when this invalid_grant is encountered and there is no auth api call for a refresh so I believe the refresh token is being found invalid locally by the auth0 SDK.
I use a companion object to ensure my credentials manager is only initialised once.
I pass "openid profile email offline_access" to the scope of the WebAuthProvider.login()
My Auth0 dashboard settings allow refresh token rotation with a rotation overlap period of 30 seconds. I use the same time frame as the minTTL property passed to awaitCredentials()
I am using Kotlin with Composables
I encounter this issue on both physical and emulated devices:
Physical pixel 6 with api 36
Emulated pixel 7 with api 36 running Google Play Store services
Emulated pixel 7 with api 36.1 running Google Api’s services
In attempting to debug this issue I have found a workaround for now, but do not believe this is the expected process:
After logging in successfully I check if this is the first time the app has been launched on the device via a property in the sharedPrefs, and if it is I create a new credentials manager and replace the old one with it. I do not know why this works but the invalid_grant error is not encountered this way and no other errors or odd behaviors seem to occur.
Here is a code snippet of of how I create my CredentialsManager:
val auth0 = Auth0.getInstance(clientID, domain)
val authClient = AuthenticationAPIClient(auth0)
val storage = SharedPreferencesStorage(context)
credentialsManager = CredentialsManager(
authClient,
storage
)
Log.d("credentials", "New Credentials Manager:\n ${System.identityHashCode(credentialsManager)}")
Here is a code snippet of how I create the webAuthProvider
val auth0 = Auth0.getInstance(clientID, domain)
WebAuthProvider.login(auth0)
.withScope("openid profile email offline_access")
.withAudience(audience)
.withRedirectUri(redirect)
.start(context, object : Callback<Credentials, AuthenticationException> {
Auth0.Android version
3.10.0
Android version(s)
8.13.0