Next.js and Next-Auth V5: Guide to Social Logins(OAuth)

Next.js and Next-Auth V5: Guide to Social Logins(OAuth)

Using Next-Auth V5 for Google and GitHub OAUTH Login in a Next.js app: A complete guide

ยท

15 min read

Featured on Hashnode

Authentication and Authorization are the implicit needs for any user-facing application. Using authentication users let the application and underlying services know who they are, and if they are the legitimate people to get access to the system. There are various ways of authenticating like credential-based authentication, OTP-based authentication, magic links, biometrics, and more.

While authentication is the way to tell someone who you are, authorization is the way to provide someone access to resources they are supposed to have. There are role-based, policy-based authorization models that ensure the right access to resources when users try accessing them after authentication.

You will find this article helpful if you are looking for a guide to learning about Authentication and Authorization.

In this step-by-step tutorial, we will focus on setting up authentication using OAuth providers like Google and Github with a Next.js application. We will use Auth.js or Next-Auth V5, an open-source authentication tool for the web to get the auth set-up done.

Let's get started ๐Ÿš€

Note: It would be great if you code as you follow along the sections of this article. The source code used in this article is publicly available on GitHub for you to access and use. You can find the link to it at the bottom of this article.

Create a Next.js App Router Project

First, create a Next.js app using the command below from your terminal/command prompt.

npx create-next-app@latest

Make sure you have the latest Node.js installed.

The prompt will guide you to create a Next.js app with a name of your choice, a target tech stack (JavaScript or TypeScript), and a few more configurations. In my case, I went ahead with the following choices:

Create a Next.js project

Please note, that I have selected JavaScript, TailwindCSS, and most importantly, the App Router. After the project completion, change the directory to the project directory and start the local server using the following command:

yarn dev # Or its npm, pnpm, bun equivalent

Now, the local server will run on localhost:3000 by default.

Auth.js , aka Next-Auth(Version 5)

Auth.js is an open-source authentication library that integrates with modern JavaScript frameworks like Next.js, Svelte, and Express. Previously the project was available only for Next.js and thus it used to be called Next-Auth. Today, a portion of Auth.js is still supports Next-Auth, but along with it, there are SvelteKitAuth, and ExpressAuth available too.

Installation

We will use Next-Auth Version 5 which is in its beta while writing this article. Open a terminal and install next-auth with the following command:

yarn add next-auth@beta

# Or
# npm install next-auth@beta
# pnpm add next-auth@beta
# bun add next-auth@beta

Setting up environment

Create a file called .env.local at the root of your project folder. We need to add a key called AUTH_SECRET with a value. You can generate the secret by using the following command from your terminal or command prompt:

npx auth secret

You should see an output like below which prompts you to copy the key-value pair and paste it into the .env.local file.

Auth Secret

Alternatively, if you are on a Linux / Mac OS X system, you can use the following command to generate the auth secret:

openssl rand -base64 33

In whatever ways you generate the secret, please ensure to copy-paste and create a key-value pair in the .env.local file like this:

VS Code - Auth Secret

Configurations: The auth.js file

Next, create a auth.js file at the root of the project folder with the following code:

import NextAuth from "next-auth";

export const {
    handlers: { GET, POST },
    auth,
    signIn,
    signOut,
} = NextAuth({
    providers: [
        // WE WILL ADD THINGS HERE SHORTLY
    ],
});

Here we have created a minimalistic configuration of the Next-Auth to avail functionalities like signIn, signOut, along with an auth object that can provide us with all information about the session and the logged-in user in the session. The GET, and POST handlers will be used in a route handler that we will learn about soon.

At this point, the only configuration we pass to the NextAuth is an empty providers array. However, do not worry much about it, as we will be filling it soon.

The callback route

Now we will create a route handler specially for Next-Auth. This route handler will be used as a callback route from the OAuth providers when we configure them after a while.

Create a folder hierarchy called api/auth/[...nextauth]/ under the app/ folder. The [...nextauth] directory creates a dynamic route. Now create a file called route.js under this hierarchy as follows: /app/api/auth/[...nextauth]/route.js.

Copy and paste the following code snippet inside the route.js file.

// Here the GET and POST is being imported
// from the auth.js file before we export
// them from here.

