Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
October 22, 2022 09:17 am GMT

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 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 youre 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: '[email protected]';        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_projectscd my_projectsnpx 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 --saveoryarn 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 projects 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, dont 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:3000NEXT_PUBLIC_JWT_SECRET_KEY: <your jwt secret key>

Google Provider Initialization

Now, back to our app. Were 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, were importing NextAuth, which is our main package.

On line 2, were 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, were 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: '[email protected]';        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, well wrap our component in a Provider.

Create an _app.js file in your pages directory (if it doesnt 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.

Lets make use of the useSession Hook:

const { data: session } = useSession();

The session will return the users details. Lets 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="https://dev.to/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


Original Link: https://dev.to/ifennamonanu/building-google-jwt-authentication-with-nextauth-5g78

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To