<- Back

what is next-auth

next-auth is lib that allows you to add authentication to your app.

Key things to know about next-auth are

  • Open source software
  • Works with various auth providers and auth strategies like OAuth 1.0, 1.0A, 2.0 and OpenID Connect
  • Works with various databases like postgress, ms sql, my sql, supabase, MongoDB and SQLite.
  • Supports both JSON Web Token sessions(session data stored in token at client side) and database sessions(session data is stored in database). Both approach can use cookies.
  • By default, JWT is used to persist user sessions. But you can use adapter or database strategy
  • Databases in NextAuth.js is optional. Database is required only when you want to persist user information and manage more complex user flows.Email sign in also requires database.
  • NextAuth.js stores access token and refresh token(sent by providers) in jwt or database if you are using database
  • Next Auth does not handle Access Token rotation for OAuth providers as of version 4.
  • Tokens are encrypted (JWE) and cookie larger than 4kb get split and reassembled upon parsing.

High level steps

  • create 2 environment variables - these variables are required in Production environment
NEXTAUTH_URL - This is for storing callback urls
NEXTAUTH_SECRET - This is used for security purpose
  • Configure auth options in pages/api/auth/[...nextauth].js

    • providers - array of providers like google, github, credentials. You can created client id and secret by going to google console and github developer settings. Callback urls look like https://www.softpost.org/api/auth/callback/google or https://www.softpost.org/api/auth/callback/github
    • secret - not rquired if you have set NEXTAUTH_SECRET
    • session - settings like strategy, maxAge, updateAge, generateSessionToken. Session strategy can be jwt.
    • jwt - You can verify and decode token using getToken method. import { getToken } from "next-auth/jwt"
    • pages - NextJS provides the default pages for sign in and sign out but you can add custom pages using this option
    • callbacks - Callbacks can be configured here. Callbacks are executed and return object when certain action is completed e.g. signIn, redirect, session, jwt etc
    • events - This is used for auditing purpose
    • theme - You can customise the theme of default pages here.
    • debug - Use this to enable debug messages for authentication and database operations.
    • logger - logger levels and send logs to specific sink
  • Adding providers Do not get overwhelmed by the number of options above. To get started, you will only need to add couple of providers like email, credentials or Oauth like google or github.

import CredentialsProvider from 'next-auth/providers/credentials'
import GitHubProvider from 'next-auth/providers/github'

export default (req,res) => NextAuth(req,res, {

providers : [
  GitHubProvider({
  clientId :  process.env.githubClientId,
  clientSecret : process.env.githubClientSecret
  }),
  CredentialsProvider({
    name: 'Credentials',
    credentials: {
      username: { label: "Username", type: "text", placeholder: "sagar" },
      password: {  label: "Password", type: "password" }
    },
    async authorize(credentials, req) {
      //here custom logic will go to check that user is in db and credentials are correct
      return {name:"sagar"}
    }
  })

  ],

  debug : true,
  secret : "auth-secrett",

  jwt : {

  }

})
  • Once providers are added, you can go to /api/auth/signin and boom! You will see Signin with GitHub abd Sign in with credentials options. Also make sure that github client id and secret are correct. Oauth details for github can be found at developer section in github settings.

  • That's it!! You are signed in now. Keep reading to know how to use session and token at client side and server side as well.

Detailed steps

  • Wrap the app component inside SessionProvider in _app.jsx
import { SessionProvider } from "next-auth/react"

export default function App({
  Component,
  pageProps: { session, ...pageProps },
}) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  )
}
  • Render Sign in buttons
import { signIn } from "next-auth/react"

export default () => <button onClick={() => signIn()}>Sign in</button>

Or you can go to provider page directly

export default () => (
  <button onClick={() => signIn("google")}>Sign in with Google</button>
)

Or you can sign in using email

import { signIn } from "next-auth/react"

export default ({ email }) => (
  <button onClick={() => signIn("email", { email })}>Sign in with Email</button>
)
  • Callback url
