Google Authentication in a Next.js App Using next-auth

Google Authentication in a Next.js App Using next-auth

How to Implement Google Authentication in a Next.js App Using NextAuth

Introduction

User authentication is a pivotal aspect of delivering secure and personalized experiences. Leveraging the power of Next.js and the versatile next-auth library, this article explores a comprehensive approach to implementing Google Authentication in your Next.js applications. We'll dive into the step-by-step process of integrating Google's OAuth services seamlessly, ensuring hassle-free user access while maintaining robust security. This guide will equip you with the knowledge and tools needed to effortlessly integrate Google Authentication into your Next.js projects.

What is next-auth

next-auth is a powerful authentication library that has quickly gained popularity within the Next.js community for its simplicity and versatility. Developed to address the complexities of user authentication, next-auth offers a seamless solution for integrating various authentication providers into your Next.js applications.

At its core, next-auth streamlines the process of setting up authentication workflows, including OAuth, JWT, and session-based approaches. With built-in support for numerous authentication providers like Google, Facebook, GitHub, and more, developers can effortlessly implement social logins and third-party authentications.

The library's modular architecture and intuitive API allow developers to focus on their application's logic while next-auth handles the intricate aspects of authentication, such as token management, session handling, and user identity management. Additionally, next-auth seamlessly integrates with popular databases and data storage solutions, enabling efficient and secure user data storage.

In this article, we'll explore how to leverage next-auth to incorporate Google Authentication into a Next.js app with the help of the MongoDB database.

Pros and cons of using next-auth

Table outlining the pros and cons of using next-auth for authentication in your Next.js applications:

ProsCons
Simplicity: next-auth abstracts much of the complexity involved in authentication setup, making it easy to implement authentication features.Customization Limitations: Although next-auth provides customization options, some advanced customization might require more manual work or modification of its internal code.
Versatility: Supports a wide range of authentication providers, including OAuth, JWT, and more, making it suitable for various use cases.Feature Set: While next-auth offers a robust feature set, it might lack certain advanced features that specialized authentication libraries provide.
Built-in Providers: Comes with pre-built authentication provider configurations, saving time and effort in setting up common authentication methods like Google, Facebook, etc.Dependency on Providers: Some authentication providers might have limitations or changes that impact your app's behavior.
Session Management: Offers flexible session management, including JWT and server-side sessions, allowing you to choose the approach that suits your app's needs.Dependency: Using a third-party library introduces a dependency that might need to be maintained and updated over time.
Modular and Extensible: Its modular architecture enables you to extend or modify its functionality to match your specific requirements.
Active Community: next-auth benefits from an active community, leading to regular updates, bug fixes, and community-contributed extensions.

Setting Up Your Next.js Project

To begin, let's kick off your Next.js project using one of two methods:

npx

npx create-next-app@latest

yarn

yarn create next-app

For the scope of this blog post, we'll focus solely on implementing the pages router. If you're interested in incorporating an app router, you can refer to the dedicated article on this topic.

Once you've generated your project, let's ensure a clean and organized starting point:

  • Navigate to Your Project: Locate your project directory and open it in your preferred code editor, such as Visual Studio Code.

  • Refine "index.js": Begin by tidying up the "index.js" file. Here's how your "index.js" should appear after cleaning up:

export default function Home() {
  return <main>Hello World</main>;
}
  • Refine "globals.css": Take a moment to clean up the "globals.css" file as well:
@tailwind base;
@tailwind components;
@tailwind utilities;
  • Install Required Packages: We need a few essential packages to get started. Install them using the following command:
npm install next-auth mongoose mongodb @next-auth/mongodb-adapter

With these steps, you're all set to dive into creating your Google authentication provided by next-auth.

Making MongoDB database

For a comprehensive guide on setting up this crucial step, refer to my dedicated blog post that walks you through each step.

Defining the User MongoDB Model

  1. Define the User Model: Start by creating a new folder "models" at the root of your project. Inside this folder, craft a file named "User.js".

  2. Model Schema and Structure: Import the mongoose library and construct your user schema. Your model should look something like this:

import mongoose from "mongoose";

const userSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: "Please enter your full name.",
    },
    email: {
      type: String,
      required: "Please enter your email address.",
      trim: true,
      unique: true,
    },
    password: {
      type: String,
      required: 'Please enter your password.',
    },
    role: {
      type: String,
      default: "user",
    },
    image: {
      type: String,
      // default: "your_default_image_url",
    },
    emailVerified: {
      type: Boolean,
      default: false,
    },
  },
  {
    timestamps: true,
  }
);
const User = mongoose.models.User || mongoose.model("User", userSchema);

export default User;

Make a connection with .env file

If you read the blog on how to connect your project with MongoDB, you have your MongoDB URL.

  • Setting Up .env: To securely connect to your MongoDB, you'll need your MongoDB URL. In the root of your project, create a ".env" file and add your MongoDB URL like this:
MONGODB_URL=your_mongodb_url 
// Replace your_mongodb_url with your actual MongoDB URL.

Adding MongoDB Password:

  • Access your MongoDB Atlas dashboard

  • Navigate to "Database Access"

  • Click "Edit"

  • Click "Edit Password"

  • Choose "Autogenerate Secure Password"

  • Copy the generated password.

  • Update Your ".env": Replace <password> in your MongoDB URL with the copied password.

MONGODB_URL=mongodb+srv://your_username:<password>.../your_database_name?retryWrites=true&w=majority
  • Replace your_username with your actual MongoDB username and your_database_name with your chosen database name.

Pay close attention not to place the code within the brackets "<password>"; make sure to remove the brackets entirely. I made this mistake personally, and it resulted in an error.

Setting Up Google API Authentication

Let's walk through the steps to set up Google API authentication for your project. This process ensures that your application can securely interact with Google services. Follow these steps carefully

  • Access the Google Cloud Console: Visit the Google Cloud Console to get started.

  • Open the drop-down menu

  • select "NEW PROJECT"

  • Give your project a meaningful name, then click "CREATE"

  • Allow a little time for the project creation, then click "SELECT PROJECT" in the upper-right corner.

  • Credentials Configuration: Navigate to the "Credentials" section

  • Click on "CREATE CREDENTIALS"

  • select "OAuth client ID"

  • Choose "CONFIGURE CONSENT SCREEN"

  • Choose "External" and click "CREATE"

  • Provide App Information: Fill in your app's name, user support email, logo (optional), application home page (for now, use localhost:3000; remember to change this in production), and developer contact information.

  • Once done, click "SAVE AND CONTINUE"

  • Scopes: You'll now see the "Scopes" section. Don't make any changes here; simply click "SAVE AND CONTINUE" again

  • Test Users: You'll reach the "Test users" section. Once again, there's no need to make changes here; just click "SAVE AND CONTINUE"

  • click "BACK TO DASHBOARD"

  • select "Credentials" once more

  • Click "CREATE CREDENTIALS" again, and select "OAuth client ID"

  • In the "Aplication type" drop-down menu, choose "Web application"

  • Click "CREATE"

  • Congratulations! You now have your Client ID and Client secret.

  • Copy your Client ID and Client secret. Open your .env file and add these lines:
MONGODB_URL=mongodb+srv://filiptrivan5:A23w3Jagv3b23sTm@cluster0.mqpzef6.mongodb.net/your_database_name?retryWrites=true&w=majority
GOOGLE_ID=your_Client_ID
GOOGLE_SECRET=your_Client_secret

Remember to replace your_Client_ID and your_Client_secret and MONGODB_URL value with the actual values you obtained from the Google Cloud Console and MongoDB. Your MongoDB URL and Google API credentials are now securely set up for your project. This ensures proper interaction with Google services and database connectivity.

Creating the Authentication Logic in the API folder

To implement authentication for your application, follow these steps to set up the necessary logic in your API:

  1. Create the "auth" Folder: Within your API directory, establish a new folder called "auth".

  2. Inside the "auth" folder, Create the [...nextauth].js File: This file is vital for configuring the authentication mechanism. Ensure it resides inside the "auth" folder.

  3. Configuration and Provider Setup: Populate the [...nextauth].js file with the following code:

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import User from "../../../models/User";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import clientPromise from "./lib/mongodb";
import db from "../../../utils/db";

// Connect to the database
db.connectDb(); 

