From 600c8e763578f4a5771a9ca54407f7c0ecd6d45f Mon Sep 17 00:00:00 2001 From: Nithin Shekar Kuruba Date: Sat, 25 Nov 2023 20:59:44 -0800 Subject: [PATCH] feat: refactored to logout user when refresh token expires --- app/layout/Layout.tsx | 24 +++++++++++-------- app/pages/api/auth/[...nextauth].ts | 37 +++++++++++------------------ 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/app/layout/Layout.tsx b/app/layout/Layout.tsx index 7b35bd7..35eb69e 100644 --- a/app/layout/Layout.tsx +++ b/app/layout/Layout.tsx @@ -4,13 +4,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCommentDots, faEnvelope, faFileAlt } from '@fortawesome/free-solid-svg-icons'; import Button from '@button-inc/bcgov-theme/Button'; import Footer from '@button-inc/bcgov-theme/Footer'; -import StyledLink from '@button-inc/bcgov-theme/Link'; import styled from 'styled-components'; -import { startCase } from 'lodash'; import BCSans from './BCSans'; import Navigation from './Navigation'; import BottomAlertProvider from './BottomAlert'; -import { useSession } from 'next-auth/react'; +import { getSession, useSession } from 'next-auth/react'; import { useEffect } from 'react'; import { User } from 'next-auth'; @@ -164,15 +162,21 @@ function Layout({ children, onLoginClick, onLogoutClick }: any) { const currentUser: Partial = session?.data?.user!; const pathname = router.pathname; - useEffect(() => { - // logout user when both access and refresh tokens expire - const interval = setInterval(() => { - if (Date.now() > session?.data?.refreshTokenExpired && Date.now() > session?.data?.accessTokenExpired) { + const checkSession = async () => { + if (Date.now() > session?.data?.accessTokenExpiry) { + const session: any = await getSession(); + if (session?.error === 'RefreshAccessTokenError') { onLogoutClick(); } - }, 1000 * 5); - return () => clearInterval(interval); - }, [session]); + } + }; + + useEffect(() => { + if (session?.status === 'authenticated') { + const interval = setInterval(checkSession, 1000 * 1); + return () => clearInterval(interval); + } + }); const rightSide = currentUser ? ( diff --git a/app/pages/api/auth/[...nextauth].ts b/app/pages/api/auth/[...nextauth].ts index a185919..213b4f6 100644 --- a/app/pages/api/auth/[...nextauth].ts +++ b/app/pages/api/auth/[...nextauth].ts @@ -22,12 +22,11 @@ async function refreshAccessToken(token: any) { }, ); const refreshedTokens = await response.data; + return { ...token, accessToken: refreshedTokens.access_token, - accessTokenExpired: Date.now() + (refreshedTokens.expires_in - 15) * 1000, - refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, - refreshTokenExpired: Date.now() + (refreshedTokens.refresh_expires_in - 15) * 1000, + refreshToken: refreshedTokens.refresh_token, }; } catch (error) { console.error('refresh token error', error); @@ -55,39 +54,31 @@ export const authOptions: NextAuthOptions = { }, }), ], + session: { + strategy: 'jwt', + maxAge: 10 * 60 * 60, //10 hours, same as sso/client session max + }, secret: process.env.JWT_SECRET, callbacks: { async jwt({ token, account, user }: { token: any; account: any; user: any }) { if (account) { - token.accessToken = account?.access_token; - token.refreshToken = account.refresh_token; - token.accessTokenExpired = Date.now() + (account?.expires_at - 15) * 1000; - token.refreshTokenExpired = Date.now() + (account?.refresh_expires_in - 15) * 1000; token.user = user; + token.accessToken = account.access_token; + token.refreshToken = account.refresh_token; } - - const decodedToken = jwt.decode(token.accessToken || '') as any; - - token.client_roles = decodedToken?.client_roles; - token.given_name = decodedToken?.given_name; - token.family_name = decodedToken?.family_name; - token.preferred_username = decodedToken?.preferred_username; - token.email = decodedToken?.email; - token.idir_username = decodedToken?.idir_username; - - if (Date.now() < token.accessTokenExpired) { - return refreshAccessToken(token); + const decodedAccessToken = jwt.decode(token.accessToken || '') as any; + if (Date.now() > decodedAccessToken.exp) { + token = await refreshAccessToken(token); } - + token.accessTokenExpiry = decodedAccessToken?.exp; return token; }, async session({ session, token }: { session: any; token: any }) { // Send properties to the client, like an access_token from a provider. if (token) { - session.accessToken = token.accessToken; session.user = token.user; - session.refreshTokenExpired = token.refreshTokenExpired; - session.accessTokenExpired = token.accessTokenExpired; + session.error = token.error || ''; + session.accessTokenExpiry = token.accessTokenExpiry; } return session;