Skip to content

Commit

Permalink
Session Management Bugfix (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
atrincas authored Dec 31, 2024
1 parent c51cd5b commit f757cfe
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 3 deletions.
6 changes: 5 additions & 1 deletion client/src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SessionProvider } from "next-auth/react";

import { makeQueryClient } from "@/lib/query-client";

import SessionChecker from "@/components/session-checker";
import { TooltipProvider } from "@/components/ui/tooltip";

let browserQueryClient: QueryClient | undefined = undefined;
Expand Down Expand Up @@ -44,7 +45,10 @@ export default function LayoutProviders({
<TooltipProvider>
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<JotaiProvider store={appStore}>{children}</JotaiProvider>
<JotaiProvider store={appStore}>
<SessionChecker />
{children}
</JotaiProvider>
</TooltipProvider>
</QueryClientProvider>
</TooltipProvider>
Expand Down
53 changes: 53 additions & 0 deletions client/src/components/session-checker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useEffect, useMemo } from "react";

import { usePathname } from "next/navigation";

import { TOKEN_TYPE_ENUM } from "@shared/schemas/auth/token-type.schema";
import { signOut, useSession } from "next-auth/react";

import { client } from "@/lib/query-client";
import { queryKeys } from "@/lib/query-keys";
import { isPrivatePath } from "@/lib/utils";

/**
* This should be a temporary solution to check if the access token is expired.
* next-auth currently does not support server-side signout.
* More info https://github.com/nextauthjs/next-auth/discussions/5334
* TODO: A better solution would be to use a middleware to check if the access token is expired!
*/
export default function SessionChecker() {
const { data: session } = useSession();
const queryKey = queryKeys.auth.validateToken(session?.accessToken).queryKey;

const pathname = usePathname();
const queryEnabled = useMemo(
() => isPrivatePath(pathname) && !!session?.accessToken,
[session?.accessToken, pathname],
);
const { error } = client.auth.validateToken.useQuery(
queryKey,
{
query: {
tokenType: TOKEN_TYPE_ENUM.ACCESS,
},
headers: {
authorization: `Bearer ${session?.accessToken}`,
},
},
{
queryKey,
enabled: queryEnabled,
},
);

useEffect(() => {
if (error && queryEnabled) {
signOut({
redirect: true,
callbackUrl: queryEnabled ? "/auth/signin" : undefined,
});
}
}, [error, pathname, queryEnabled]);

return null;
}
1 change: 1 addition & 0 deletions client/src/lib/query-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { filtersSchema } from "@/app/(overview)/url-store";
import { TABLE_VIEWS } from "@/containers/overview/table/toolbar/table-selector";

export const authKeys = createQueryKeys("auth", {
validateToken: (token?: string) => ["validate-token", token],
resetPasswordToken: (token: string) => ["reset-password-token", token],
confirmEmailToken: (token: string) => ["confirm-email-token", token],
});
Expand Down
5 changes: 5 additions & 0 deletions client/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,8 @@ class IntroModalManager {
}
}
export const introModalManager = IntroModalManager.getInstance();

const PRIVATE_PAGES = /^(\/profile|\/my-projects)/;
export const isPrivatePath = (pathname: string) => {
return PRIVATE_PAGES.test(pathname);
};
4 changes: 2 additions & 2 deletions client/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { NextResponse } from "next/server";

import { NextRequestWithAuth, withAuth } from "next-auth/middleware";

const PRIVATE_PAGES = /^(\/profile|\/my-projects)/;
import { isPrivatePath } from "@/lib/utils";

export default function middleware(req: NextRequestWithAuth) {
if (PRIVATE_PAGES.test(req.nextUrl.pathname)) {
if (isPrivatePath(req.nextUrl.pathname)) {
return withAuth(req, {
pages: {
signIn: "/auth/signin",
Expand Down

0 comments on commit f757cfe

Please sign in to comment.