I'm trying to build an authentication feature for my web app following this online tutorial.
I created the middleware.ts for my web app, and saw that there is a const isLoggedIn
so i thought i can use this boolean in every other pages to constantly authenticate if user is logged in.
so i abstracted it out like this
export const checkIsLoggedIn = (req: any): boolean => {
return !!req.auth;
};
since this is the middleware which is server action, and my components are all client actions, i decided to create an api such that checkisloggedin will be passed from middleware.ts -> check-auth.ts (the api) -> main-nav.tsx (the component i want to use to check if user is logged in).
but when i console.log to see if my api is working, my browser's inspect element console returned this error
[main-nav-tsx][checkAuthStatus]: Error fetching auth status
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
window.console.error @ app-index.js:33
Apparently it meant that my api route wasnt found.
The issue: Im not sure how else i can pass checkisloggedin boolean to my components as every method i tried to check if user is logged in isn't working. all my directories seemed correct too.
Expected outcome: I just need a way to check if user is logged in, and show the signout button as needed.
Im also open to new solutions involving sessions and tokens, but i have no idea how to use any of them.
These are my codes. Since NextJS is particular about file directories, I commented where each file is stored at, at the very top.
// src/middleware.ts
// import { auth } from "./auth"
import authConfig from "./auth.config"
import NextAuth from "next-auth"
import {
DEFAULT_LOGIN_REDIRECT,
apiAuthPrefix,
authRoutes,
publicRoutes,
} from "@/routes"
import { NextResponse } from "next/server";
const { auth } = NextAuth(authConfig);
export const checkIsLoggedIn = (req: any): boolean => {
return !!req.auth;
};
export default auth((req) => {
const { nextUrl } = req;
const isLoggedIn = !!req.auth;
console.log('[middleware-ts][checkisloggedin]: ', checkIsLoggedIn(req));
console.log('[middleware-ts][isloggedin]: ', isLoggedIn);
const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
const isPublicRoute = publicRoutes.includes(nextUrl.pathname);
const isAuthRoute = authRoutes.includes(nextUrl.pathname);
if (isApiAuthRoute) {
return NextResponse.next();
}
if (isAuthRoute) {
if (isLoggedIn) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, nextUrl))
}
return NextResponse.next();
}
if (!isLoggedIn && !isPublicRoute) {
return NextResponse.redirect(new URL("/auth/login", nextUrl));
}
return NextResponse.next();
})
export const config = {
// matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
// matcher: ["/auth/login", "/auth/register"],
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
}
// src/components/main-nav.tsx
"use client"
import * as React from "react"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { docsConfig } from "@/config/docs"
import { siteConfig } from "@/config/site"
import { cn } from "@/lib/utils"
import { Icons } from "@/components/icons"
import { signOut } from "@/auth"
export function MainNav() {
const pathname = usePathname()
const [isLoggedIn, setIsLoggedIn] = React.useState(false);
React.useEffect(() => {
const checkAuthStatus = async () => {
console.log('[main-nav-tsx][checkAuthStatus]: Checking auth status...');
try {
const response = await fetch('/app/api/authentication/check-auth');
if (response.ok) {
const data = await response.json();
console.log('[main-nav-tsx][checkAuthStatus]: Auth status response:', data);
setIsLoggedIn(data.isLoggedIn);
} else {
console.error('[main-nav-tsx][checkAuthStatus]: Error fetching auth status', response.status);
}
} catch (error) {
console.error('[main-nav-tsx][checkAuthStatus]: Error fetching auth status', error);
}
}
checkAuthStatus();
}, []);
/**
* handleSignOut is a server side action to check for log in
* main-nav is a client side action
* even though the method is post, we cannot use server action for checking if logged in is true or false
* hence a POST api is needed
*/
const handleSignOut = async () => {
const response = await fetch('/app/api/authentication/signout', {
method: 'POST',
});
if (response.ok) {
// Handle successful sign-out, e.g., redirect or update state
setIsLoggedIn(false);
} else {
// Handle error
console.error('Error signing out');
}
};
return (
<div className="mr-4 hidden md:flex">
<Link href="/" className="mr-6 flex items-center space-x-2">
<Icons.logo className="h-6 w-6" />
<span className="hidden font-bold sm:inline-block">
{siteConfig.name}
</span>
</Link>
<nav className="flex items-center gap-4 text-sm lg:gap-6">
{docsConfig.mainNav?.map(item => (
item.href && (
<Link
key={item.href}
href={item.href}
className={cn(
"transition-colors hover:text-foreground/80",
pathname === item.href ? "text-foreground" : "text-foreground/60"
)}
>
{item.title}
</Link>
)
))}
{/* change this button else where */}
{isLoggedIn && (
<button onClick={handleSignOut}>Sign Out</button>
)}
</nav>
</div>
)
}
// src/app/api/authentication/check-auth.ts
import { NextApiRequest, NextApiResponse } from 'next'
import { checkIsLoggedIn } from '../../../middleware'
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const isLoggedIn = checkIsLoggedIn(req);
// console.log("[check-auth-ts]: ", isLoggedIn)
res.status(200).json({ isLoggedIn });
}