In my NextJS, I am using NextAuth. I have sign in/sign up with credentials and Google set up. The problem I am having is of redirect. I have made this middleware:
export { default } from 'next-auth/middleware';
export const config = { matcher: ['/dashboard'] };
Now, I have this page in app/auth/register/page.tsx
:
export default function AuthPage() {
const [isSignIn, setIsSignIn] = useState(true);
const [error, setError] = useState('');
const router = useRouter();
const { data: session, status } = useSession();
useEffect(() => {
if (status === 'authenticated') {
router.push('/dashboard');
}
}, [status, router]);
const handleGoogleSignIn = async () => {
try {
const result = await signIn('google', {
callbackUrl: '/dashboard',
});
if (result?.error) {
setError('Failed to sign in with Google');
}
} catch (err) {
console.error(err);
setError('Something went wrong with Google sign in');
}
};
const handleSignIn = async (email: string, password: string) => {
setError('');
try {
const result = await signIn(undefined, {
email,
password,
callbackUrl: 'http://localhost:3000/dashboard',
});
if (result?.error) {
setError('Invalid email or password');
} else {
router.push('/dashboard');
}
} catch (err) {
console.log(err);
setError('Something went wrong');
}
};
const handleSignUp = async (
email: string,
password: string,
firstName: string,
lastName: string
) => {
setError('');
try {
const res = await fetch('/api/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, firstName, lastName }),
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || 'Something went wrong');
}
// Auto sign in after successful sign up
const result = await signIn('credentials', {
email,
password,
callbackUrl: 'http://localhost:3000/dashboard',
});
if (result?.error) {
setError('Failed to sign in after sign up');
} else {
router.push('/dashboard');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Something went wrong');
}
};
return (
<div className="flex-1 flex flex-col md:flex-row">
<Link href="/" className="absolute top-6 left-6">
<Image
src="/images/orbit-logo.png"
alt="Logo"
width={150}
height={150}
style={{ filter: 'invert(1) grayscale(1) contrast(100%)' }}
/>
</Link>
<div className="flex-1 md:w-1/2 bg-[#f3f3f5] flex flex-col justify-center items-center p-6 md:p-8">
<div className="max-w-md">
<h2 className="text-4xl md:text-5xl font-bold text-[#171717] mb-6">
{isSignIn ? 'Welcome back' : 'Join Orbit today'}
</h2>
<p className="text-[#454545] text-lg mb-8">
{isSignIn
? 'Sign in to access your dashboard and continue your journey.'
: 'Create an account and start exploring the universe of possibilities with Orbit.'}
</p>
<div className="hidden md:block">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="bg-white p-6 rounded-xl shadow-lg"
>
<div className="flex items-start gap-4">
<div className="bg-[#fed835] p-2 rounded-full">
<Orbit className="h-5 w-5 text-white" />
</div>
<div>
<h3 className="font-medium text-[#171717]">
Seamless Integration
</h3>
<p className="text-[#454545] text-sm">
Connect with your favorite tools and services effortlessly.
</p>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className="bg-white p-6 rounded-xl shadow-lg mt-4"
>
<div className="flex items-start gap-4">
<div className="bg-[#304fff] p-2 rounded-full">
<Orbit className="h-5 w-5 text-white" />
</div>
<div>
<h3 className="font-medium text-[#171717]">
Powerful Analytics
</h3>
<p className="text-[#454545] text-sm">
Gain insights with our comprehensive analytics dashboard.
</p>
</div>
</div>
</motion.div>
</div>
</div>
</div>
{/* Right side - Form */}
<div className="w-full md:w-1/2 flex justify-center items-center p-6 md:p-8 bg-white">
<div className="w-full max-w-md">
<div className="flex justify-center mb-6">
<div className="inline-flex p-1 bg-[#f3f3f5] rounded-lg">
<button
onClick={() => setIsSignIn(true)}
className={`px-4 py-2 text-sm font-medium rounded-md transition-all duration-300 ${
isSignIn
? 'bg-[#fed835] text-[#171717]'
: 'bg-transparent text-[#454545] hover:text-[#171717]'
}`}
>
Sign In
</button>
<button
onClick={() => setIsSignIn(false)}
className={`px-4 py-2 text-sm font-medium rounded-md transition-all duration-300 ${
!isSignIn
? 'bg-[#fed835] text-[#171717]'
: 'bg-transparent text-[#454545] hover:text-[#171717]'
}`}
>
Sign Up
</button>
</div>
</div>
{error && (
<div className="bg-red-50 text-red-500 p-3 rounded-md text-sm mb-4">
{error}
</div>
)}
<AnimatePresence mode="wait">
<motion.div
key={isSignIn ? 'signin' : 'signup'}
initial={{ opacity: 0, x: isSignIn ? -20 : 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: isSignIn ? 20 : -20 }}
transition={{ duration: 0.3 }}
>
{isSignIn ? (
<SignInForm
onSubmit={handleSignIn}
onGoogleSignIn={handleGoogleSignIn}
/>
) : (
<SignUpForm
onSubmit={handleSignUp}
onGoogleSignIn={handleGoogleSignIn}
/>
)}
</motion.div>
</AnimatePresence>
</div>
</div>
</div>
);
}
The behaviour that I want is that it redirects me to /dashboard page however, what's happening is something like this:
Sign In with Google
http://localhost:3000/auth/register?callbackUrl=http://localhost:3000/dashboard
from http://localhost:3000/auth/register
I cannot figure out what I am doing wrong and what's causing this issue. Any help would be greatly appreciated.
await signIn('google', {
callbackUrl: '/dashboard',
});
NextAuth redirects you to the Google consent screen, then back to the current page with a ?callbackUrl= query string
if useSession()
on that page doesn't re-trigger a redirect after auth, you're just stuck there.
Instead of manually redirecting with router.push after checking session, just use the callbackUrl. Update your Google sign-in handler
await signIn('google', { callbackUrl: '/dashboard' });
in middleware.ts, handle redirect for already-authenticated users if they visit /auth/register
// middleware.ts
import { getToken } from 'next-auth/jwt';
import { NextResponse } from 'next/server';
export async function middleware(req) {
const token = await getToken({ req });
const { pathname } = req.nextUrl;
// Redirect alredy logged-in users from auth pages
if (token && pathname.startsWith('/auth')) {
return NextResponse.redirect(new URL('/dashboard', req.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard', '/auth/:path*'], // apply the middleware for register path
};