export { GET, POST } from "@/auth";

That's it. We are done with the installation, set-up, and basic configurations of Next-Auth. Let us now shift our focus to building the user interface(UI) needed for the authentication.

Create Login Form

Create a components folder at the root of your project folder and create a file called LoginForm.jsx under it with the following code:

import { doSocialLogin } from "@/app/actions";

const LoginForm = () => {
    return (
        <form action={doSocialLogin}>
            <button 
                className="bg-pink-400 text-white p-1 rounded-md m-1 text-lg" 
                type="submit" 
                name="action" 
                value="google">
                Sign In With Google
            </button>

            <button 
                className="bg-black text-white p-1 rounded-md m-1 text-lg" 
                type="submit" 
                name="action" 
                value="github">
                Sign In With GitHub
            </button>
        </form>
    );
};

export default LoginForm;

A few things to note here:

  • We have created a login form with two submit buttons to sign in with Google and GitHub respectively.

  • We have imported a server action called doSicialLogin and using it as the form action so that the function gets invoked when the user submits the form by clicking on either button. The server action function doesn't exist yet, we will create it soon.

  • Each of the submit buttons has the same name attribute value called action. It is helpful to identify the button that triggers the form submission inside the server action.

The Home Page

Now, open the page.js file under the app/ folder and replace its content with the following code:


import LoginForm from "@/components/LoginForm";

export default function Home() {
  return (
    <div className="flex flex-col justify-center items-center m-4">
      <h1 className="text-3xl my-3">Hey, time to Sign In</h1>
      <LoginForm />
    </div>
  );
}

It is a simple component that imports the LoginForm we created above and used it in the JSX. If you try to access the app now, you will get an error due to the non-existence of the doSocialLogin server action used in the login form. Let's fix that.

Next.js Server Actions: Log in and out

Server Action in Next.js are built on React Actions that you can define in server components and/or calls from client components. Server actions are JavaScript async functions that run on the server by the user interactions with the client.

It helps to build a data mutation technique more often when you want to submit a form to create, update, or delete data on the server. It can also be useful to perform isolated server-side actions like sending emails, processing payments, user login, logout, and many more use cases.

Read this article to understand the usages and use cases of Server Actions in-depth.

Create a folder called actions/ under the app/ folder. Now, create a file called index.js under the app/actions/ with the following code:


'use server'

import { signIn, signOut } from "@/auth";

export async function doSocialLogin(formData) {
    const action = formData.get('action');
    await signIn(action, { redirectTo: "/home" });
}

export async function doLogout() {
  await signOut({ redirectTo: "/" });
}

Let us understand what is happening there.

  • We used a directive called 'use server' at the top of the file to inform Next.js that this file contains server actions and they must be executed on the server side.

  • Then, we have defined two server actions, doSocialLogin() and doLogout().

  • Earlier, the action doSocialLogin() we have imported into the LoginForm component to invoke it when the login form is submitted. Hence it receives a formData object containing the information of the submitted form fields. In our case, the login form doesn't have any other fields than two submit buttons sharing the same name value, action. Hence we retrieve the button value using the action name to see whether users have clicked on the Google or GitHub buttons to submit the form.

  • Inside the doSocialLogin() function we call the signIn() from the auth.js by passing a couple of arguments. The first argument is to tell the Next-Auth about the provider to use to sign in. In our case, it will be either google or github. The second parameter is a configuration object informing auth.js about the redirection page after login. It says, redirect to the home page(/home route) once authenticated.

  • The doLogout() action invokes the signOut() function from auth.js and redirect to the root route(/) on log out.

Now access the app, you should see it running and showing you the user interface like the image below. Clicking on either of the buttons will not do much as we haven't configured the providers yet, and the great news is, that we will be doing it next.

Login Form

Social Login With Google Oauth

Let us make the sign-in with Google work first. This is a multi-step procedure. First, we need to create a client ID and client secret from Google Cloud Console. Once we have them, we need to configure the Google Provider in the auth.js file,

Client ID and Secret from Google