export default NextAuth({
  adapter: MongoDBAdapter(clientPromise),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    })
  ],
  callbacks: {
    async session({ session, token }) {
      let user = await User.findById(token.sub);
      session.user.id = token.sub || user._id.toSting();
      // When creating a profile with Google, we need to provide a user role by default
      // if we want to implement roles, as it is not given by default
      session.user.role = user.role || "user";
      token.role = user.role || "user";
      return session;
    },
  },
  session: {
    strategy: "jwt",
  },
  secret: process.env.JWT_SECRET,
});

This configuration file sets up authentication using Google as the provider. It also connects to the MongoDB database, allowing you to store user-related data.

With this configuration in place, your application will be equipped with a robust authentication system using NextAuth.js and Google as the authentication provider. This lays the foundation for secure user interactions within your app.

Generating a Secure JWT Secret

When it comes to generating the JWT secret for your application, it's crucial to use dedicated JWT generator tools. These tools are purpose-built for this task, ensuring that you won't encounter issues with incompatible characters in your .env file. I strongly recommend using a trusted JWT generator, and I've found one that I believe is particularly reliable - JWT generator. As a result, your ".env" file should now look like this:

MONGODB_URL=mongodb+srv://filiptrivan5:RY353JtZv3b63sTm@cluster0.mqpzef6.mongodb.net/my-blog-2?retryWrites=true&w=majority
JWT_SECRET=eyJ0eXAiOiJKV1QiLCasfGciOirthzI1asf9.eyJjautiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2OTE1ODQ1MTIsImV4cCI6MTcyMzEyMDUxMiwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.rc8aA4zp7jLoaEOdcolQdfzRtG_IoyMzSm0lnjbh7Uc
GOOGLE_ID=your_Client_ID
GOOGLE_SECRET=your_Client_secret

By following these steps, you'll have a secure JWT secret generated through the recommended tool, setting the stage for a safer and smoother authentication process.

MongoClient promise

Inside the "auth" folder, create an additional folder named "lib" Within this "lib" folder, add a file called "mongodb.js".

In the "mongodb.js" file, use the following code. This approach is inspired by the example provided in the Next.js repository for MongoDB integration (github.com/vercel/next.js/tree/canary/examp..).

import { MongoClient } from "mongodb";

const uri = process.env.MONGODB_URL;
const options = {
  useUnifiedTopology: true,
  useNewUrlParser: true,
};

let client;
let clientPromise;

if (!process.env.MONGODB_URL) {
  throw new Error("Please add your Mongo URI to .env.local");
}

if (process.env.NODE_ENV !== "production") {
  // In development mode, use a global variable so that the value
  // is preserved across module reloads caused by HMR (Hot Module Replacement).
  if (!global._mongoClientPromise) {
    client = new MongoClient(uri, options);
    global._mongoClientPromise = client.connect();
  }
  clientPromise = global._mongoClientPromise;
} else {
  // In production mode, it's best to not use a global variable.
  client = new MongoClient(uri, options);
  clientPromise = client.connect();
}

// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise;

Managing database connections

Simplifying Database Management with a Utility Module

To make managing database connections more efficient and maintainable, we've created a dedicated utility module called "db.js". This utility takes care of connecting to and disconnecting from your MongoDB database, ensuring that your application's data interactions remain smooth and reliable.

Features of the "db.js" Utility

  1. Connection Handling: The connectDb function establishes a connection to the MongoDB database specified by your environment variable MONGODB_URL. This connection is managed intelligently, allowing it to be reused if already established, thus minimizing redundant connections.

  2. Graceful Disconnection: In a production environment, the disconnectDb function can be employed to gracefully close the database connection when it's no longer needed.

Usage

To utilize this utility, simply import it into the relevant parts of your project where database connectivity is required:

Implementation

Create a "utils" folder at the root of your project, and within this folder, create a file named "db.js".

// utils/db.js
import mongoose from "mongoose";
const connection = {};

async function connectDb() {
  if (connection.isConnected) {
    console.log("Already connected to the database.");
    return;
  }
  if (mongoose.connections.length > 0) {
    connection.isConnected = mongoose.connections[0].readyState;
    if (connection.isConnected === 1) {
      console.log("Use previous connection to the database.");
      return;
    }
    await mongoose.disconnect();
  }
  const db = await mongoose.connect(process.env.MONGODB_URL, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  });
  console.log("New connection to the database.");
  connection.isConnected = db.connections[0].readyState;
}

