Closed aliza-khu closed 10 months ago
Hello, @aliza-khu 👋 . This error sounds like the user session may have expired even if you're using the default setting for the refresh token (30 days). Are you only seeing this after multiple days of leaving the app running and/or when closing the app completely as described in your reproduction steps? Or does this happen sporadically during normal use of your app?
It appears that you're also using a custom storage solution with react-native-keychain
rather than AsyncStorage. Are you able to share more details about how this is implemented beyond your manual config? I've not used react-native-keychain
personally, but it's possible that switching out your custom storage back to the default AsyncStorage that Amplify uses for React Native might be a useful test to help identify if this is what is causing the error.
@cwomack, is this issue and https://github.com/aws-amplify/amplify-js/pull/10831 issue has direct connection? can you please confirm this? if it is then updating the library version(5.0.23) makes auto-resolves the issue but please confirm the latest version of the library should not reopen this issue.
@cwomack, Q: Are you only seeing this after multiple days of leaving the app running and/or when closing the app completely as described in your reproduction steps? Or does this happen sporadically during normal use of your app? A: Cannot say anything maybe or maybe not. I don't have an exact scenario for this.
class MyStorage {
// the promise returned from sync function
static syncPromise: Promise<void> = null!;
static setItem(key: string, value: string): boolean {
Keychain.setGenericPassword(MEMORY_KEY_PREFIX + key, value, getAuthOptions(key));
dataMemory[key] = value;
return dataMemory[key];
}
static getItem(key: string): Promise<boolean> {
return Object.prototype.hasOwnProperty.call(dataMemory, key) ? dataMemory[key] : undefined;
}
static removeItem(key: string): boolean {
Keychain.resetGenericPassword(getAuthOptions(key));
return delete dataMemory[key];
}
static clear(): Record<string, unknown> {
dataMemory = {};
return dataMemory;
}
static sync(): Promise<void> {
if (!MyStorage.syncPromise) {
MyStorage.syncPromise = new Promise<void>((res, rej) => {
Keychain.getAllGenericPasswordServices().then(keys => {
const memoryKeys = keys.filter(function (key) {
return key.startsWith(MEMORY_KEY_PREFIX);
});
memoryKeys.map((k, i) => {
Keychain.getGenericPassword({
service: k,
securityLevel: Keychain.SECURITY_LEVEL.SECURE_SOFTWARE,
})
.then(credentials => {
const updatedKey = k.replace(MEMORY_KEY_PREFIX, '');
dataMemory[updatedKey] = credentials ? credentials.password : {};
res();
})
.catch(error => {
captureExcptionInSentryWithScope([], error);
rej(error);
});
});
});
res();
});
}
return MyStorage.syncPromise;
}
}
Can you please help me out on this ASAP? I am facing this issue to our production users.
@aliza-khu, upgrading to the most recent version of Amplify shouldn't cause any issues to upgrade and verify if the fix in #10831 clears this up. Breaking changes don't appear to impact you from what I see in this issue so far for the upgrade to v5, and you can always roll back if something else breaks.
Do you have SSR enabled in your app? And did get a chance to see if changing your storage back to AsyncStorage to see if that stops it?
Hello @cwomack, SSR is not enabled in my app. changing your storage back to AsyncStorage to see if that stops it? - I will check and let you know about that. Till then if using a keychain as storage can be a problem or not, can you check with your team and let me know that. The reason is we believe in security first and AsyncStorage default is not encrypted.
Hello @cwomack, changing your storage back to AsyncStorage to see if that stops it? - The issue occurs very rarely so it had to say.
Can you please share the details like when this "Cannot retrieve a new session. Please authenticate." error happen? What are the causes for the same?
Hello @cwomack, Can you please respond to the above two replies? it seems no response for two days.
@aliza-khu, apologies on the delayed response as I've been trying to reproduce the issue locally. Thank you for clarifying that SSR isn't enabled.
As to the why/when this error is thrown, the Auth.currentSession() method could throw this error when Amplify cannot retrieve a valid session token from Cognito. This would usually meant the user's authentication state has expired or there's a mismatch for the user's session token with Cognito when trying to validate with the backend.
It's understandable that you're prioritizing security and opted to use react-native-keychain
rather than AsyncStorage, but using that package may cause unexpected behavior (as you're seeing with this error) depending on the implementation. You mentioned that you're not sure what scenario is causing this, but is there only one Cognito User Pool at this time? Do you have both "authenticated" and "unauthenticated" roles for users of the app?
I'll review this with our Auth team today to see if there's something tied to the react-native-keychain
package that could cause this, but look forward to getting you unblocked ASAP!
@cwomack, Thank you for your effort putting into this for the resolution of the issue. Here is the reply to your query. is there only one Cognito User Pool at this time? - Yes, I am using a single user for authentication at a time. I have one query for you. Please understand the scenario I describe and let me know your thought on the same.
Scenario: User successfully authenticated with Cognito. The user closes the app and opens it after some time. Once the user opens the app, Auth.currentSession() method gets called but before all of that if the sync method of custom storage resolves the promise without the data getting loaded(refer to custom storage snap shared previously). Will this can be the reason that Auth.currentSession() throws "Cannot retrieve a new session. Please authenticate".
@cwomack, Here is the snap of custom storage which I think is the correct one.
static sync(): Promise<void> {
if (!MyStorage.syncPromise) {
MyStorage.syncPromise = new Promise<void>((res, rej) => {
Keychain.getAllGenericPasswordServices()
.then(keys => {
const memoryKeys = keys.filter(function (key) {
return key.startsWith(MEMORY_KEY_PREFIX);
});
if (memoryKeys.length === 0) {
res();
} else {
memoryKeys.map((k, i) => {
Keychain.getGenericPassword({
service: k,
securityLevel: Keychain.SECURITY_LEVEL.SECURE_SOFTWARE,
})
.then(credentials => {
const updatedKey = k.replace(MEMORY_KEY_PREFIX, '');
dataMemory[updatedKey] = credentials ? credentials.password : {};
i === memoryKeys.length - 1 && res();
})
.catch(error => {
captureExcptionInSentryWithScope([], error);
rej(error);
});
});
}
})
.catch(err => {
captureExcptionInSentryWithScope([], err);
rej(err);
});
});
}
return MyStorage.syncPromise;
}
You can compare this with previously shared custom storage snap.
@cwomack, Can you please verify with your team that custom storage can be the only reason to face the issue "Cannot retrieve a new session. Please authenticate."?
@aliza-khu, the only time the 'Cannot retrieve a new session. Please authenticate.'
error is thrown is when Auth can't get the refreshToken from storage.
const refreshToken = new CognitoRefreshToken({
RefreshToken: this.storage.getItem(refreshTokenKey),
});
if (!refreshToken.getToken()) {
return callback(
new Error('Cannot retrieve a new session. Please authenticate.'),
null
);
}
Is your custom storage cleaning itself out after a set period of time? Or did you set any timeframes for how long things are stored before they expire?
Hello @cwomack, We are cleaning the custom storage when only removeItem method called by aws itself (ref: https://github.com/aws-amplify/amplify-js/issues/11156#issuecomment-1491351128) which is under custom storage. Below is the snap:
static removeItem(key: string): boolean {
Keychain.resetGenericPassword(getAuthOptions(key));
return delete dataMemory[key];
}
And when user logged out I call "Auth.signout();". Please let me know if the app need to remove the custom storage data from another spots.
@cwomack, Can you please reply on the above comments?
@cwomack, I am waiting for your response can you please share your response to the above comments? It's urgent to take action.
@aliza-khu, appreciate your patience as I'm trying to reproduce and find a workaround that can be posted here either with a sample repo or code that utilizes a similar custom storage w/ react-native-keychain.
@cwomack, Let me know if you need any help from me in replicating the same on your end. But as I said it affects our production users so please keep this in high priority. Your help will guide me into come to any conclusion. 🙂
@cwomack, Any updates?
@aliza-khu, I was able to implement your custom storage solution that uses react-native-keychain
in a React Native app and didn't experience the issue. I don't have any issues using AsyncStorage either (rather than custom storage solution w/ keychain). So far it seems like the next steps to determine what is causing this would be:
Try temporarily swapping out the custom storage for the default AsyncStorage fixes the issue (just to isolate and verify if it's being caused by the custom storage or something local tied to that).
Time how long it is taking until you experience the error again (either by looking at logs or manually timing). I know you mentioned that it happens, "after some time" and that it happens very rarely... but maybe we can see if there's a certain time it takes and dig deeper on the Cognito side to see why the session would be expired/invalid.
Determine if this happens with Android, iOS, or both to see if there are related issues.
Compare our package.json to see if there's a dependency issue that might be causing this. This is what I've got and have tested locally, but can't reproduce the issue:
// package.json dependencies
"dependencies": {
"@aws-amplify/ui-react": "^4.6.0",
"@react-native-async-storage/async-storage": "^1.18.1",
"@react-native-community/masked-view": "^0.1.11",
"@react-native-community/netinfo": "^9.3.9",
"@react-navigation/native": "^6.1.6",
"@react-navigation/stack": "^6.3.16",
"amazon-cognito-identity-js": "^6.2.0",
"aws-amplify": "^5.1.2",
"core-js": "^3.30.1",
"expo": "~48.0.11",
"expo-status-bar": "~1.4.4",
"react": "18.2.0",
"react-native": "0.71.6",
"react-native-gesture-handler": "^2.9.0",
"react-native-keychain": "^8.1.1",
"react-native-reanimated": "^3.0.2",
"react-native-safe-area-context": "^4.5.1",
"react-native-screens": "^3.20.0",
"react-redux": "^8.0.5",
"redux": "^4.2.1"
},
"devDependencies": {
"@babel/core": "^7.20.0"
@cwomack, Thank you for putting efforts to reproduce the issue. I agree with the determined steps you shared and will try to apply them from my end as well. But before starting that I want to let you know that the issue I was facing is not occurring from my end. The last attempt change I made to fix that issue was updating the custom storage code. I already shared the change in the previous response and here is the link: https://github.com/aws-amplify/amplify-js/issues/11156#issuecomment-1497260879. You can verify the same with your custom storage code w/ keychain. If you found any major change in the custom storage code then please share your custom storage w/ keychain with me(really appreciated). Now to reproduce the issue which I faced 'Cannot retrieve a new session. Please authenticate' You can follow the below steps:
Note: If you are able to successfully reproduce the issue by following the above steps then it clarifies that the issue was not with the library but the way I use the custom storage implementation.
Suggestion: You can check the above scenario on weak internet as well might be that will help you to reproduce the issue in a short time.
Hoping for your positive feedback. 🙂
Hello @cwomack, Can you please share your feedback 👆 ?
Hello @cwomack, No reply since last week 😞. Please do reply at-least.
Facing a similar issue with Amplify version 4.3.43. Haven't provided a Storage option as you can see from my Amplify config below;
Amplify.configure({ identityPoolId, region, userPoolId, userPoolWebClientId: userPoolClientId, oauth: { domain: userDomain, scope: ["email", "openid", "profile"], redirectSignIn: "https://localhost:3000/demo", redirectSignOut: "https://localhost:3000/demo?sign_out=true", responseType: "token" } });
@aliza-khu, apologies for the delayed response. I had tried to reproduce the issue you're experiencing the Cannot retrieve a new session. Please authenticate
error, but haven't been able to yet. I just reviewed your comment again stating that "weak internet" may help in reproducing, so I'll try throttling the network down to 3G speeds and see if I can reproduce. And you're still not certain whether this happens on iOS or Android?
@its-aazizi, are you experiencing this on either iOS or Android specifically?
@cwomack I'm facing this on web.
Here's a screenshot for my app client settings, for testing purposes I've set the refresh token expiry to an hour, but the issue was still their when the refresh token expiry was set to 30 days, the user would unexpectedly loose the session in an hour or so and get logged out.
@aliza-khu, apologies for the delayed response. I had tried to reproduce the issue you're experiencing the
Cannot retrieve a new session. Please authenticate
error, but haven't been able to yet. I just reviewed your comment again stating that "weak internet" may help in reproducing, so I'll try throttling the network down to 3G speeds and see if I can reproduce. And you're still not certain whether this happens on iOS or Android?@its-aazizi, are you experiencing this on either iOS or Android specifically?
It reproduces when you replace the code of your custom storage with mine (https://github.com/aws-amplify/amplify-js/issues/11156#issuecomment-1491351128 - Not platform specific replicated on both the platforms) and try with a bad internet situation.
Note: Please call the API as soon as the app reaches the very first screen.
@its-aazizi are you experiencing this with both your custom auth flow and the SRP, or just one of them? Also have you tried upgrading to the latest version of Amplify to see if still happens?
@aliza-khu, I've still been unable to reproduce the issue after throttling down the network speed. I believe you're also on v4.3 of Amplify (similar to @its-aazizi). Are you able to try upgrading to the latest version of Amplify to see if the issues resolves?
If it doesn't, are you able to share a sample repo that implements just the basic logic that reproduce this?
Closing this issue as we have not heard back from you. If you are still experiencing this, please feel free to reply back and provide any information previously requested and we'd be happy to re-open the issue.
With the release of the latest major version of Amplify (aws-amplify@>6), we'd also highly recommend updating to the most recent version. Please refer to our release announcement, migration guide, and documentation for more information.
Thank you!
Before opening, please confirm:
JavaScript Framework
React Native
Amplify APIs
Authentication
Amplify Categories
auth
Environment information
Describe the bug
Sometimes Auth.currentSession() throughs an error like "Cannot retrieve a new session. Please authenticate.". For this kind of message, do I really need to re-authenticate anyhow? Like I logged in 7 days ago and my refresh token was going to expire on the 30th day of logging in. Can you please share in which situations this kind of error was raised?
Expected behavior
Auth.currentSession() should able to retrieve a new session if the old session failed due to any reason. Authentication of the user should not be required in this situation.
Reproduction steps
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