Go to Google Cloud Console and follow these steps to generate a client ID and secret. You can also use an existing client ID and client secret.

  • Log in to the Google Cloud console and click on the Credentials option from the left menu. Then click on the + CREATE CREDENTIALS drop down from the top and select the OAuth client ID option as shown in the image below.

    GCP

  • In the next screen, select Web application as an application type, give a suitable name for the OAuth 2.0 client. I preferred to keep it the same as my project name. Then provide localhost:3000 as the authorized JavaScript origin URL. It will make sure that the client and secrets can not be used from elsewhere.

  • Next, provide the URL http://localhost:3000/api/auth/callback/google as the value of the authorized redirect URI. Does this URI ring a bell ๐Ÿ””? Yes, this is the same route pattern we created earlier with the route.js.

  • Finally, click on the CREATE button.

    Create OAuth Client ID and Secret

  • Now you will see a modal dialog with the OAuth client ID and secret created. Make sure you copy them, and optionally you can download them in a JSON file to use later.

  • Click on OK to close the modal.

    Auth Client Created

Set Up Environment

In the .env.local file add two entries:

  • Key GOOGLE_CLIENT_ID, and the value will be the client ID we created above.

  • Key GOOGLE_CLIENT_SECRET, and the value will be the client secret we created above.

Google Client ID and Secret

Configure the Google Provider for Next-Auth

Open the auth.js file and import GoogleProvider as shown below. Also, add the GoogleProvider configuration in the providers array as mentioned in the code below.

Please note,

  • We are using the client ID and secret from the environment variables.

  • We also configured to provide a consent UI for our users to provide their consent to use their Google auth every time they authenticate.

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export const {
    handlers: { GET, POST },
    auth,
    signIn,
    signOut,
} = NextAuth({
    providers: [
        GoogleProvider({
            clientId: process.env.GOOGLE_CLIENT_ID,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET,
            authorization: {
                params: {
                    prompt: "consent",
                    access_type: "offline",
                    response_type: "code",
                },
            },
        })
    ],
});

Test it out from the UI

Now, it's time to test out the Sign in with Google flow. So, go to the app and click on the button to log in with Google. You will see a redirection to select an account to authenticate. If you have multiple Google accounts, please select the one you want to authenticate with.

Select an Account - Google

The next step will be a consent screen where you have to click on the Continue button to log in.

Consent Screen - Google

After this, you will be redirected to a route called /home because that's how we have configured the signIn() function above. You will see a 404-page not found error because we have not created the route yet. However, the best part is, that our authentication is successful.

Home page 404

To validate the cookie created for the session token, open up the browser dev tools and move to the Application tab. In the left panel, expand the Cookies options and click on the http://localhost:3000. You should find the authjs.session-token cookie with the token value at the bottom.

Google Auth Cookie

Show LoggedIn User's Details

It's time to fix the /home route and show something other than a 404 error. How about showing the logged-in user's details like name and profile image? We can also show a logout option to sign out from the app when the user is already logged in.

Create a folder called home/ under the app/ folder, and create a page.js file under the app/home/ with the following code:

import Image from "next/image";
import Logout from "@/components/Logout";
import { auth } from "@/auth";

import { redirect } from "next/navigation";

const HomePage = async () => {
    const session = await auth();

    if (!session?.user) redirect("/");

    return (
        <div className="flex flex-col items-center m-4">
            <h1 className="text-3xl my-2">
                Welcome, {session?.user?.name}
            </h1>
            <Image
                src={session?.user?.image}
                alt={session?.user?.name}
                width={72}
                height={72}
                className="rounded-full"
            />
            <Logout />
        </div>
    );
};

export default HomePage;

Here:

  • We have created a static route called /home.

  • We imported the Logout component. We haven't created the Logout component yet, will do that next.

  • Then we imported auth from the auth.js. The auth is an async function that returns us with the session information, including the logged-in user's details.

  • We retrieved the session information and checked if the logged-in user information was available. If not, we redirected the user to the root route where we have the login form. So this way, we can protect a specific route at the page level.

  • In the JSX, we displayed the logged-in user's name and image and the Logout component. Let's create the Logout component.

Add Sign Out(Logout) Functionality

Create a file called Logout.jsx under the components folder with the following code:


import { doLogout } from "@/app/actions"

const Logout = () => {
  return (
    <form action={doLogout}>
        <button 
            className="bg-blue-400 my-2 text-white p-1 rounded" 
            type="submit">
                Logout
        </button>
    </form>
  )
}
export default Logout