async function disconnectDb() {
  if (connection.isConnected) {
    if (process.env.NODE_ENV === "production") {
      await mongoose.disconnect();
      connection.isConnected = false;
    } else {
      console.log("not diconnecting from the database.");
    }
  }
}
const db = { connectDb, disconnectDb };
export default db;

_app file

Inside your "_app.js" file put the necessary setup for useSession by incorporating the Next.js SessionProvider for a smoother and more organized session management (If you don't do this, you will encounter the following error: 'useSession' must be wrapped in a <SessionProvider />):

// _app.js
import "@/styles/globals.css";
import { SessionProvider } from "next-auth/react";

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

Crafting a Dynamic User Navbar

We'll demonstrate key concepts, but for the sake of simplicity, we'll focus on functionality rather than elaborate styling or icons.

Key Components

  1. Session Management: We'll utilize NextAuth.js for session handling. By using the useSession hook, we can easily access user session data, including user information, allowing us to personalize the navbar.

  2. Dynamic Rendering: The navbar will dynamically adjust based on the user's authentication status. When a user is signed in, we'll display their profile picture and a "Sign out" option. For anonymous users, we'll present "Sign in" and "Sign up" buttons.

  3. Toggleable User Menu: We'll add a simple dropdown user menu that appears when the user clicks on their profile picture or the "Profile" text. This menu will offer actions such as signing out.

import Image from "next/image";
import Link from "next/link";
import { signOut, signIn, useSession } from "next-auth/react";
import { useState } from "react";

export default function Home() {
  const { data: session } = useSession();
  const [visible, setVisible] = useState(false);
  return (
    <div className="p-3 bg-white border-b-2">
      <div className="max-w-[1140px] m-auto justify-between flex items-center">
        <div className="flex gap-5">
          <Link href="/" className="sm:inline-block">
            <div>Logo</div>
          </Link>
        </div>
        <div className="gap-8 sm:flex">
          <div className="flex justify-end gap-6 sm:gap-8">
            <div
              className="cursor-pointer"
              onClick={() => setVisible(!visible)}
            >
              {session ? (
                <div>
                  <Image
                    width={250}
                    height={250}
                    src={session?.user?.image}
                    alt="profilna slika"
                    className="rounded-full w-[30px]"
                  />
                </div>
              ) : (
                <div>Profile</div>
              )}
              {visible && (
                <div className="absolute z-10 bg-white p-2 ml-[-255px] shadow-xl w-[300px] mt-2">
                  {session ? (
                    <ul className="space-y-1">
                      <li
                        onClick={() => signOut()}
                        className="flex hover:bg-slate-100 cursor-pointer rounded-sm p-1"
                      >
                        <span>Sign out</span>
                      </li>
                    </ul>
                  ) : (
                    <div className={`p-1`}>
                      <div
                        onClick={() => signIn()}
                        className="p-1 mb-1 hover:bg-gray-100"
                      >
                          <button>Sign up</button>
                      </div>
                      <div
                        onClick={() => signIn()}
                        className="p-1 hover:bg-gray-100"
                      >
                        <button>Sign in</button>
                      </div>
                    </div>
                  )}
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

Adjust our next.config.js

To display Google profile images, we'll need to adjust our next.config.js file. Additionally, it's essential to understand that each change to this file requires a restart of your app to take effect.

Here's the enhanced next.config.js file:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: [
      "lh3.googleusercontent.com",
    ],
  },

}

module.exports = nextConfig

Streamlining Sign-in and Sign-up

We use a unified approach for both sign-in and sign-up, particularly when utilizing external authentication methods such as Google or Facebook. This approach ensures a consistent user experience. We'll differentiate these processes only if additional information is required during sign-up.

In the interest of user experience, we plan to create separate pages for sign-in and sign-up with the same underlying logic, utilizing distinct button labels to distinguish between the two actions.

Please note that if you wish to customize the Google sign-in page, modifications are needed in your [...nextauth].js file and the addition of supplemental files (custom sign-in page).

Lastly, it's time to try out the Google sign-in feature in your project.

Verifying User Existence in the Database

Before we proceed, it's crucial to ensure the user's presence in the database. If you've followed my previous MongoDB blog, you've likely observed how to manage the database through the web interface or the MongoDB Compass application. Personally, I find Compass to be a great choice. When I initiated the sign-in process and selected the Google account, a new user was automatically created.

Email Management Tip

When testing, you might require emails and find yourself running low on options. It's often tempting to delete them from the database directly, but if you're hesitant about potential issues, consider utilizing a 10-minute mail service available on the internet. There are several options available, so select the one that aligns with your preferences.

Watch Out for this Common Pitfall

In case you've exhausted your testing emails and decide to delete some from the database, it's essential to maintain consistency. Just as you delete a user, ensure that you also delete the associated account to prevent any potential issues. By adhering to this practice, you'll avoid encountering unexpected problems during your testing and development.

Crafting Personalized Authentication Pages

If you're aiming for a unique touch in your authentication process, creating custom authentication pages is a fantastic idea. Here's how you can achieve this by making a few straightforward adjustments.

  • Modify [...nextauth].js: Open your [...nextauth].js file and incorporate the following code:
  pages: {
    signIn: "/signin",
  },
// api/auth/[...nextauth].js
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import User from "../../../models/User";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";
import clientPromise from "./lib/mongodb";
import db from "../../../utils/db";

db.connectDb(); 

export default NextAuth({
  adapter: MongoDBAdapter(clientPromise),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    })
  ],
  callbacks: {
    async session({ session, token }) {
      let user = await User.findById(token.sub);
      session.user.id = token.sub || user._id.toSting();
      // when we create a profile with google we have to
      // give it a user role by default
      // because it is not given by default
      session.user.role = user.role || "user";
      token.role = user.role || "user";
      return session;
    },
  },
// This sets the custom sign-in page route to "/signin".
  pages: {
    signIn: "/signin",
  },
//
  session: {
    strategy: "jwt",
  },
  secret: process.env.JWT_SECRET,
});
  • Create a Sign-in Route: Within your Next.js project, generate a new folder named "signin" within the "pages" directory. Inside this "signin" folder, add a file named "index.js", this page will serve as the custom sign-in interface. Place the following code within it:

// index.js ...\signin
import Link from "next/link";
import {
  getCsrfToken,
  getProviders,
  getSession,
} from "next-auth/react";
import { useRouter } from "next/router";
import { signIn } from "next-auth/react";

const LoginInput = ({ providers, callbackUrl, csrfToken }) => {
    const router = useRouter();

    const handleSignIn = async (providerId) => {
      const result = await signIn(providerId, {
        redirect: false,
        csrfToken: csrfToken, // Pass the csrfToken as a parameter
      });
      if (result?.error) {
        // Handle sign-in error
        console.log(result.error);
      } else { 
        // Redirect to homepage
        router.push(callbackUrl || "/");
      }
    };
  return (
    <div className="space-y-6 xl:space-y-7 max-w-[440px] m-auto p-1 text-center mt-24">
      <Link href={"/"} className="text-7xl">
        Logo
      </Link>
      <div className="text-center text-xl md:text-3xl font-light">
        Sign in
      </div>
      <div className="p-4 border bg-white rounded-md">
        <div className="grid">
        {providers?.map((provider) => (
        <button
          key={provider.id}
          onClick={() => handleSignIn(provider.id)}
          className="py-4 rounded-full ring font-semibold flex items-center hover:bg-gray-100"
        >
          <p className="m-auto">Sign in with Google</p>
        </button>
      ))}
        </div>
      </div>
    </div>
  );
};

export default LoginInput;

export async function getServerSideProps(context) {
  const { req, query } = context;

  const session = await getSession({ req });
  const { callbackUrl = "/" } = query;

  if (session) {
    return {
      redirect: {
        destination: callbackUrl, // Redirect to callbackUrl or homepage
        permanent: false,
      },
    };
  }
  const csrfToken = await getCsrfToken(context);
  const providers = Object.values(await getProviders());
  return {
    props: {
      providers,
      csrfToken,
      callbackUrl,
    },
  };
}
  • Customize Sign-up Page: For simplicity, you can reuse much of the code from the sign-in page for the sign-up page while making necessary adjustments to the component names and content:

// index.js ...\signup
import Link from "next/link";
import {
  getCsrfToken,
  getProviders,
  getSession,
} from "next-auth/react";
import { useRouter } from "next/router";
import { signIn } from "next-auth/react";

