~/javascript/next-js-strapi-authentication-credentials-jwt-next-auth.md

Authenticating Strapi backend with Next.js and next-auth using credentials and jwt

3 min readApril 18, 2021by Gorav Singal#javascript#nextjs#reactjs
// TL;DR

Authenticate users from Next.js to Strapi using next-auth's CredentialsProvider with email/password, then store and use the JWT token for subsequent authenticated REST API calls.

Authenticating Strapi backend with Next.js and next-auth using credentials and jwt

Introduction

Strapi is a backend system provides basic crud operations with customizable content types, and auto-magically provide its Rest APIs. Next.js is an excellent framework over React.js which uses capability of React.js and provides SEO benefits by rendering pages at server side.

In this post, I will authenticate a registered user fron next.js to strapi and use its Rest API with jwt for further authenticated operations.

What We Will Learn

  • Render a pre-built login form, and its corresponding actions
  • authentication will be with strapi email/password system.
  • Signout functionality
  • Save jwt in session
  • Use jwt tokens in rest-apis for authenticated calls

Pre-requisites

  1. Have a registered and active user in strapi.
  2. Have a content type.
  3. This content type is only usable by authenticated users.

NPM Libraries required

  • axios
  • next
  • next-auth
  • react
  • react-dom

Using Next-auth

Create a file named /pages/api/auth/[...nextauth].js

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import axios from 'axios'

const options = {
  providers: [
    Providers.Credentials({
      name: 'Credentials',
      credentials: {
        email: { label: "Email", type: "text", placeholder: "[email protected]" },
        password: {  label: "Password", type: "password" }
      },
    async authorize(credentials) {
        try {
          const { data } = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/auth/local`, {
            identifier: credentials.email,
            password: credentials.password
          });
          if (data) {
            return data;
          }
          else {
            return null;
          }
        } catch (e) {
          // console.log('caught error');
          // const errorMessage = e.response.data.message
          // Redirecting to the login page with error message          in the URL
          // throw new Error(errorMessage + '&email=' + credentials.email)
          return null;
        }
      }
    })
  ],

  session: {
    jwt: true,
  },

  callbacks: {
    // Getting the JWT token from API response
    jwt: async (token, user, account) => {
      const isSignIn = user ? true : false;
      if (isSignIn) {
        token.jwt = user.jwt;
        token.id = user.user.id;
        token.name = user.user.username;
        token.email = user.user.email;
      }
      return Promise.resolve(token);
    },
  
    session: async (session, user) => {
      session.jwt = user.jwt;
      session.id = user.id;
      return Promise.resolve(session);
    },
  }
}

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

Edit _app.js

import { Provider } from 'next-auth/client'

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

export default MyApp

.env.local Environment variable file

NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:1337

Summary so far

We have configured our Next.js frontend so that we will be able to:

  • Configured a credential provider in next-auth with strapi backend.
  • Configured action to be performed when user submit Login form.
  • Authentication rest call to strapi backend and upon successful authentication, save jwt token to session
  • Easily identify if a user has logged-in or not.
  • Easily get jwt token, if user has authenticated.

Playground with Sign-in/Sign-out and Authenticated Rest call

import Head from 'next/head'
import { signIn, signOut, useSession, getSession } from 'next-auth/client'
import axios from 'axios'

export default function Home(initialData) {
  const [ session, loading ] = useSession()
  return (
    <div className='container'>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
        <link rel="stylesheet" href="/style.css"/>
      </Head>

      <h1>Auth Test</h1>

      <div>
          {!session && <>
          Not signed in <br/>
          <button onClick={() => signIn()}>Sign in</button>
        </>}
        {session && <>
          Signed in as {session.user.email} <br/>
          <button onClick={() => signOut()}>Sign out</button>
        </>}
      </div>

      <h1>Content...</h1>

      <div>
        {initialData.journals && initialData.journals.map((each, index) => {
          return(
            <div key={index}>
              <h3>{each.Title}</h3>
              <p>{each.Journal}</p>
            </div>
          )
        })}
      </div>
      
    </div>
  )
}

export async function getServerSideProps({req}) {
  let headers = {}
  const session = await getSession({ req });
  if (session) {
    headers = {Authorization: `Bearer ${session.jwt}`};
  }
  let journals = [];
  try {
    let {data } = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/journals`, {
      headers: headers,
    })
    journals = data;
  } catch (e) {
    console.log('caught error');
    journals = [];
  }
  
  return {props: {journals: journals}}  
}

Understanding getServerSideProps

First we are checking, if a session is there. If it is, then we are fetching jwt token from session and using it to pass in a Rest call for getting a content type.

Detect User is Logged-in or not

const [ session, loading ] = useSession()

{!session && <>
          Not signed in <br/>
          <button onClick={() => signIn()}>Sign in</button>
        </>}

Also see How to use Next-auth in Client side vs Server Side

The magic methods of next-auth gives you methods to get session information. You just need to check, if session is present.

If there is no session, it gives you a signin action and when you click on this button, you will be presented with the ready-made form.

$ subscribe --weekly

One deep dive a week, in your inbox

System design, AI engineering, trading systems and interview prep. Tested code, real numbers. Free, and you can unsubscribe any time.

or read it on gyanbyte.substack.com

Share

Related Posts

Nextjs - Fixing Loading External Images and Issue of Url Paramater Not Allowed

Nextjs - Fixing Loading External Images and Issue of Url Paramater Not Allowed

Introduction In this post, we will see How to load external images to your next…

Request Entity Too Large(413) - Uploading File with Formdata with Axios and Custom header

Request Entity Too Large(413) - Uploading File with Formdata with Axios and Custom header

Introduction I was trying to upload images to my backend using rest APIs. I was…

Nextjs - How to Build Next.js Application on Docker and Pass API Url

Nextjs - How to Build Next.js Application on Docker and Pass API Url

Introduction In this post we will see: How to prepare a docker image for your…

Nextjs - How to Redirect to Another Page and Pass State

Nextjs - How to Redirect to Another Page and Pass State

Introduction We have a page, for example a . And, upon submission we would want…

How to use Draft.js WYSWYG with Next.js and Strapi Backend, Edit/Update Saved Article

How to use Draft.js WYSWYG with Next.js and Strapi Backend, Edit/Update Saved Article

Introduction This post is in contuation of our previous post: How to use Draft…

Create Articles with Image Upload Using Draft.js, Next.js, and Strapi

Create Articles with Image Upload Using Draft.js, Next.js, and Strapi

Introduction In this post, we will use in Next.js with strapi. And, we will…

Latest Posts

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

How to Stay Valuable as a Software Engineer in the AI Era: A Practical Playbook

In the previous article I argued that AI is not killing software engineering…

How to Review AI-Generated Code: A Checklist for Catching What the Model Missed

How to Review AI-Generated Code: A Checklist for Catching What the Model Missed

A few months back I approved a PR that was almost entirely AI-generated. It…

How to Debug AI-Generated Code: Understand First, Fix Last

How to Debug AI-Generated Code: Understand First, Fix Last

In the last article I wrote about reviewing AI code before it goes in. This one…

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

The Future of Software Engineering: What AI Changes, What Stays, and Where the Jobs Go

Every few weeks somebody sends me a scary headline — “AI will replace all…

The Transactional Outbox Pattern: Never Lose an Event Again

The Transactional Outbox Pattern: Never Lose an Event Again

This is one of those patterns that looks like nothing until you get burnt once…

Event Sourcing, Explained: Store What Happened, Not Just the Final State

Event Sourcing, Explained: Store What Happened, Not Just the Final State

Here is a question I like to ask in interviews: your bank account shows a…