Explanation:

  • It is a simple React component with a form and a submit button.

  • With the click of a button, we submit the form and invoke the doLogout() server action we have created already!

That's it. Time for a final test. Perform the login with Google as before. Wait, do you get an error like this instead of seeing the logged-in user's details?

next-image-error

Don't worry. It is because we haven't informed Next.js about the source of the profile image coming from Google. Open the next.config.mjs file and replace the content with the following configuration:

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'lh3.googleusercontent.com'
      },
    ],
  },
};

export default nextConfig;

In the code above, we have provided the trusted hostname as the image source. The hostname was mentioned in the error as a suggestion for us to configure.

Now refresh the UI. You should see the home page up and running with the logged-in user's name, image, and a Logout button. Clicking on the Logout button will log you out of the app and take you to the login form.

Successful Home Page

Now, You can also check the cookies like the last time. You will not find the session token cookie when you log out.

Wow! Finally, we are here after implementing the social login using Google by using Next-Auth/Auth.js.

Let's finish the configurations for GitHub as well.

Social Login With GitHub OAuth

We will be re-using a lot of things done so far. First, let us create the client ID and client secret for a GitHub OAuth app.

Client ID and Secret from GitHub

Log in to your GitHub account and go to the developer settings following this URL. Under the OAuth Apps section, you will find your existing Oauth Apps, if any. Now, create a new OAuth app by clicking the button at the top-right corner of the page.

Next, you need to fill up a few details like we did for Google. Give a suitable application name, home page URL as localhost:3000, and the authorized callback URL. Please provide http://localhost:3000/api/auth/callback/github as the value of the authorized callback URL. Then, register the application.

You will see a client ID generated. Please copy and keep it safe somewhere. Now, click on the Generate a new client secret button to create a client secret.

A new client secret will be generated for you. Please copy and keep this one safe, too.

Set Up Environment

Create two new more environment variables GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET and assign the respective values we have created.

Configure GitHub Provider From Next-Auth

The last thing remaining is to configure the GitHub provider. Open the auth.js file and import the GitHubProvider as shown below. Then, add the GitHubProvider object in the providers array.

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import GitHubProvider from "next-auth/providers/github";

export const {
    handlers: { GET, POST },
    auth,
    signIn,
    signOut,
} = NextAuth({
    providers: [
        GoogleProvider({
            clientId: process.env.GOOGLE_CLIENT_ID,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET,
            authorization: {
                params: {
                    prompt: "consent",
                    access_type: "offline",
                    response_type: "code",
                },
            },
        }),
        GitHubProvider({
            clientId: process.env.GITHUB_CLIENT_ID,
            clientSecret: process.env.GITHUB_CLIENT_SECRET,
            authorization: {
                params: {
                    prompt: "consent",
                    access_type: "offline",
                    response_type: "code",
                },
            },
        })
    ],
});

That's it. Go back to UI and Sign in with GitHub. You should get a screen to Authorize the Oauth app. Click on the Authorize button.

Boom! ๐Ÿ’ฅ

The same error as before, but you know how to fix it.

Open the next.config.mjs file and add another entry to the remotePatterns array for GitHub images.

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'lh3.googleusercontent.com'
      },
      {
        protocol: 'https',
        hostname: 'avatars.githubusercontent.com'
      },
    ],
  },
};

export default nextConfig;

Now refresh the screen and you should see the logged-in user's name, image, and the Logout button.

Incredible! We have learned to set up and code both Google and GitHub Oauth login using Next-Auth/Auth.js with Next.js App Router. It feels amazing!

Source Code

All the source code you have written so far as you explored this article can be found in this repository(under the branch 02-integrate-github-provider).

If you like the work, do not forget to encourage me with a star โญ.

Further Learning

If you enjoyed this article and developing the app, you may also like to explore a few more related topics on Next.js and Auth.js. Here are the links:

Also, this same tutorial is available as an interactive video tutorial.


Liked it? Please let me know with your comments and likes. โค๏ธ

Let's connect. I share knowledge on web development, content creation, Open Source, and careers on these platforms.

Did you find this article valuable?

Support Tapas Adhikary by becoming a sponsor. Any amount is appreciated!