Building Google-JWT Authentication with NextAuth

To satisfy the needs of the modern man his comfort and time is not negotiable. Gone are the days where users go through a long sequence of input fields in order to sign up into a software. This however brought about the innovation of a passwordless onb…


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Ifenna Monanu

To satisfy the needs of the modern man his comfort and time is not negotiable. Gone are the days where users go through a long sequence of input fields in order to sign up into a software. This however brought about the innovation of a passwordless onboarding system where time and energy are conserved.

In this article I would work you through a flow process on how to setup user authentication just in two clicks using Next.js and it's authentication library NextAuth.js .

What is Next.js?

Next.js is a framework built on top of React that makes developing production-ready, fully-optimized React apps super fast and easy. One of the main characteristics of Next.js is its run time speed.

Next.js is used in production by top companies like Netflix, Tiktok, and Nike. It is super easy to learn, especially if you’re familiar with React.

What is NextAuth.js?

NextAuth.js is a Next.js authentication library. It exists as an abstraction of the OAuth library as it leverages on its authentication and authorization providers (such as Google, Twitter, Facebook, GitHub). With NextAuth.js a software can have access to a user's profile information on adequate permission by the user.

NextAuth.js has a client-side API you can use to interact with sessions in your app. The session data returned from the Providers contains user payload, and this can be displayed to the user upon successful login.

The session data returned to the client looks like this:

{
  expires: '2055-12-07T09:56:01.450Z';
  user: {
        email: 'newsuer@example.com';
        image: 'https://avatars2.githubusercontent.com/u/45228014?v=4';
        name: 'newuser';
    }
}

Requirements

  • Node.js 10.13 or later installed on your local machine
  • Basics of React.js

Create a new Next.js project

Creating a Next.js app is similar to creating a new react app. At first we created a new directory in our case my_projects in our new created directory we create a new instance of a next app into a the next_auth directory.

mkdir my_projects
cd my_projects
npx create-next-app@latest next_auth

NB: @latest ensures that our installation comes along with the latest Next.js version.

Once this is done an instance of a Next app would be available in our project directory.

Installing Packages

Our next line of action would be to install the necessary packages.

npm i next-auth axios jsonwebtoken --save
or
yarn add next-auth axios jsonwebtoken --save
  • next-auth:- Next.js authentication library the backbone of our authentication build.

  • jsonwebtoken:- used in to sign user payload thereby generating a token necessary for authorization.

  • axios:- with axios we would be making 2 API calls to our server. The first is to confirm if the authenticating email already exists if yes then it logs in the user, else it calls the second API which is responsible for storing user details in the database. We would look into this more as we proceed.

Upon successful installation, next-auth should be added to the dependencies in your package.json file:

//Dependencies in package.json
"dependencies": {
 "dependencies": {
    "axios": "^0.27.2",
    "jsonwebtoken": "^8.5.1",
    "next": "latest",
    "next-auth": "^4.10.2",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
  }
}

Create a Google OAuth app

To allow users to log in to our app using their Google account, we have to obtain OAuth 2.0 client credentials from the Google API Console. Navigate to Credentials and click on Create credentials, and then OAuth client ID:
Image description

You would be asked to fill in the following:

  • Choose an Application Type: Select Web Application
  • Name: This is the name of your application
  • Authorized JavaScript origins: This is the full URL to the homepage of our app. Since we are still in development mode, we are going to fill in the full URL our development server is running on. In this case, it is http://localhost:3000.

NB: Remember to change your home page URL once your app gets into production mode.

NB: These URLs are custom URLs none of these them are assigned to our software by google.

Next, a popup will display your client ID and client secret. Copy both keys to your clip board.

Add an environmental variable

Next, create an .env.local file in your project’s root directory. Next.js has inbuilt support for environment variables and .env.local will load those variables to process.env. Therefore, the filename cannot be a random name. For more information, don’t hesitate to read Next.js documentation on environment variables.

Next, populate our newly created file with our OAuth credentials we had copied earlier.

GOOGLE_ID=<client id of your google auth app should go in here>
GOOGLE_SECRET=<client secret of your google auth app should go in here>
NEXTAUTH_URL= http://localhost:3000
NEXT_PUBLIC_JWT_SECRET_KEY: <your jwt secret key>

Google Provider Initialization

Now, back to our app. We’re going to create a file named [...nextauth].js in pages/api/auth.

Image description

In our newly created file add the following code:

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

const options = {
    providers: [
        GoogleProvider({
            clientId: process.env.GOOGLE_ID,
            clientSecret: process.env.GOOGLE_SECRET
        }),
    ],
}

On line 1, we’re importing NextAuth, which is our main package.

On line 2, we’re importing our GitHub Provider from the next-auth library, which are services that we can integrate into our app to allow users to sign in.

On line 6, we’re configuring our GitHub provider and passing in our GitHub secret and client ID through our environmental variables.

