Skip to content
File

Blob: src/client/hooks/use-auth-guard.ts

typescript45 lines
1import { useEffect } from "react";
2import { useNavigate } from "react-router";
3 
4import { type SessionState, useSession } from "@/client/lib/auth-client";
5import { signInPath } from "@/client/lib/navigation";
6import { hasAdminRole } from "@/shared/role";
7 
8export const useRequireSession = (redirectTo: string): SessionState => {
9 const navigate = useNavigate();
10 const session = useSession();
11 const userId = session.data?.user.id ?? null;
12 
13 useEffect(() => {
14 if (!session.isPending && !userId) {
15 navigate(signInPath(redirectTo), { replace: true });
16 }
17 }, [session.isPending, userId, navigate, redirectTo]);
18 
19 return session;
20};
21 
22export const useRequireAdmin = (redirectTo: string): SessionState => {
23 const navigate = useNavigate();
24 const session = useSession();
25 const userId = session.data?.user.id ?? null;
26 const role = session.data?.user.role ?? null;
27 
28 useEffect(() => {
29 if (session.isPending) return;
30 // Distinguish unauthenticated from authenticated-but-non-admin so a
31 // signed-out admin deep link round-trips through /sign-in?redirect=
32 // and lands back on the original admin URL after sign-in. A blanket
33 // /account redirect would lose the destination.
34 if (!userId) {
35 navigate(signInPath(redirectTo), { replace: true });
36 return;
37 }
38 if (!hasAdminRole(role)) {
39 navigate("/account", { replace: true });
40 }
41 }, [session.isPending, userId, role, navigate, redirectTo]);
42 
43 return session;
44};