const LoginInput = ({ providers, callbackUrl, csrfToken }) => {
    const router = useRouter();

    const handleSignIn = async (providerId) => {
      const result = await signIn(providerId, {
        redirect: false,
        csrfToken: csrfToken, // Pass the csrfToken as a parameter
      });
      if (result?.error) {
        // Handle sign-in error
        console.log(result.error);
      } else { 
        // Redirect to homepage
        router.push(callbackUrl || "/");
      }
    };
  return (
    <div className="space-y-6 xl:space-y-7 max-w-[440px] m-auto p-1 text-center mt-24">
      <Link href={"/"} className="text-7xl">
        Logo
      </Link>
      <div className="text-center text-xl md:text-3xl font-light">
        Sign up
      </div>
      <div className="p-4 border bg-white rounded-md">
        <div className="grid">
        {providers?.map((provider) => (
        <button
          key={provider.id}
          onClick={() => handleSignIn(provider.id)}
          className="py-4 rounded-full ring font-semibold flex items-center hover:bg-gray-100"
        >
          <p className="m-auto">Sign up with Google</p>
        </button>
      ))}
        </div>
      </div>
    </div>
  );
};

export default LoginInput;

export async function getServerSideProps(context) {
  const { req, query } = context;

  const session = await getSession({ req });
  const { callbackUrl = "/" } = query;

  if (session) {
    return {
      redirect: {
        destination: callbackUrl, // Redirect to callbackUrl or homepage
        permanent: false,
      },
    };
  }
  const csrfToken = await getCsrfToken(context);
  const providers = Object.values(await getProviders());
  return {
    props: {
      providers,
      csrfToken,
      callbackUrl,
    },
  };
}
  • Link to Sign-up Page: Finally, let's provide a convenient way for users to navigate to the custom sign-up page. You can add a Link component in the root index.js file that points to the newly created sign-up page:
// index.js
import Image from "next/image";
import Link from "next/link";
import { signOut, signIn, useSession } from "next-auth/react";
import { useState } from "react";

export default function Home() {
  const { data: session } = useSession();
  const [visible, setVisible] = useState(false);
  return (
    <div className="p-3 bg-white border-b-2">
      <div className="max-w-[1140px] m-auto justify-between flex items-center">
        <div className="flex gap-5">
          <Link href="/" className="sm:inline-block">
            <div>Logo</div>
          </Link>
        </div>
        <div className="gap-8 sm:flex">
          <div className="flex justify-end gap-6 sm:gap-8">
            <div
              className="cursor-pointer"
              onClick={() => setVisible(!visible)}
            >
              {session ? (
                <div>
                  <Image
                    width={250}
                    height={250}
                    src={session?.user?.image}
                    alt="profilna slika"
                    className="rounded-full w-[30px]"
                  />
                </div>
              ) : (
                <div>Profile</div>
              )}
              {visible && (
                <div className="absolute z-10 bg-white p-2 ml-[-255px] shadow-xl w-[300px] mt-2">
                  {session ? (
                    <ul className="space-y-1">
                      <li
                        onClick={() => signOut()}
                        className="flex hover:bg-slate-100 cursor-pointer rounded-sm p-1"
                      >
                        <span>Sign out</span>
                      </li>
                    </ul>
                  ) : (
                    <div className={`p-1`}>
                      <Link href={"/signup"}>
                        <div className="p-1 mb-1 hover:bg-gray-100">
                          <button>Sign up</button>
                        </div>
                      </Link>
                      <div
                        onClick={() => signIn()}
                        className="p-1 hover:bg-gray-100"
                      >
                        <button>Sign in</button>
                      </div>
                    </div>
                  )}
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

Conclusion

In this comprehensive guide, we explored the powerful capabilities of Google authentication in Next.js applications, all thanks to the next-auth library. We delved into the seamless integration of Google authentication, unlocking a world of user interactions and personalized experiences.

With the knowledge gained from this guide, you're well-equipped to implement Google authentication in your Next.js projects, enhancing both the security and user experience of your application. Take advantage of next-auth, explore further customization options, and continue to refine your application to meet the unique needs of your users. Cheers to your journey of creating secure and user-centric Next.js applications!