DEV Community

Abdullah Al Fahad
Abdullah Al Fahad

Posted on

Implementing User Authentication in Next.js 14 using AuthJs Credentials (REST API)

Let’s talk step by step about how to implement Next.js 14’s user authentication with Auth.js (credentials) and REST API.

To start, begin by creating a Next.js application.

npx create-next-app@latest

Then, integrate the Auth.js library into your application.

npm install next-auth@beta
Next, you will need to add a AUTH_SECRET and NEXTAUTH_URL to your env file. Although during development you could leave AUTH_SECRET it empty, during production it would raise an error.

Now, proceed to create the auth.ts file within your /src directory. This file will handle authentication by interfacing with a REST API, specifically for login functionality. Depending on your API structure, the response may vary, but you can reference the example API response provided here to guide your implementation.

import type {NextAuthConfig} from "next-auth";
import NextAuth from "next-auth";

import CredentialsProvider from "next-auth/providers/credentials";
import $axios from "@/lib/Axios";

async function login(credentials: any) {
    try {
        return await $axios.post("your-login-api", credentials).then((res: any) => {
            const {user} = res;
            return {
                name: user.name,
                email: user.email,
                image: user.profile_photo,
                accessToken: res.access_token,
                // If you need any other information you can add here...
            };
        });
    } catch (e) {
        throw new Error("Something went wrong.");
    }
}

export const config: NextAuthConfig = {
    pages: {signIn: "/login"},
    providers: [
        CredentialsProvider({
            name: "Credentials",
            credentials: {
                email: {label: "Email", type: "email", placeholder: "example@email.com"},
                password: {label: "Password", type: "password", placeholder: "******"}
            },
            async authorize(credentials,) {
                try {
                    return login(credentials);
                } catch (e) {
                    return {};
                }
            },
        }),
    ],
    callbacks: {
        async jwt({user, token}) {
            if (user) {
                token.user = user;
            }
            return token;
        },
        async session({session, token}: any) {
            session.user = token.user;
            return session;
        },
    },
    debug: process.env.NODE_ENV === "development",
};

export const {handlers, auth, signIn, signOut} = NextAuth(config);
Enter fullscreen mode Exit fullscreen mode

The jwt() callback is called user when the user first logs in. The user object will be populated with the object that is returned from the authorize function. The object that is returned from the jwt callback is what will be saved on the session cookie.

The session() callback receives the session cookie content in its token parameter. Whatever is returned from this callback is what will be presented when useSession or getServerSession is called.

Next, establish a api directory within either your /src/app or /app directory. Inside this api directory, create a route.ts file within the api/auth/[...nextauth] directory structure.

import {handlers} from "@/auth";

export const {GET, POST} = handlers;
Then create another route.ts file inside api/protected directory.

import {auth} from "@/auth";

export const GET = auth((req) => {
    if (req.auth) {
        return Response.json({data: "Protected data"});
    }

    return Response.json({message: "Not authenticated"}, {status: 401});
}) as any;
Enter fullscreen mode Exit fullscreen mode

In your layout.tsx file, you'll utilize the SessionProvider to manage session data. This component enables your application to interact with the user authentication state across various components. You can use it to display different content or UI elements based on whether a user is authenticated or not.

"use client";
import React from "react";
import {SessionProvider} from "next-auth/react";
...

export default function RootLayout({children}: { children: React.ReactNode }) {
    return (
        <SessionProvider>
            <html lang="en">
            <body className="pintoe">
                {children}
            </body>
            </html>
        </SessionProvider>
    );
}
Enter fullscreen mode Exit fullscreen mode

Well done, your Auth.js setup is complete. Now you can use your session data on every page (route). ex: app/pages.tsx

"use client";

import {useSession} from "next-auth/react";
import Image from "next/image";

export default function HomePage() {
    const {data: session} = useSession();

    return (
        <div>
            <h2>{session?.user.name}</h2>

            <Image src={session?.user.image} alt="User Avatar" width={200} height={200}/>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Now you can use a higher order component to protect authenticated routes. On my side, I create a AuthGuard.tsx file in src/ components directory.

"use client";
import React, {useEffect, useState} from "react";
import {useSession} from "next-auth/react";
import {usePathname, useRouter} from "next/navigation";

export default function AuthGuard({children}: { children: React.ReactNode }) {
    const {data: status}: any = useSession();
    const router = useRouter();
    const pathname = usePathname();
    const [loading, setLoading] = useState<boolean>(true);

    const guestRoutes: string[] = [
        "/login",
        "/register"
    ];

    useEffect(() => {
        const isGuestRoute = !!guestRoutes.find(route => route === pathname);
        setLoading(true);

        if (status === "loading") {
            return;
        } else if (status === "authenticated" && isGuestRoute) {
            router.push("/");
        } else if (status === "unauthenticated" && !isGuestRoute) {
            router.push("/login");
        } else {
            setLoading(false);
        }
    }, [pathname, status]);

    return (loading ?
        <div>
            Loading...
        </div> : children);
};
Enter fullscreen mode Exit fullscreen mode

Use the AuthGuard component in your layout.tsx file.

"use client";
import "@/assets/styles/tailwind.css";
import React from "react";
import {SessionProvider} from "next-auth/react";
import AuthGuard from "@/lib/AuthGurad";

export default function RootLayout({children}: { children: React.ReactNode }) {
    return (
        <SessionProvider>
            <html lang="en">
            <body className="pintoe">
            <AuthGuard>
                {children}
            </AuthGuard>
            </body>
            </html>
        </SessionProvider>
    );
}
Enter fullscreen mode Exit fullscreen mode

Now the AuthGuard component will automatically handle guest/auth route redirects based on login session data.

Next, we will discuss step by step how to implement redux-toolkit in Next.js 14 and how to configure Axios to get access-token from the auth session.

Top comments (1)