Closed ChristopherGabba closed 8 months ago
Hello @ChristopherGabba, and thank you for opening this issue. Working to reproduce this and wanted to confirm, are you seeing this behavior in both dev environment and prod when deployed/built?
I've never experienced this on a development build, but then again I'm the only one using the development build because my partner is using a TestFlight build. The error has only appeared for him and not me.
Good afternoon @cwomack , I made some improvements to the refresh token checker to essentially refresh every time it expires and never lose track of the existing expiration time. I will keep you posted the error shows up again. Kind of frustrating that we as Amplify users have to maintain our own refresh tokens, I would have thought the Auth system manages that accordingly. Anyway, hopefully this solves our problem and never lets the token expire.
export function useAWSAuthentication(): { isAuthenticated: boolean; isVerified: boolean } {
const {
authenticationStore: { isAuthenticated, setProp },
userStore,
} = useStores()
const [authToggled, setAuthToggled] = useState(false)
const [tokenRefresher, setTokenRefresher] = useState(false)
const [isVerified, setIsVerified] = useState(false)
const isForeground = useIsForeground()
async function refreshUserSession(refreshToken: CognitoRefreshToken) {
Auth.currentAuthenticatedUser().then((res) => {
res.refreshSession(refreshToken, (err, data: CognitoUserSession) => {
if (err) {
alert("got an auth error so signing out:" + err)
Auth.signOut()
setProp("authToken", "")
} else {
console.log("Successfully refreshed token session")
setProp("authToken", data.getIdToken().getJwtToken())
}
})
})
}
// https://github.com/aws-amplify/amplify-js/issues/2560
useEffect(() => {
let tokenExpirationTimer: NodeJS.Timeout | undefined
;(async function manageUserAuthSession() {
// Every time we enter the foreground, clear the existing timer (useEffect return)
// Start a new timer the runs exactly when the idTokenExpires. Once it expires
// refresh the session and trigger the useEffect again to start another timer
if (isForeground) {
await Auth.currentSession()
.then((session) => {
const idTokenExpire = session.getIdToken().getExpiration()
const refreshToken = session.getRefreshToken()
const currentTimeSeconds = Math.round(+new Date() / 1000)
const timeRemainingOnToken = idTokenExpire - currentTimeSeconds
console.log("Token expires in", timeRemainingOnToken * 0.0166667, "minutes")
tokenExpirationTimer = setTimeout(() => {
refreshUserSession(refreshToken)
setTokenRefresher((prev) => !prev)
}, timeRemainingOnToken * 1000)
})
.catch((err) => {
console.log("Auth current session error: ", err)
setProp("authToken", "")
})
}
})()
return () => {
if (tokenExpirationTimer) clearTimeout(tokenExpirationTimer)
}
}, [isForeground, tokenRefresher])
useEffect(() => {
const authUnsubscribe = Hub.listen("auth", (data) => {
setAuthToggled((prev) => !prev)
const authChange = data?.payload?.event
switch (authChange) {
case "autoSignIn":
console.log("User entered in correct verification code")
setProp("authToken", data?.payload?.data?.signInUserSession?.accessToken?.jwtToken)
break
case "signIn":
console.log("user signed in normally")
setProp("authToken", data?.payload?.data?.signInUserSession?.accessToken?.jwtToken)
break
case "signOut":
console.log("user signed out")
setProp("authToken", "")
break
}
})
return () => {
authUnsubscribe()
}
}, [])
useEffect(() => {
;(async function fetchCurrentUserInfo() {
if (isAuthenticated) {
const {
username,
attributes: {
sub,
birthdate,
family_name,
given_name,
picture,
phone_number,
phone_number_verified,
},
} = await Auth.currentUserInfo()
setIsVerified(phone_number_verified)
const currentUser = {
...userStore.currentUser,
id: sub,
username,
birthdate,
phoneNumber: phone_number,
profileImage: picture,
firstName: given_name,
lastName: family_name,
}
userStore.setProp("currentUser", currentUser)
}
})()
}, [isAuthenticated, authToggled])
return { isAuthenticated, isVerified }
}
It does appear to be function correctly at least:
@cwomack Hello Chris, After further beta testing, my partner using the production version is still experiencing this error, even with my updated code/algorithm attempts. It appears that it is only hitting during S3 uploads, but I'm not certain. But clearly this is only happening in Prod. It's working the majority of the time, but the error just seems to hit every now and then for him. Here is our upload code that has worked without error in dev and most of the time in prod, with exception to this one error:
async function uploadVideoToS3() {
// Push video to s3
// Do I need to refresh the token right here in order to avoid this bug??
setIsUploading(true)
try {
// Upload main phone media to S3
const compressedVideo = await compressVideo(videoUri) // this function uses ffmpeg to reduce the size of the video
const video = await fetchResourceFromLocalURI(compressedVideo)
const response = await Storage.put(
`${post.id}/videoUri.mp4`,
video,
{
level: "public",
contentType: "video/mp4",
},
)
const videoUri = await Storage.get(response.key, { level: "public" })
...
} catch (err) {
alert("Error during upload process:" + err)
}
setIsUploading(false)
}
@cwomack
Hey Chris another update. I hard coded directly before any upload to S3 a complete refresh of the token like so:
async function upload() {
// Push reactionVideoUrl to Amazon S3 storage
setIsUploading(true)
try {
// Upload main phone media to S3
await Auth.currentSession().then((session) => {
const refreshToken = session.getRefreshToken()
Auth.currentAuthenticatedUser().then((res) => {
res.refreshSession(refreshToken, (err, data: CognitoUserSession) => {
if (err) {
console.log("Error refreshing token before upload:" + err)
} else {
console.log("Successfully refreshed token before upload")
}
})
})
})
const videoBlob = await fetchResourceFromLocalURI(videoUrl)
const response = await Storage.put(
`video.mp4`,
videoBlob,
{
level: "public",
contentType: "video/mp4",
},
)
}
} catch (err) {
alert("Error during upload process:" + err)
}
setIsUploading(false)
setSuccess(true)
}
That way every single time a user uploads anything to S3, they for sure have a brand new refreshed session. Today, even after doing this, we got the error again:
"The provided token is expired"
I have no idea what else I could try on my end to fix this. My friend can immediately send another one right after this error and he can successfully upload it. So it literally appears to be happening at random.
@cwomack okay I was able to reproduce the bug reliably. What it seems like is occurring is that if the app is in the background for a long time without swiping out of the app (dismissing it on the iPhone), and I open it up after x amount of hours, I get this token error.
What's bizarre is that I hard code a token refresh before every upload to S3 and I still get the error. So basically refreshing the token is not the solution to it.
This issue is continuing to plague us. After reading a few other Github threads and issues, perhaps this is related to my Cognito configuration:
This is how it was set up:
I can't tell for sure. I'm not an expert in these tokens, but these refresh tokens were set to expire in 30 days, and the idToken and accessToken were set to 60 minutes, so I upped them to 1 day in the configuration setup for the access and id tokens.
Any advice on our setup would be fantastic.
@cwomack.
I've rewritten my entire authentication system in several ways and this error just keeps coming, its driving us crazy and it is actually one of the last errors that is stopping us from going to production. Essentially right when you open up the application after its been in the background for about a couple of hours and you try to upload something to S3, this error pops up. It's very replicable now.
What's even weirder is that it allows us to write to DynamoDB perfectly immediately before the S3 upload.
Could it be related to our bucket policy for S3:
{
"Version": "2012-10-17",
"Id": "Policy1697807526487",
"Statement": [
{
"Sid": "AuthenticatedAccess",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::media/public/*",
"arn:aws:s3:::media/protected/*"
]
},
{
"Sid": "PublicAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::media/public/*",
"arn:aws:s3:::media/protected/*"
]
}
]
}
Thanks in advance for any response... Really frustrated with this one.
All, upgrading to Amplify v6 somehow fixed this issue, and I have not experienced it anymore. I will close it, with the comment that upgrading to v6 solved the problem. The ECONNABORTED error still comes though with v6 on occasion.
ECONNABORTED
Hi @ChristopherGabba. I am facing exactly the same issue. Updating V5 to V6 resolved the "Token expired" issue properly? You said you still sometimes get ECONNABORTED error on V6.
I'm facing the exact same issue as well on v5... Any updates on this @cwomack?
@SuperSuccessTalent @uzaymacar This issue was (and still is) awful. I couldn't get rid of it for months. Finally I upgraded to V6 from V5 (which has an enormous amount of breaking changes btw, you'll basically have to redo every function altogether) and I basically replaced it with ECONNABORTED. They both come through the exact same -- during the S3 upload.
I suspect that this issue and ECONNABORTED are directly related, if not the same with a different code. Occasionally on upload, it's like the server from AWS rejects the user and throws these errors, particularly during upload to S3. I apologize that you are getting them too, but I'm kind of glad that someone else is that way we can get pressure to fix these errors--they literally kill the user experience. My app relies heavily on uploads and this sometimes locks the app up and forces the user to completely swipe out and reset the app. The most annoying part about them is that you can't really make a reproducible example unless you are in production and using the app a bit. Follow the ECONNABORTED thread and you'll see that I've pinpointed it coming directly from the library. I've been in Beta on my app for about 6 months and we have gotten one of these two errors every single day. I have no idea how no one else is experiencing them unless others just aren't using Amplify Storage w/ React Native.
Sounds like they may need to re-open this issue.
Before opening, please confirm:
JavaScript Framework
React Native
Amplify APIs
Authentication
Amplify Categories
auth
Environment information
Describe the bug
I have my application submitted into TestFlight on iOS and my friend is beta testing the app and there is an issue appearing for him that I have not seen before. When he tries to upload an item to DynamoDB he is getting this error "Expired Token: The Token is Expired".
After much research, I stumbled across this particular Github issue: https://github.com/aws-amplify/amplify-js/issues/2560
And it shed some light on managing refresh tokens, etc. I built my own custom hook to determine whether or not the user is authenticated based on that issue and the Amplify docs. I figured I had it figured out and he got the alert again when trying to upload something to Dynamo DB again.
Expected behavior
This error should not appear if managed correctly.
Reproduction steps
Code Snippet
Log output
aws-exports.js
/ eslint-disable / // WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.
const awsmobile = { "aws_project_region": "us-east-1", "aws_appsync_graphqlEndpoint": "https://5tvnvesrgjfjdclupp5rzrh5gi.appsync-api.us-east-1.amazonaws.com/graphql", "aws_appsync_region": "us-east-1", "aws_appsync_authenticationType": "API_KEY", "aws_appsync_apiKey": "REDACTED", "aws_cognito_identity_pool_id": "us-east-1:a2d5d29b-d69d-46d6-81c3-c6dc71323225", "aws_cognito_region": "us-east-1", "aws_user_pools_id": "us-east-1_eDAImGHL9", "aws_user_pools_web_client_id": "4uj0jgf4p8m3au9h8u6g429111", "oauth": {}, "aws_cognito_username_attributes": [], "aws_cognito_social_providers": [], "aws_cognito_signup_attributes": [ "GIVEN_NAME", "FAMILY_NAME", "BIRTHDATE", "PHONE_NUMBER" ], "aws_cognito_mfa_configuration": "OFF", "aws_cognito_mfa_types": [], "aws_cognito_password_protection_settings": { "passwordPolicyMinLength": 8, "passwordPolicyCharacters": [ "REQUIRES_LOWERCASE", "REQUIRES_UPPERCASE", "REQUIRES_NUMBERS", "REQUIRES_SYMBOLS" ] }, "aws_cognito_verification_mechanisms": [ "PHONE_NUMBER" ], "aws_user_files_s3_bucket": "reelfeelmedia", "aws_user_files_s3_bucket_region": "us-east-1" };
export default awsmobile;
Manual configuration
No response
Additional configuration
No response
Mobile Device
iPhone 15
Mobile Operating System
iOS 17
Mobile Browser
N/A
Mobile Browser Version
N/A
Additional information and screenshots