Setup action page

NextAuth provides us with an option to setup custom pages which would be returned to the browser depending on several line of action tolled during the course of our user authentication.
The pages object is placed directly below the providers array.

const options = {
    providers: [
        GoogleProvider({
            clientId: process.env.GOOGLE_ID,
            clientSecret: process.env.GOOGLE_SECRET
        }),
    ],

    pages: {
    signIn: '/auth/dashbaord', // on successfully signin    
    signOut: '/auth/login', // on signout redirects users to a custom login page.
    error: '/auth/error',  // displays authentication errors
    newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
  }
}

Authentication Callbacks

Callbacks are asynchronous functions you can use to control what happens when an action is performed. Request to the backend API is handled using callbacks.

NextAuth provides us with certain vital callbacks which include:

  • Sign in callback : Use the signIn() callback to control if a user is allowed to sign in. With in this object we would be making two axios request to our backend API, the first one would be to verify if our user exists, this request would query our database to verify if the user's email exists if yes it returns true signifying that our user want's to login to his account, if our query returns false we would then make our second axios request which receives the user profile object returned from google and stores it in our custom database.

lets see how this really works:-

  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      // first axios request to ascertain if our user exists in our custom DB
      const response = await axios.post(
        "http://localhost:9000/v1/auth/userExists",
        { email: profile.email }
      );
      if (response && response.data?.value === true) {
        // user exists return true passing control to the next callback
        return true;
      } else {
        // second axios call which creates a new user in our database
        const data = {
          firstName: profile.given_name,
          lastName: profile.family_name,
          email: profile.email,
          profileUrl: profile.picture,
        };
        const response = await axios.post(
          "http://localhost:9000/v1/auth/signup",
          data
        );
        // retruns true thereby passing control to the next callback
        return true;
      }
    },
}

- JWT callback : this callback takes in a user object, token object as it's parameters. The user object contains the details of the signed in user, on line 5, SignToken() function is called which returns a jwt token, this token is appended to our callback token object as userToken,

token.userToken = token;

  async jwt({ token, user, account }) {
      if (account) {
        console.log(user, token, account);
        // call the signToken function which returns a JWT token
        const token = await SignToken(user?.email as string);
        token.userToken = token;
      }
// our token object is passed done to the session call back for persistence
      return token;

    },

our SignToken() function:

import jwt from 'jsonwebtoken';

const SignToken = async (email)=> {
const token = await jwt.sign({id:email}, process.env.NEXT_PUBLIC_JWT_SECRET_KEY, {expiresIn: '1d'});
    return token
}

export default SignToken;
  • Session Callback: Our session callback takes in the token object passed in from the jwt callback seen above, and a session object. Our jwt token is appended to our session object as loggedUser;

session.loggedUser = token.userToken;

Our session object get persisted in our browser session for authorization at any point. The session callback is called whenever a session is checked.

   async session({ session, token, user }) {
      // Send properties to the client, like an access_token from a provider.
      session.loggedUser = token.userToken;
      return session;
    },
  },

A glimpse of our how our browser session object now looks like;

{
  expires: '2055-12-07T09:56:01.450Z';
// our jwt token
  loggedUser: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCI1NiIsInR"
  user: {
        email: 'newsuer@example.com';
        image: 'https://avatars2.githubusercontent.com/u/45228014?v=4';
        name: 'newuser';
    }
}

Put together our [...nextauth].js file looks like:

import GoogleProvider from "next-auth/providers/google";
import NextAuth from "next-auth";
import axios from "axios";
import { SignToken } from "../../../utils/siginToken";

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: <string>process.env.GOOGLE_CLIENT_ID,
      clientSecret: <string>process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
 pages: {
    signIn: '/auth/dashbaord',
    signOut: '/auth/login',  
    error: '/auth/error',   
    newUser: '/auth/new-user'
  }
 callbacks: {
    async signIn({ user, account, profile}) {
    const response = await axios.post(
        "http://localhost:9000/v1/auth/userExists",
        { email: profile.email }
      );
      if (response && response.data?.value === true) {        
          return true;
      } else {
        const data = {
          firstName: profile.given_name,
          lastName: profile.family_name,
          email: profile.email,
          profileUrl: profile.picture,
        };
        const response = await axios.post(
          "http://localhost:9000/v1/auth/signup",
          data
        );
        return true;
      }
    },
    async jwt({ token, user, account }) {
      if (account) {         
        const userLoggedIn = await SignToken(user?.email as string);
        token.loggedUser = userLoggedIn;
      }
      return token;
    },
    async session({ session, token, user }) {
      session.loggedUser = token.loggedUser;
      return session;
    },
  },
});

Check user login state with the useSession() Hook

We need to get the login state of our users and render user details on the frontend of our app. This can be easily achieved by using a Next.js feature called custom app. Then, we’ll wrap our component in a Provider.

