Skip to content

Commit 0a83f3e

Browse files
committed
fix: token mechanism
1 parent 46b7f5f commit 0a83f3e

File tree

7 files changed

+59
-135
lines changed

7 files changed

+59
-135
lines changed

front-end/src/app/(home)/support/page.tsx

Lines changed: 0 additions & 73 deletions
This file was deleted.

front-end/src/app/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ export default async function RootLayout({
3939
children: React.ReactNode;
4040
}>) {
4141
const token = await authStorage.getAccessToken();
42+
const refreshToken = await authStorage.getRefreshToken();
4243

4344
return (
4445
<html lang="en">
4546
<body
4647
className={`flex min-h-screen w-full flex-col ${geistSans.variable} ${geistMono.variable}`}
4748
>
4849
<Toaster richColors />
49-
<Providers hasAccessToken={Boolean(token)}>
50+
<Providers initialHasToken={Boolean(token) || Boolean(refreshToken)}>
5051
<main className={styles.page}>
5152
<div className={styles.container}>
5253
<Navbar />

front-end/src/app/not-found.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { redirect } from "next/navigation";
2+
import { routes } from "@/data/routes";
3+
4+
export default function NotFound() {
5+
redirect(routes.home);
6+
}

front-end/src/app/providers.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
77
import { NuqsAdapter } from "nuqs/adapters/next/app";
88

99
export default function Providers({
10-
hasAccessToken,
10+
initialHasToken,
1111
children,
1212
}: {
13-
hasAccessToken: boolean;
13+
initialHasToken: boolean;
1414
children: React.ReactNode;
1515
}) {
1616
const [queryClient] = useState(
@@ -40,7 +40,7 @@ export default function Providers({
4040
return (
4141
<NuqsAdapter>
4242
<QueryClientProvider client={queryClient}>
43-
<AuthProvider hasAccessToken={hasAccessToken}>
43+
<AuthProvider initialHasToken={initialHasToken}>
4444
<TooltipProvider>{children}</TooltipProvider>
4545
</AuthProvider>
4646
</QueryClientProvider>

front-end/src/components/Navbar/navbar.tsx

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ import UserMenu from "./UserMenu";
1515
import Logo from "./Logo";
1616
import type { NavbarProps, NavbarNavItem } from "./types";
1717
import Link from "next/link";
18-
import {
19-
DropdownMenu,
20-
DropdownMenuTrigger,
21-
} from "../ui/dropdown-menu";
18+
import { DropdownMenu, DropdownMenuTrigger } from "../ui/dropdown-menu";
2219

2320
const defaultLinks: NavbarNavItem[] = [
2421
{ href: routes.home, label: "Home" },
@@ -42,7 +39,7 @@ const Navbar = React.forwardRef<HTMLElement, NavbarProps>(
4239
},
4340
ref
4441
) => {
45-
const { isAuthenticated } = useAuth();
42+
const { isAuthenticated, isLoading } = useAuth();
4643
const pathname = usePathname();
4744
const [isMobile, setIsMobile] = useState(false);
4845
const [openMobile, setOpenMobile] = useState(false);
@@ -121,29 +118,30 @@ const Navbar = React.forwardRef<HTMLElement, NavbarProps>(
121118
)}
122119
</div>
123120

124-
{isAuthenticated ? (
125-
<UserMenu />
126-
) : (
127-
<div className="flex gap-2">
128-
<Button
129-
variant="ghost"
130-
size="sm"
131-
onClick={onSignInClick}
132-
asChild={!onSignInClick}
133-
>
134-
<Link href={signInHref}>{signInText}</Link>
135-
</Button>
136-
137-
<Button
138-
size="sm"
139-
className="shadow-sm"
140-
onClick={onCtaClick}
141-
asChild={!onCtaClick}
142-
>
143-
<Link href={ctaHref}>{ctaText}</Link>
144-
</Button>
145-
</div>
146-
)}
121+
{!isLoading &&
122+
(isAuthenticated ? (
123+
<UserMenu />
124+
) : (
125+
<div className="flex gap-2">
126+
<Button
127+
variant="ghost"
128+
size="sm"
129+
onClick={onSignInClick}
130+
asChild={!onSignInClick}
131+
>
132+
<Link href={signInHref}>{signInText}</Link>
133+
</Button>
134+
135+
<Button
136+
size="sm"
137+
className="shadow-sm"
138+
onClick={onCtaClick}
139+
asChild={!onCtaClick}
140+
>
141+
<Link href={ctaHref}>{ctaText}</Link>
142+
</Button>
143+
</div>
144+
))}
147145
</div>
148146
</header>
149147
);

front-end/src/context/AuthContext.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import { createContext, useContext, useMemo } from "react";
24
import { IUser } from "@/types/user.types";
35
import { useQuery } from "@tanstack/react-query";
@@ -8,28 +10,29 @@ interface AuthContextType {
810
isLoading: boolean;
911
isAuthenticated: boolean;
1012
error?: unknown;
13+
refetchUser: () => void;
1114
}
1215

1316
const AuthContext = createContext<AuthContextType | undefined>(undefined);
1417

1518
export const AuthProvider = ({
16-
hasAccessToken,
19+
initialHasToken,
1720
children,
1821
}: {
19-
hasAccessToken: boolean;
22+
initialHasToken: boolean;
2023
children: React.ReactNode;
2124
}) => {
2225
const {
2326
data: user,
2427
isLoading,
2528
error,
29+
refetch,
2630
} = useQuery<unknown, unknown, IUser>({
2731
queryKey: ["api/user"],
2832
queryFn: getUserInfo,
29-
enabled: hasAccessToken,
30-
31-
staleTime: Infinity,
32-
gcTime: Infinity,
33+
enabled: initialHasToken,
34+
staleTime: 5 * 60 * 1000,
35+
gcTime: 10 * 60 * 1000,
3336
refetchOnMount: false,
3437
refetchOnWindowFocus: false,
3538
refetchOnReconnect: false,
@@ -38,11 +41,12 @@ export const AuthProvider = ({
3841
const value = useMemo(
3942
() => ({
4043
user,
41-
isAuthenticated: Boolean(hasAccessToken),
42-
isLoading,
44+
isAuthenticated: Boolean(user),
45+
isLoading: initialHasToken && isLoading,
4346
error,
47+
refetchUser: () => refetch(),
4448
}),
45-
[user, hasAccessToken, isLoading, error]
49+
[user, initialHasToken, isLoading, error, refetch]
4650
);
4751

4852
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;

front-end/src/middleware.ts

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { NextResponse } from "next/server";
22
import type { NextRequest } from "next/server";
33
import { routeConfig, isProtectedRoute, isAuthRoute } from "./data/routes";
4-
import { decodeToken } from "./utils/userUtils";
5-
import { authStorage } from "./api-actions/authStorage";
64

75
export async function middleware(request: NextRequest) {
86
const path = request.nextUrl.pathname;
@@ -14,32 +12,22 @@ export async function middleware(request: NextRequest) {
1412
return NextResponse.next();
1513
}
1614

17-
const token = await authStorage.getAccessToken();
18-
let userId: string | undefined;
19-
20-
if (token) {
21-
try {
22-
const decoded = decodeToken(token);
23-
userId = decoded.userId;
24-
} catch (error) {
25-
const response = NextResponse.redirect(
26-
new URL(routeConfig.protected.redirectTo, request.nextUrl)
27-
);
28-
response.cookies.delete("access_token");
29-
response.cookies.delete("refresh_token");
30-
return response;
31-
}
32-
}
15+
const accessToken = request.cookies.get("access_token")?.value;
16+
const refreshToken = request.cookies.get("refresh_token")?.value;
3317

34-
const isAuthenticated = Boolean(userId);
18+
const initialHasToken = Boolean(refreshToken || accessToken);
3519

36-
if (isProtected && !isAuthenticated) {
20+
if (isProtected && !initialHasToken) {
3721
const loginUrl = new URL(routeConfig.protected.redirectTo, request.nextUrl);
3822
loginUrl.searchParams.set("from", path);
3923
return NextResponse.redirect(loginUrl);
4024
}
4125

42-
if (isAuth && isAuthenticated) {
26+
if (isProtected && refreshToken && !accessToken) {
27+
return NextResponse.next();
28+
}
29+
30+
if (isAuth && initialHasToken) {
4331
const from = request.nextUrl.searchParams.get("from");
4432
const redirectUrl =
4533
from && !isAuthRoute(from) ? from : routeConfig.auth.redirectTo;

0 commit comments

Comments
 (0)