2025-12-19 11:08:19 +01:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useEffect } from "react";
|
|
|
|
|
import { useRouter } from "next/navigation";
|
|
|
|
|
import { useAuth, PermissionType, Permission } from "../auth-context";
|
|
|
|
|
|
|
|
|
|
interface UseRequireAuthOptions {
|
|
|
|
|
/** Required permission to access the page */
|
|
|
|
|
requiredPermission?: PermissionType;
|
|
|
|
|
/** Required role to access the page */
|
|
|
|
|
requiredRole?: string;
|
|
|
|
|
/** Where to redirect if permission check fails (defaults to best available page) */
|
|
|
|
|
fallbackRedirect?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface UseRequireAuthResult {
|
|
|
|
|
user: ReturnType<typeof useAuth>["user"];
|
|
|
|
|
isLoading: boolean;
|
|
|
|
|
isAuthorized: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Hook that handles authentication and authorization checks.
|
|
|
|
|
* Automatically redirects to login if not authenticated,
|
|
|
|
|
* or to a fallback page if missing required permissions.
|
|
|
|
|
*/
|
|
|
|
|
export function useRequireAuth(options: UseRequireAuthOptions = {}): UseRequireAuthResult {
|
|
|
|
|
const { requiredPermission, requiredRole, fallbackRedirect } = options;
|
|
|
|
|
const { user, isLoading, hasPermission, hasRole } = useAuth();
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
|
|
const isAuthorized = (() => {
|
|
|
|
|
if (!user) return false;
|
|
|
|
|
if (requiredPermission && !hasPermission(requiredPermission)) return false;
|
|
|
|
|
if (requiredRole && !hasRole(requiredRole)) return false;
|
|
|
|
|
return true;
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (isLoading) return;
|
|
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
|
router.push("/login");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isAuthorized) {
|
|
|
|
|
// Redirect to the most appropriate page based on permissions
|
2025-12-25 19:04:45 +01:00
|
|
|
// Use hasPermission/hasRole directly since they're stable callbacks
|
2025-12-21 21:59:26 +01:00
|
|
|
const redirect =
|
|
|
|
|
fallbackRedirect ??
|
2025-12-22 20:18:33 +01:00
|
|
|
(hasPermission(Permission.VIEW_ALL_EXCHANGES)
|
|
|
|
|
? "/admin/trades"
|
|
|
|
|
: hasPermission(Permission.CREATE_EXCHANGE)
|
|
|
|
|
? "/exchange"
|
2025-12-21 21:59:26 +01:00
|
|
|
: "/login");
|
2025-12-19 11:08:19 +01:00
|
|
|
router.push(redirect);
|
|
|
|
|
}
|
2025-12-25 19:04:45 +01:00
|
|
|
// Note: hasPermission and hasRole are stable callbacks from useAuth,
|
|
|
|
|
// so they don't need to be in the dependency array. They're only included
|
|
|
|
|
// for clarity and to satisfy exhaustive-deps if needed.
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
}, [isLoading, user, isAuthorized, router, fallbackRedirect]);
|
2025-12-19 11:08:19 +01:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
user,
|
|
|
|
|
isLoading,
|
|
|
|
|
isAuthorized,
|
|
|
|
|
};
|
|
|
|
|
}
|