Create an _app.js file in your pages directory (if it doesn’t already exist) and add the following code:

import { SessionProvider } from "next-auth/react"
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return (
    <SessionProvider session={pageProps.session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}

export default MyApp

By wrapping our component in a Session Provider, we enable session state to be shared between pages. This, in turn, will preserve our state during page navigation, improve performance, and reduce network traffic.

User Sign In / Sign Out Buttons

Next, open the components/Header.js file and import useSession, signIn, and signOut from next-auth/client:

import { useSession, signIn, signOut } from 'next-auth/react'

useSession will be used to manage the sign in and sign out state of our users, while signIn and signOut will be used to perform the login and logout features in our app.

Let’s make use of the useSession Hook:

const { data: session } = useSession();

The session will return the user’s details. Let’s use the details returned to conditionally render a sign in and sign out button.

With in the body of our Header.js file we would append the signin / signout conditional logic:

<div className='header'>
      <Link href='/'>
        <a className='logo'>NextAuth.js</a>
      </Link>
           {session && <a href="#" onClick={handleSignout} className="btn-signin">Sign out</a>  } 
           {!session && <a href="#" onClick={handleSignin}  className="btn-signin">Sign in</a>  } 
    </div>

We need to create the handleSignin and handleSignout methods to enable our users to sign in and sign out:

const handleSignin = (e) => {
      e.preventDefault()
      signIn()
  }    
const handleSignout = (e) => {
      e.preventDefault()
      signOut()
    }

Our browser display once after the click of the sigin button:

Image description

Your Header.js should now look like this:

import Link from 'next/link'
import { useSession, signIn, signOut } from 'next-auth/react'

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

  const handleSignin = (e) => {
    e.preventDefault()
    signIn()
  }

  const handleSignout = (e) => {
    e.preventDefault()
    signOut()
  }

  return (
    <div className='header'>
      <Link href='/'>
        <a className='logo'>NextAuth.js</a>
      </Link>
      {session && <a href="#" onClick={handleSignout} className="btn-signin">Sign out</a>  }
      {!session && <a href="#" onClick={handleSignin}  className="btn-signin">Sign in</a>  }
    </div>
  )
}

Retrieve and display user information

Now, onto our pages/index.js. We need to display and conditionally render user details based on their login details. First, we have to import the useSession hook from next-auth.

So, replace your index.js with the following content:

import Head from 'next/head'
import Header from '../components/Header'
import styles from '../styles/Home.module.css'
import { useSession } from 'next-auth/react'

export default function Home() {
  const { data: session, status } = useSession()
  const loading = status === "loading"

  return (
    <div className={styles.container}>
      <Head>
        <title>Nextjs | Next-Auth</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />
      <main className={styles.main}>
        <h1 className={styles.title}>Authentication in Next.js app using Next-Auth</h1>
        <div className={styles.user}>
           {loading && <div className={styles.title}>Loading...</div>}
           {
            session &&
              <>
               <p style={{ marginBottom: '10px' }}> Welcome, {session.user.name ?? session.user.email}</p> <br />
               <img src={session.user.image} alt="" className={styles.avatar} />
              </>
            }
           {
            !session &&
              <>
               <p className={styles.title}>Please Sign in</p>

              </>
           }
         </div>
      </main>
    </div>
  )
}

Conclusion

Through this article we have been able to understand the development flow process of google authentication using NextAuth library. We further explained how we could interact with a custom backend API using axios as well as JSON web token for authorizing users.

if you did like to know more about Next.js kindly checkout their documentation at https://nextjs.org/docs

more details on NextAuth.js, how to integrate other authentication Strategies like GitHub, Twitter and others can be seen in their documentation at: https://next-auth.js.org/getting-started/introduction


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Ifenna Monanu


Print Share Comment Cite Upload Translate Updates
APA

Ifenna Monanu | Sciencx (2022-10-22T09:17:04+00:00) Building Google-JWT Authentication with NextAuth. Retrieved from https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/

MLA
" » Building Google-JWT Authentication with NextAuth." Ifenna Monanu | Sciencx - Saturday October 22, 2022, https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/
HARVARD
Ifenna Monanu | Sciencx Saturday October 22, 2022 » Building Google-JWT Authentication with NextAuth., viewed ,<https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/>
VANCOUVER
Ifenna Monanu | Sciencx - » Building Google-JWT Authentication with NextAuth. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/
CHICAGO
" » Building Google-JWT Authentication with NextAuth." Ifenna Monanu | Sciencx - Accessed . https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/
IEEE
" » Building Google-JWT Authentication with NextAuth." Ifenna Monanu | Sciencx [Online]. Available: https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/. [Accessed: ]
rf:citation
» Building Google-JWT Authentication with NextAuth | Ifenna Monanu | Sciencx | https://www.scien.cx/2022/10/22/building-google-jwt-authentication-with-nextauth/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.