The Mystery of the Vanishing SessionYour users are browsing along when suddenly, they are kicked to the login screen. It doesn't matter if they were in the middle of a checkout or a complex form; their session simply evaporated. When you dive into the logs, you find the culprit: NotAuthorizedException: Refresh Token has expired. This isn't a crash, but a configuration limit that has finally hit its deadline.
This happens when the refreshToken—the long-lived credential used to silently fetch new 60-minute idTokens—finally expires. Once that token hits its limit (whether that is 24 hours or 30 days), Cognito refuses to issue new credentials. Your background refresh process fails, and the user is left with no choice but to log in from scratch.
Why is this happening now?Cognito isn't broken; it's doing exactly what the App Client settings dictate. You are likely facing one of three scenarios:
- Restrictive Defaults: Your App Client is using a short expiration (like the 1-day default) that doesn't match your users' behavior.- Unit Mismatch: You intended for a 30-day window but accidentally set it to 30 hours.- Zombified State: Your frontend assumes a session is valid until an API call fails, rather than checking the token's health proactively.## The Fix: Extending the Session WindowYou can solve this immediately by adjusting the
RefreshTokenValidity. Most modern web applications aim for at least 30 days to keep the experience frictionless.
Option 1: The AWS Console (Manual Fix)- Open the Cognito Console and select your User Pool.- Navigate to the App integration tab and scroll down to App clients.- Click on the specific client your frontend uses.- Find Token expiration and click Edit.- Change the Refresh token expiration value. Cognito allows anything from 60 minutes up to 3,650 days (10 years). - Save changes. New logins will now inherit this longer lifespan.### Option 2: AWS CLI (For DevSecOps)If you manage multiple environments, don't click through the UI. Use this command to set a 90-day expiration for a more "set and forget" approach:
aws cognito-idp update-user-pool-client \
--user-pool-id us-east-1_exampleID \
--client-id your_client_id \
--refresh-token-validity 90 \
--token-validity-units "{\"RefreshToken\":\"days\"}"
Option 3: Defensive Code ImplementationConfiguration changes only apply to new sessions. You still need to handle existing expired tokens gracefully. Don't let your app crash; catch the exception and redirect the user properly.
import { Auth } from 'aws-amplify';
async function checkSession() {
try {
return await Auth.currentSession();
} catch (err) {
if (err.code === 'NotAuthorizedException') {
// This is the clean way to handle an expired refresh token
console.error("Session expired. Clearing local cache...");
await Auth.signOut();
window.location.href = '/login?reason=expired';
}
}
}
Verifying the Fix Without Waiting 30 DaysDon't wait a month to see if your fix worked. Validate it in five minutes:
- Create a temporary test App Client.- Set the Refresh Token expiration to 1 hour (the minimum).- Log in, then manually advance your system clock or simply wait 61 minutes.- Perform an action that triggers a token refresh.- Verify that your app catches the
NotAuthorizedExceptionand redirects as expected.## Best Practices for ProductionSecurity and user experience are a balancing act. For banking apps, a 24-hour refresh token is standard. For a SaaS dashboard, 30 to 90 days is more appropriate. Security Tip: While adjusting these tokens, you might need to generate secure admin credentials for testing. I use ToolCraft's Password Generator. Since it runs locally in your browser, your generated secrets never touch their servers, which is vital when handling AWS environments.