signIn('google', { callbackUrl: 'http://localhost:3000/bar' })
signIn('email', { email, callbackUrl: 'http://localhost:3000/foo' })
  • do not redirect
signIn('credentials', { redirect: false, password: 'password' })
signIn('email', { redirect: false, email: '[email protected]' })
  • Sign out and redirect
signOut({ callbackUrl: 'http://localhost:3000/xyz' })
const data = await signOut({redirect: false, callbackUrl: "/xyz"})

  • Check if someone is signed in using useSession() function in client side
import { useSession, signIn, signOut } from "next-auth/react"

export default function Component() {
const { data: session, status } = useSession()
  if (session) {
    return (
      <>
        Logged in as {session.user.email} <br />
        <button onClick={() => signOut()}>Sign out</button>
      </>
    )
  }
  return (
    <>
      Not logged in <br />
      <button onClick={() => signIn()}>Sign in</button>
    </>
  )
}
  • Check if Session is available on server side
import { getServerSession } from "next-auth/next"
import { authOptions } from "./auth/[...nextauth]"

export default async (req, res) => {
  const session = await getServerSession(req, res, authOptions)

  if (session) {
    res.send({
      content:
        "Signed in users land here",
    })
  } else {
    res.send({
      error: "Guests land here.",
    })
  }
}
  • Get session info as shown in below code

const { data: session, status } = useSession()

useSession() returns an object containing two values: data and status:

data: with values - Session / undefined / null. status: session states: "loading" | "authenticated" | "unauthenticated"

Sample session object is given below

{
  user: {
    name: string
    email: string
    image: string
  },
  expires: Date
}

The expires value in session object is rotated. Whenever the session is retrieved from the REST API, this value will be updated thus avoiding session expiry.

  • Refetch session from client side to keep session active
 <SessionProvider
      session={session}

      // Re-fetch session every 10 minutes
      refetchInterval={10 * 60}
      // Re-fetches session when window is focused
      refetchOnWindowFocus={true}
    >
      <Component {...pageProps} />
    </SessionProvider>

By default, session will end in 30 days. But you can roll session expiry, using refetchInterval. So client sends request to /api/auth/session after every 5 mins of inactivity and it will reset the expiry time.

  • verify token on server
import { getToken } from "next-auth/jwt"

const secret = process.env.NEXTAUTH_SECRET

export default async function handler(req, res) {
  const token = await getToken({ req, secret })
  console.log("JSON Web Token details", token)
  res.end()
}
  • Adapter If you want to store session and user data in database, you can use one of the adapters for databases like mongodb
import { MongoDBAdapter } from '@next-auth/mongodb-adapter'
import MongoClientPromise from '../../../lib/mongodb'

export default (req,res) => NextAuth(req,res, {
    adapter: MongoDBAdapter(MongoClientPromise),
}

What this does is that next auth will connect to mongodb database using MongoClientPromise and then store session and user data in it automatically. If you do not specify the adapter, data is not stored in database.

How next auth works behind the scene

  • When user visits website, front end checks if user session is active by calling useSession()
  • If session is not active, user is redirected to signIn page - /api/auth/signin
  • Signin page shows the sign in options as configured in /api/auth/[...nextauth]
  • When user logs in successfully, session is created.
  • If adapter is configured, session data is pushed to database. SessionToken is added in sessions table for the logged in user.
  • Session Token and XSRF token is sent to browser via cookie

Here is how the session table looks in db

  • user - Unique user of app. id,name,email,emailverified
  • account - User can have multiple accounts (in the context of provider). e.g. User can sign in using google and same email can be used to sign in using github as well. Columns in this table are id, userId, type, provider, providerAccountId, refresh_token, access_token, expires_at, token_type, scope, id_token, session_state, oauth_token_secret, oauth_token
  • session - A User can have multiple sessions. id, expires, sessionToken, userId
  • verification_token - identifier, token, expire

more details at https://next-auth.js.org/adapters/models

Cookies

In application tab of chrome dev tools, you will see below cookies

  • next-auth.callback-url
  • next-auth.csrf-token

Web development and Automation testing

solutions delivered!!