Ivan Gregor Tabalno
Ivan Gregor Tabalno

Reputation: 73

How to redirect to callback url after sign in using next-auth@beta v5 on next.js 14?

I have a protected route '/new'. Whenever I try to visit this '/new' route, it redirects me to the '/login' route to log in using the Google provider. After I log in, the callback URL is '/new', but instead of redirecting to '/new', it redirects to '/' which is my home route.

I just follow the instruction of next.js learn by adding this file and code:

/api/auth/[...nextauth]/route.ts

export { GET, POST } from '@/auth'

/auth.config.ts

import type { NextAuthConfig } from 'next-auth'
import Google from 'next-auth/providers/google'

export const authConfig = {
  pages: {
    signIn: '/login'
  },
  callbacks: {
    authorized({ auth, request: { nextUrl } }) {
      const isLoggedIn = !!auth?.user
      // const isOnDashboardRoutes = nextUrl.pathname.startsWith('/')
      const isOnDashboard = nextUrl.pathname === '/'
      const isOnNew = nextUrl.pathname.startsWith('/new')
      if (isOnDashboard || isOnNew) {
        if (isLoggedIn) return true
        return false // Redirect unauthenticated users to login page
      } else if (isLoggedIn) {
        return Response.redirect(new URL('/', nextUrl))
      }
      return true
    }
  },
  // Add providers with an empty array for now
  providers: [Google]
} satisfies NextAuthConfig

/auth.ts

import NextAuth from 'next-auth'
import { authConfig } from './auth.config'
import { PrismaAdapter } from '@auth/prisma-adapter'

import prisma from '@/lib/prismadb'

export const {
  handlers: { GET, POST },
  auth,
  signIn,
  signOut
} = NextAuth({
  adapter: PrismaAdapter(prisma),
  session: { strategy: 'jwt' },
  ...authConfig
})

/middleware.ts

import NextAuth from 'next-auth'
import { authConfig } from '@/auth.config'

export default NextAuth(authConfig).auth

export const config = {
  // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
  matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)']
}

/action.ts

export async function login() {
  await signIn('google')
}

Upvotes: 6

Views: 2772

Answers (2)

Boris
Boris

Reputation: 11

callbacks:{
  redirect({ url, baseUrl }) {
  const callbackUrl = url.split('?').pop();
  const searchParams = new 
  URLSearchParams(callbackUrl).get('callbackUrl');
  if (searchParams != null) return searchParams;

  if (url.startsWith('/')) return `${baseUrl}${url}`;
  if (new URL(url).origin === baseUrl) return url;
  return baseUrl;
  }

}

Upvotes: 1

Ahmet Firat Keler
Ahmet Firat Keler

Reputation: 4055

Just to be sure we're on the same page

import { signIn } from "next-auth/react"

After importing the necessary method, let's observe its details signIn

As you see, there's a second parameter; options which is type of SignInOptions

SignInOptions has an option called redirect

Before we dive deeper, first we should set up a next middleware along with next-auth

middleware.ts

import { NextRequest, NextResponse } from "next/server"
import { auth } from "auth"

import { privateRoutes } from "@/contains/contants" // our private routes which can only be accessed by authenticated users

export default auth((request: NextRequest) => {
    // @ts-ignore
    const { auth } = request // if the user is authenticated or not
    const { pathname } = request.nextUrl // the url path

    const searchTerm = request.nextUrl.pathname.split("/").slice(0, 2).join("/") // the part of url after the domain

    if (privateRoutes.includes(searchTerm)) {
        const isLoggedIn = !!auth

        // redirect unauthenticated users to login page when they try to access a private route, please mind the callback url
        if (!isLoggedIn) {
            return NextResponse.redirect(new URL(`/login?callbackUrl=${encodeURIComponent(request.nextUrl.href)}`, request.nextUrl))
        }
    }
}

export const config = {
    matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
}

Now, let us come back to our login process and finish the job. To do that, we will take control of redirect process using the second parameter I mentioned earlier

"use client"

import { useSearchParams, useRouter } from "next/navigation"

import type { SignInResponse } from "next-auth/lib/client"
import { signIn } from "next-auth/react"

import { Route } from "@/routers/types"

export default function LoginForm() {
    const router = useRouter()
    const searchParams = useSearchParams()

    async function logIn() {
        const callbackUrl = searchParams.get("callbackUrl") // this is what we set up in our middleware
        signIn("google", { redirect: false }) // we will control the redirect process
            .then((res: SignInResponse | undefined) => {
                if (!res) {
                    return
                }
                if (!res.ok)
                    alert("Something went wrong!")
                else if (res.error) {
                    console.log(res.error)

                    if (res.error == "CallbackRouteError")
                        alert("Could not login! Please check your credentials.")
                    else
                        alert(`Internal Server Error: ${res.error}`)
                } else {
                    // THIS IS WHAT YOU ARE AFTER
                    if (callbackUrl)
                        router.push(callbackUrl as Route)
                    else
                        router.push("/home")
                }

            })
    }

    return (
        {/*JSX*/}
    )
}

That's it! When an unauthenticated user tries to access a private route, e.x. http://localhost:3000/home, it will be redirected to http://localhost:3000/login?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2Fhome path. After logging in, the user will be redirected to http://localhost:3000/home path.

Upvotes: 0

Related Questions