Table of Contents
- Intro to Next.js authentication
- About NextAuth package
- Setting up NextAuth
- Authentication with different providers (ex: Google, Github, Facebook, LinkedIn etc)
- Handling SignIn, SignOut functionalities
- Securing Pages
Intro to Next.js authentication
- The internet is a magical place where you can connect to people from any part of the world, but it is also a place where criminals lurk and try to steal your identity, your money, and your data.
- One of the biggest problems with the internet is identity theft but it can be prevented with authentication.
- Compare to React.js where we only have to deal with Client-Side authentication, In Next.js we have to authenticate not only Client-side but also Server-Side and API routes.
- In this blog, I'm going to implement authentication with the help of the Next-auth package in Next.js.
About NextAuth package
- NextAuth in my opinion is the best way to handle authentication in Next.js till now.
- It's because NextAuth provides built-in support for Google, Facebook, Auth0, Apple, and many more.
- If you don't want to authenticate your users with these providers, NextAuth provides Built-in email / passwordless / magic link authentication too.
- Also If you want to persist the user then also you can do it by bringing your own Databases like MySQL, Postgres, MSSQL, MongoDB, etc.
- It also provides CSRF Token validation JWT with JWS / JWE / JWK / JWK, Tab syncing, auto-revalidation etc.
Setting up NextAuth
- Before moving ahead fork the below repository for getting the starter project.
- .
- After forking run this project, you'll see something like this -
- Okay, so now that we've done all the setup let's install NextAuth package.
Installing NextAuth
npm i next-auth
Configuring Different Providers
- To implement different auth providers we have to first create a file inside
api/auth
called[...nextauth].js
- Now Import NextAuth and Providers
import NextAuth from "next-auth" import Providers from "next-auth/providers" // contains all the provider
- Now export default NextAuth
import NextAuth from "next-auth" import Providers from "next-auth/providers" export default NextAuth ({ // ... })
- This NextAuth has one property called providers in which we can define all the list of providers that we want to use.
- Let's add Github provider
export default NextAuth({ providers: [ Providers.GitHub({ clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, }), // You can add here multiple provider as per your requirement ], });
- To get the CLIENT_ID and CLIENT_SECRET, go to Profile Settings.
- Click on the Developer settings
- Click on the OAuth Apps tab and create a New OAuth App
- Now fill the application details and Register application.
- Now copy your CLIENT_ID and CLIENT_SECRET and paste it inside the
.env.local
file //.env.local GITHUB_CLIENT_ID=2ff72fc2ae451c063edc GITHUB_CLIENT_SECRET=60e9dc019d9d172b10662a66350cbfb16ff0d189
- Now if you run the application and type
http://localhost:3000/api/auth/signin
in the browser, you'll see one button named Sign in with Github - And if you click on this button, you will be redirected to GitHub authorization page.
- After authorizing, you will be then redirected to the HomePage.
- If you want to verify whether you are signed in or not, open Developer tool in chrome and go to Cookies under Application tab.
- If you see below to cookies then it means you've successfully signed in.
- Now if you want to signOut then simply put
http://localhost:3000/api/auth/signout
in the browser URL and you will see - - If you click on this button you will be redirected to HomePage and your Next-session cookie will be deleted automatically.
- Now we will not tell user to type this
/api/auth/signin
or/api/auth/signout
URL manually ,right? - We want to handle this authentication process on some kind of button click.
- So let's see how we can do signIn and signOut on button click.
Handling SignIn, SignOut on Button Click
- It's very simple, Import
signIn
andsignOut
fromnext-auth/client
package import {signIn, signOut} from "next-auth/client"
- Now simply call these two functions on
onClick
. And also don't forget to passhref
. function Navbar() { const handleSignIn = (e) => { e.preventDefault(); signIn(); }; const handleSignOut = (e) => { e.preventDefault(); signOut(); }; return ( <nav> <Link href="/"> <button>Home</button> </Link> <Link href="/dashboard"> <button>Dashboard</button> </Link> <Link href="/blog"> <button>Blog</button> </Link> <Link href="/api/auth/signin"> <button onClick={handleSignIn}>SignIn</button> </Link> <Link href="/api/auth/signout"> <button onClick={handleSignOut}>SignOut</button> </Link> </nav> ); } export default Navbar;
- And BOOM !!.
But if you see in the navbar, The SignIn button is still visible even if the user has signed in. And also the SignOut button is visible even if the user has not signed in.
- To solve this issue, we have to check if the user has signed in or not, and according to that information, we can conditionally hide or show these buttons.
- To check whether the user has signed in or not, NextAuth provides a hook called
useSession
. - This hooks returns two values -
- session object and
- loading flag
- If session is
null
that means the user has not signed in and we can show signIn button and hide signOut button and vice-versa. - First import
useSession
hookimport { signIn, signOut, useSession } from "next-auth/client";
And now conditionally render buttons
function Navbar() { const [session, loading] = useSession(); return ( <nav> <Link href="/"> <button>Home</button> </Link> <Link href="/dashboard"> <button>Dashboard</button> </Link> <Link href="/blog"> <button>Blog</button> </Link> {!loading && !session && ( <Link href="/api/auth/signin"> <button onClick={handleSignIn}>SignIn</button> </Link> )} {session && ( <Link href="/api/auth/signout"> <button onClick={handleSignOut}>SignOut</button> </Link> )} </nav> ); } export default Navbar;
Securing Pages
- Sometimes we want users to be authenticated to be able to access a particular page.
- To do that NextAuth provides a very handy hook called
getSession
. - This will return
null
if the user has not signed in, otherwise, return an object. - Let's secure our Dashboard page. We want our users to access the dashboard page if they are signed in, otherwise will be redirected to signIn page.
- Import packages
import { getSession, signIn } from "next-auth/client"; import { useEffect, useState } from "react";
function Dashboard() { const [loading, setLoading] = useState(true); useEffect(() => { const checkIsSignedIn = async () => { await getSession().then((session) => { if (!session) { // Not Signed In signIn(); } else { // Signed In setLoading(false); } }); }; checkIsSignedIn(); }, []); if (loading) { return ( <div className={styles.dashboard}> <h2>Loading</h2> </div> ); } return ( <div className={styles.dashboard}> <h1>Dashboard page</h1> </div> ); } export default Dashboard;
- Now if you click on the dashboard, you will be redirected to signIn page if you are not signed in otherwise to Dashboard page if signed in.
- Above we're securing our pages on the Client-Side. Now, what if we want to Secure pages on the Server-Side.
- We can use
getSession
hook to do this - import { getSession } from "next-auth/client"; import styles from "../styles/Blog.module.css";
function Blog({ data }) { return ( <div className={styles.blog}> <h1>{data}</h1> </div> ); } export default Blog;
export async function getServerSideProps(context) { const session = await getSession(context); // pass the context if (!session) { // redirecting to signIn page if not signed In return { redirect: { destination: "/api/auth/signin?callbackUrl=http://localhost:3000/blog", permanent: false, }, }; } return { props: { data: "Welcome to Blog page", }, }; }
- We are also dealing with APIs in Next.js.
- We can also secure our API routes using
getSession()
hook. - Goto your API route page, and import
getSession()
hook. - And paste the below code inside -
import { getSession } from "next-auth/client";
const userInfo = async (req, res) => { const session = await getSession({ req }); if (!session) { res.status(401).json({ error: "User not authenticated" }); } else { res.status(200).json({ user: session.user }); } }; export default userInfo;
- Now if you try to access
/api/userInfo
without signIn then you'll get a message - User not authenticated, otherwise a user data if signed in - That's it!! We've implemented authentication and also secured the pages and API routes in Next.js.
- You can go further and do many things with NextAuth as it provides so many features.
Wrapping up
- Finished Authentication :
- Thank you for reading. Hope you liked it.
- Comments and Feedbacks are welcomed ✍️