I am writing a Next.js app for a client. The app is using NextAuth to authenticate users via AWS Cognito (OAuth, OIDC, JWT, etc.). Currently, I am using the Cognito hosted UI for authentication. All is working well. However, the client would like sessions to expire if the users close the browser without explicitly logging out. I modified my NextAuth config to use a JWT cookie that expires after only 3 minutes and a keep-alive that keeps the session open as long as the user remains on the site. So far, so good… except not really.
The Problem: When the user is logged in, closes the browser, and reopens the app 5 minutes later, my local app session is correctly expired. This triggers a redirect to Cognito. However, Cognito still has a valid session/cookie for the user that is hard-coded to expire after 1 hour. This causes Cognito to immediately redirect the user back to my app with a new, valid token. I was hoping they would have to re-authenticate. Since this cookie is not configurable. The only way I see to remove/expire it is to redirect the browser to the Cognito logout page.
This leads to my ugly solution… It works, mind you, but I really don’t like it! What I did was configure NextAuth such that when the user wants to log in, it first redirects them to the Cognito Logout URL with a callback URL to my app’s login URL which sends then sends them back to Cognito (dizzy yet?). Consider this example:
- First, assume user is already authenticated and is on my site.
- User closes the browser
- Five minutes later, the user reopens their browser
- NextAuth detects that the user is not authenticated (JWT has expired) and redirects the user to the Cognito Logout URL
- Cognito logs the user out and redirects them back to my site. Note that the user does not actually see Cognito here because all of the HTTP responses are redirects.
- My app then redirects the user back to Cognito’s normal login URL.
- The user sees the Cognito login page prompting them for their credentials.
- Upon successful authentication, the user lands back on my site with a new JWT.
Wow, yuck! …but it works and isn’t a bad UX. Please tell me there is a better way! My own thoughts are:
- Accept the 1-hour expiration time on Cognito (not great security wise)
- Don’t use Cognito (too late for me)