Next Auth: A Practical Guide to Modern Authentication

Next Auth: A Practical Guide to Modern Authentication

What is next auth?

NextAuth is a library that lets you do authentication in Next.js

Can you do it w/o next-auth - Yes

Should you - Probably not!

Popoular choices while doing auth include -

  1. External provider -

    1. https://auth0.com/

    2. https://clerk.com/

    3. Firebase auth

  1. In house using cookies
  1. NextAuth

Why not use JWT + localstorage?

Next.js is slightly different from React + Express apps

Express app

NextJS app

NextAuth

Nextjs lets you add Authentication to your Next.js app

  1. It supports various providers

    1. Login with email

      1. Login with google

      2. Login with facebook

Catch all routes

If you want to add a single route handler for

  1. /api/auth/user
  1. /api/auth/random
  1. /api/auth/123
  1. /api/auth/...

You can create a catch all route

  1. Create a simple next.js app
npx create-next-app@latest
  1. Create app/api/auth/[...nextauth]/route.ts
import { NextRequest, NextResponse } from "next/server"

export function GET(req: NextRequest) {
    return NextResponse.json({
        message: "Handler"
    })
}
  1. Try going to a few endpoints
http://localhost:3000/api/auth/signin
http://localhost:3000/api/auth/123
http://localhost:3000/api/auth/random/random2
  1. Try logging the sub-route you’re at
import { NextRequest, NextResponse } from "next/server"

export function GET(req: NextRequest, { params }: { params: { nextauth: string[] } }) {
    console.log(params.nextauth[0])
    return NextResponse.json({
        message: "Handler"
    })
}

Give NextAuth access to a catch-all

Ref https://next-auth.js.org/configuration/initialization#route-handlers-app

  1. Create /api/auth/[…nextauth]/route.ts
  1. Install next-auth
npm install next-auth
  1. Updated handler
import NextAuth from "next-auth"

const handler = NextAuth({
  ...
})

export { handler as GET, handler as POST }
  1. Adding providers - There are three broad types of providers

    1. OAuth (Login with google)

    2. Email (Passwordless Email login via email OTP)

    3. Credentials (your own strategy)

Let’s them one by one

Credentials provider

This lets you create your own authentication strategy

For example

  1. Email + Password
  1. Phone number
  1. Login with Metamask

Steps to follow

  1. Add a credentials provider
import NextAuth from "next-auth"
import CredentialsProvider from 'next-auth/providers/credentials';

const handler = NextAuth({
  providers: [
    CredentialsProvider({
        name: 'Credentials',
        credentials: {
          username: { label: 'email', type: 'text', placeholder: '' },
          password: { label: 'password', type: 'password', placeholder: '' },
        },
        async authorize(credentials: any) {

            return {
                id: "user1"
            };
        },
      })
  ],
  secret: process.env.NEXTAUTH_SECRET
})

export { handler as GET, handler as POST }
  1. Add NEXTAUTH_URL to .env
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=password_nextauth
  1. Update App.tsx to have a simple Appbar
"use client";
import { signIn, signOut } from "next-auth/react"

export const Appbar = () => {
    return <div>
    <button onClick={() => signIn()}>Signin</button>
    <button onClick={() => signOut()}>Sign out</button>
  </div>
}Click
  1. Add providers.tsx
'use client';
import React from 'react';
import { SessionProvider } from 'next-auth/react';

export const Providers = ({ children }: { children: React.ReactNode }) => {
  return (
    <SessionProvider>
      {children}
    </SessionProvider>
  );
};
  1. Wrap layout with Providers
import { Providers } from "./provider";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Providers>
          {children}
        </Providers>
      </body>
    </html>
  );
}
  1. Get the user details in the top level page.tsx (client component)
"use client"

import { useSession } from "next-auth/react";

export default function Home() {
  const session = useSession();
  return (
    <div>
      {JSON.stringify(session.data?.user)}
    </div>
  );
}
  1. Get the user details on the server (server component)
import { getServerSession } from "next-auth"

async function getUser() {
  const session = await getServerSession();
  return session;
}

export default async function Home() {
  const session = await getUser();

  return (
    <div>
      {JSON.stringify(session?.user?.name)}
    </div>
  );
}
  1. Get user in an api route (/api/user)
import { getServerSession } from "next-auth"
import { NextResponse } from "next/server";

export async function GET() {
    const session = await getServerSession();

    return NextResponse.json({
        name: session?.user?.name
    })
}
  1. Persist more data (user id) (Ref https://next-auth.js.org/getting-started/example#using-nextauthjs-callbacks) (Ref https://next-auth.js.org/configuration/callbacks)
  callbacks: {
      jwt: async ({ user, token }: any) => {
          if (user) {
              token.uid = user.id;
          }
          return token;
      },
    session: ({ session, token, user }: any) => {
        if (session.user) {
            session.user.id = token.uid
        }
        return session
    }
  },
  1. Move auth config to lib/auth.tshttps://github.com/nextauthjs/next-auth/issues/7658#issuecomment-1683225019
import CredentialsProvider from 'next-auth/providers/credentials';

export const NEXT_AUTH_CONFIG = {
    providers: [
      CredentialsProvider({
          name: 'Credentials',
          credentials: {
            username: { label: 'email', type: 'text', placeholder: '' },
            password: { label: 'password', type: 'password', placeholder: '' },
          },
          async authorize(credentials: any) {

              return {
                  id: "user1",
                  name: "asd",
                  userId: "asd",
                  email: "ramdomEmail"
              };
          },
        }),
    ],
    secret: process.env.NEXTAUTH_SECRET,
    callbacks: {
        jwt: async ({ user, token }: any) => {
        if (user) {
            token.uid = user.id;
        }
        return token;
        },
      session: ({ session, token, user }: any) => {
          if (session.user) {
              session.user.id = token.uid
          }
          return session
      }
    },
  }

Adding Google Provider

Ref https://next-auth.js.org/providers/google

Adding Github provider

Ref - https://next-auth.js.org/providers/github