Google Login with NextAuth.js

Install NextAuth

yarn add next-auth
npm install next-auth

Version

    "next": "14.1.4",
    "next-auth": "^4.24.7"

Implementation

  1. Let's start with Providers, For Google Login We need Google Provider (https://next-auth.js.org/providers/google)

     import GoogleProvider from "next-auth/providers/google";
     ...
     providers: [
       GoogleProvider({
         clientId: process.env.GOOGLE_CLIENT_ID,
         clientSecret: process.env.GOOGLE_CLIENT_SECRET
       })
     ]
     ...
    
  2. Now, How can we get our clientID anf clientSecrect?

    • Go to Google Cloud Console - console.developers.google.com/apis/credenti..

    • Create a New Project

    • Select Create crendentials and add a OAuth client ID

    • The "Authorized redirect URIs" used when creating the credentials must include your full domain and end in the callback path. For example;

    • Form here you can access your ClientID and Client Secret

    • Put them is you .env

  3. Now you can separate out your Auth Options in authOptions.ts file

     //authOptions.ts
     import clientPromise from "@/lib/mongoClient";
     import {AuthOptions} from "next-auth";
     import GoogleProvider from "next-auth/providers/google";
    
     export const authOptions:AuthOptions = {
       secret: process.env.AUTH_SECRET,
       providers: [
         GoogleProvider({
           clientId: process.env.GOOGLE_CLIENT_ID as string,
           clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
         }),
       ],
     };
    
  4. Now you need Adapter to store your users and sessions

      • Here we are using MongoDB to store out Users.(https://next-auth.js.org/adapters)

        • Add mongodb adapter

           yarn add @auth/mongodb-adapter mongodb
          
           //or
          
           npm install @auth/mongodb-adapter mongodb
          
        • Setup:

          1. Add MongoClient :

             //mongoClient.ts
             import { MongoClient } from "mongodb";
            
             if (!process.env.MONGODB_URI) {
               throw new Error("Please add your Mongo URI to .env.local");
             }
            
             const uri: string = process.env.MONGODB_URI;
             let client: MongoClient;
             let clientPromise: Promise<MongoClient>;
            
             if (process.env.NODE_ENV === "development") {
               // In development mode, use a global variable so that the value
               // is preserved across module reloads caused by HMR (Hot Module Replacement).
            
               let globalWithMongoClientPromise = global as typeof globalThis & {
                 _mongoClientPromise: Promise<MongoClient>;
               };
            
               if (!globalWithMongoClientPromise._mongoClientPromise) {
                 client = new MongoClient(uri);
                 globalWithMongoClientPromise._mongoClientPromise = client.connect();
               }
            
               clientPromise = globalWithMongoClientPromise._mongoClientPromise;
             } else {
               // In production mode, it's best to not use a global variable.
               client = new MongoClient(uri);
               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;
            
            1. Config Auth.js : use MongoDBAdapter (in Our case authOptions.js)

                adapter: MongoDBAdapter(clientPromise)
              

              Finally Auth.js File -

               //authOptions.ts
               import clientPromise from "@/lib/mongoClient";
               import {MongoDBAdapter} from "@auth/mongodb-adapter";
               import {AuthOptions} from "next-auth";
               import GoogleProvider from "next-auth/providers/google";
              
               export const authOptions:AuthOptions = {
                 secret: process.env.AUTH_SECRET,
                 providers: [
                   GoogleProvider({
                     clientId: process.env.GOOGLE_CLIENT_ID as string,
                     clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
                   }),
                 ],
                   // @ts-ignore
                   adapter: MongoDBAdapter(clientPromise),
               };
              
    1. Now Create a userClient.ts using authOptions

       //userClient.ts
       import {authOptions} from "@/lib/authOptions";
       import {getServerSession} from "next-auth";
      
       export async function getUserEmail(): Promise<string> {
         const session = await getServerSession(authOptions);
         return session?.user?.email || '';
       }
      
    2. Now Create a API route for login in : api/auth/[...nextauth]

       //route.ts
       import NextAuth from "next-auth"
       import { authOptions } from "@/lib/authOptions";
      
       const handler = NextAuth(authOptions);
      
       export { handler as GET, handler as POST }
      
    3. Let's Create a Login Page :

      Just use the signIn('google') and BOOMMMM!!

       'use client';
       import { signIn } from "next-auth/react";
      
       export default function LoginButton() {
        return (
         <button onClick={() => signIn('google')}
          className="bg-gray-300 py-2 px-4 ml-2 rounded-md">
          Login
         </button>
        );
       }
      
    4. Now to handle Logout:

      We have magical singOut()

       'use client';
       import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
       import { signOut } from "next-auth/react";
       import {faArrowRightFromBracket} from "@fortawesome/free-solid-svg-icons";
      
       export default function LogoutButton() {
        return (
         <button onClick={() => signOut()}
          className="bg-gray-300 py-2 px-4 ml-2 rounded-md inline-flex gap-2 items-center">
          Logout
          <FontAwesomeIcon icon={faArrowRightFromBracket} />
         </button>
        );
       }