Open walterholohan opened 4 weeks ago
Hey @cwomack , thanks for picking up. Happy to jump a call to help triage further if that helps.
Also if you have any other workarounds that would be super helpful i.e. do we just directly hit the cognito endpoints in order to refresh the token?
if you want to check out our app its https://runna.com
Hello, @walterholohan and sorry to hear you've got users running into this. It may be hard to identify why this is happening with the information provided due to these types of unknown errors get asserted in every Auth API (see here). It's odd that the underlyingError
object is empty though.
Can you provide the stack trace of the error if you have it? Or possibly get more details from the network request when this happens? Are you able to access the AuthError.underlyingError
and print that out?
Thanks for the reply Chris. I will add in more debugging logs to see if I can expose more of the error.
In the meantime, should I go pure vanilla in order to refresh the access tokens?
i.e.
@walterholohan, we're hesitant to make any recommendations to manually start making HTTP Post requests to the Cognito endpoints without seeing the debugging logs/errors. Do you know if any of the users that experience this also have network issues or slow connections?
Yep I can see in Sentry a few slow network/no network requests.
In our app, if no network connection then we still want the user to be logged in and we show them cached content which we persist to local storage.
To determine if they are logged in we check async storage to see if a refresh token is present.
Do you think fetchAuthSession is clearing async storage if no network or if the refresh request fails? I can see from logs in sentry it usually when a user opens app more than 24 hours since last session (i.e. access token is 24 hours) so fetchAuthSession I presume would try refresh the token under the hood
@walterholohan, fetchAuthSession()
should attempt to refresh after the access token has expired (providing there's a valid refresh token as well). As for network errors and how that changes the behavior, fetchAuthSession()
will clear tokens if an error is NOT a network error. However, if your users are getting network errors then that shouldn't result in tokens getting cleared (assuming valid refresh token).
Just to summarize my understanding of what's happening so far so we can attempt to reproduce on our side better:
fetchAuthSession()
APIThat look proper for reproduction? And are you able to reproduce this yourself locally at all? If so, can you verify by inspecting your async storage that the tokens are not there?
Hey @cwomack sorry about the delay. I added some extra logs to our app and was just waiting to see what came back. Below is what I found
Sentry shows that there is some timeout or network issue when calling the cognito endpoint
After this fetchAuthSession() will return the below error
{"name":"Unknown","message":"An unknown error has occurred.","underlyingError":{}}
After this on some occasion's the tokens will be removed from async storage.
I have tried to replicate locally but was unable too, but it seems some users get the Unknown
error
@cwomack we seem to be getting a lot of people now where the token is failing to refresh, even though our refresh token expiry is set to 3 years and we have only been using Cognito for 1 year.
Just for context, we fire off quite a few API requests when the app launches, and on each request we check if the token has expired or not. Do you think if we fired fetchAuthSession
multiple times in a short space of time then would this affect anything?
As mentioned above is there any other primitive way of refreshing the token? fetchAuthSession
is quite abstracted so its hard to see from the source code what is happening.
Or happy to jump on a 30 minute call either and I can show you what we do in the app?
@walterholohan, it sounds like there's a possibility this is related to the Quota Limits in Cognito. While the InitiateAuth
call has been deduped to ensure there's less calls happening with Cognito each time, both GetId
and GetCredentialsForIdentity
are not, and are likely hitting the limits (seen here).
Can you help us verify this by clarifying how many calls are made to fetchAuthSession()
per each device as well as provide any screenshots or additional data of the server logs when failures happen?
So at one stage we were making the fetchAuthSession() before every API call thinking that it would just return the idToken
from async store if it was not expired and then if it was expired it would refresh the token. However at the weekend I refactored it so we just make a call once on app start, and then save the idToken in memory and only call fetchAuthSession if the token has expired.
const getIdToken = useCallback(async () => {
try {
if (!idTokenRef.current) {
const idTokenKey = (await AsyncStorage.getAllKeys()).find(key => key.includes('idToken'))
const idToken = await AsyncStorage.getItem(idTokenKey || '')
if (idToken) {
log.debug('Setting id token ref from async storage')
idTokenRef.current = idToken
} else {
log.debug('No id token found in async storage')
}
}
if (idTokenRef.current) {
// Check if the token is expired
const idTokenJWT = decodeJWT(idTokenRef.current)
if (idTokenJWT?.exp && idTokenJWT.exp * 1000 < Date.now()) {
log.debug('Id token is expired, refreshing')
try {
const session = await fetchAuthSession()
const idToken = session.tokens?.idToken?.toString()
if (idToken) {
log.debug('Setting id token in ref')
idTokenRef.current = idToken
} else {
log.debug('No id token found in session')
}
} catch (e) {
if (e instanceof AuthError) {
log.debug(
JSON.stringify({
name: e.name,
message: e.message,
error: e.underlyingError,
recovery: e.recoverySuggestion,
}),
)
if (e.name !== 'Unknown') {
log.error('Error fetchAuthSession')
}
} else {
log.error('Error refreshing idToken', e)
}
}
}
return idTokenRef.current
}
log.debug('No id token found in async storage, return null')
return null
} catch (e) {
log.error('Error getting id token', e)
return null
}
}, [])
FYI we have no server logs as we use AWS AppSync and use cognito as the authentication source
@walterholohan, thanks for the follow up. Wanted to check in and see if the refactoring has helped at all from getting the errors (and possibly confirming that it was the quota limits from Cognito that were the culprit). Let us know if you're still running into this?
Before opening, please confirm:
JavaScript Framework
React Native
Amplify APIs
Authentication
Amplify Version
v6
Amplify Categories
auth
Backend
Other
Environment information
Describe the bug
In our app we call
fetchAuthSession()
when the app load so that we can get the latestidToken
toke to use with our API requests. However randomly for some users they get the error{"name":"Unknown","underlyingError":{}}
which then results in theidToken
token getting removed from async storage.Expected behavior
The user is logged in, and a valid
idToken
should be returnedReproduction steps
await fetchAuthSession()
to get latest token or refresh the tokenawait fetchAuthSession()
will throw an error randomly for some users (FYI we have over 200,000 MAU's)Code Snippet
Log output
aws-exports.js
No response
Manual configuration
Additional configuration
No response
Mobile Device
No response
Mobile Operating System
No response
Mobile Browser
No response
Mobile Browser Version
No response
Additional information and screenshots
No response