In this tutorial, we'll be building a Next.js application and authenticate that with Strapi and NextAuth.
Strapi is the leading open-source headless CMS. It's 100% Javascript, fully customizable, and developer-first. I've been using Strapi for some of my Open Source projects, and the developer experience is excellent. It has helped me build prototypes and products much faster.
Next.js is a React framework for building Server-side rendered applications. It has many features with a good developer experience and supports TypeScript out of the box.
NextAuth is an authentication library built for use with Next.js. It's easy, fast, and secure.
The code for this tutorial is available on GitHub.
Let's start by creating a new Next.js application. We can create a Next.js application using the following command:
1 yarn create next-app
The above command will ask for the name of the project. We'll call it “frontend”.
Once the setup of the project and installing all dependencies are complete, we can go inside the frontend directory and start the application using the following command:
1 cd frontend && yarn dev
The above command will start the application on http://localhost:3000/.
In this section, we'll create a new Strapi application using Docker. More information about how to create a new Strapi application using Docker can be obtained from their repository. We'll use Postgres to do this.
We can create a new directory and name it “backend” in the root of our project. We can create a new file docker-compose.yml
inside the backend directory with the following content:
1# backend/docker-compose.yml
2
3version: '3'
4services:
5 strapi:
6 container_name: strapi
7 image: strapi/strapi
8 environment:
9 - DATABASE_CLIENT=postgres
10 - DATABASE_HOST=db
11 - DATABASE_PORT=5432
12 - DATABASE_NAME=strapi
13 - DATABASE_USERNAME=strapi
14 - DATABASE_PASSWORD=strapi
15 ports:
16 - 1337:1337
17 volumes:
18 - ./app:/srv/app
19 depends_on:
20 - db
21 db:
22 container_name: postgres
23 image: postgres
24 restart: always
25 volumes:
26 - ./db:/var/lib/postgresql/data
27 ports:
28 - 5432:5432
29 environment:
30 POSTGRES_USER: strapi
31 POSTGRES_PASSWORD: strapi
32 POSTGRES_DB: strapi
Now, we need to start Docker and run the following command inside the backend directory to create our new Strapi application:
1 docker-compose up
/srv/app
folder of the container. If there is nothing, it will run the Strapi new command in the container /srv/app
folder.Once the setup of our Strapi application is complete, we'll be able to view the administration panel at http://localhost:1337/admin.
Next, we'll need to create our first administrator in order to log into the Strapi administration panel:
Once we create our first administrator, we'll be logged into the Strapi administration panel:
In this section, we'll create a new Google OAuth Client and integrate it into Next.js and Strapi applications.
First, we'll need to visit the Google Developer Console to create a new OAuth Client and copy the credentials (Client ID and Client Secret) in our frontend/**.env**
file:
http://localhost:3000/api/auth/callback/google
http://localhost:1337/connect/google/callback
Once we click on the save button, we'll get our new OAuth Client's credentials. We can create a new file .env
inside our frontend directory to store all the credentials:
1// frontend/.env
2
3NEXT_PUBLIC_API_URL=http://localhost:1337
4NEXT_PUBLIC_DATABASE_URL=postgres://strapi:strapi@localhost:5432/strapi?synchronize=true
5NEXTAUTH_URL=http://localhost:3000
6GOOGLE_CLIENT_ID="12345.apps.googleusercontent.com"
7GOOGLE_CLIENT_SECRET="1234-567-9"
You'll need to replace the values of GOOGLE_CLIENT_ID
and GOOGLE_CLIENT_SECRET
with the values of your new Google OAuth Client.
Next, let's add these credentials to our Strapi application as well. In the Strapi administration panel, we need to add the Google OAuth Client credentials and enable the Google provider. The values can be added in the Providers menu inside the Settings tab.
We need to enter the Client ID and Client Secret in the Google provider and enable that:
In this section, we'll be installing and integrating NextAuth.
We can run the following command inside our frontend directory to install NextAuth as a dependency:
1 yarn add next-auth
Next, we'll need to create a new file, ...nextauth.js
, inside frontend/pages/api/auth
directory with the following content:
1// frontend/pages/api/auth/[...nextauth].js
1 import NextAuth from "next-auth";
2 import Providers from "next-auth/providers";
3
4 const options = {
5 providers: [
6 Providers.Google({
7 clientId: process.env.GOOGLE_CLIENT_ID,
8 clientSecret: process.env.GOOGLE_CLIENT_SECRET,
9 }),
10 ],
11 database: process.env.NEXT_PUBLIC_DATABASE_URL,
12 session: {
13 jwt: true,
14 },
15 callbacks: {
16 session: async (session, user) => {
17 session.jwt = user.jwt;
18 session.id = user.id;
19 return Promise.resolve(session);
20 },
21 jwt: async (token, user, account) => {
22 const isSignIn = user ? true : false;
23 if (isSignIn) {
24 const response = await fetch(
25 `${process.env.NEXT_PUBLIC_API_URL}/auth/${account.provider}/callback?access_token=${account?.accessToken}`
26 );
27 const data = await response.json();
28 token.jwt = data.jwt;
29 token.id = data.user.id;
30 }
31 return Promise.resolve(token);
32 },
33 },
34 };
35
36 const Auth = (req, res) =>
37 NextAuth(req, res, options);
38
39 export default Auth;
In the NextAuth callback function, we're calling the Strapi Authentication API endpoint. We're storing the JWT (token.jwt
) and user ID (data.user.id
) from the data that the Strapi API sends us.
In this way, we can understand which user is currently authenticated.
We can get the details of the authenticated users from the [getSession](https://next-auth.js.org/getting-started/client#getsession)
function of NextAuth. If the getSession
function doesn't return us any details, we can assume that the user is not authenticated.
We'll also need to add the pg package, so that NextAuth can connect to our database directly. We can install that package using the following command from our frontend directory:
yarn add pg
We can verify whether NextAuth is working with our application by updating our frontend/pages/index.js
with the following content:
1// frontend/pages/index.js
1 import { getSession, signIn, signOut } from "next-auth/client";
2 import Head from 'next/head';
3 import Link from "next/link";
4 import React from "react";
5
6 const IndexPage = ({
7 session,
8 }) => {
9 const signInButtonNode = () => {
10 if (session) {
11 return false;
12 }
13
14 return (
15 <div>
16 <Link href="/api/auth/signin">
17 <button
18 onClick={(e) => {
19 e.preventDefault();
20 signIn();
21 }}
22 >
23 Sign In
24 </button>
25 </Link>
26 </div>
27 );
28 };
29
30 const signOutButtonNode = () => {
31 if (!session) {
32 return false;
33 }
34
35 return (
36 <div>
37 <Link href="/api/auth/signout">
38 <button
39 onClick={(e) => {
40 e.preventDefault();
41 signOut();
42 }}
43 >
44 Sign Out
45 </button>
46 </Link>
47 </div>
48 );
49 };
50
51 if (!session) {
52 return (
53 <div className="hero">
54 <div className="navbar">
55 {signOutButtonNode()}
56 {signInButtonNode()}
57 </div>
58 <div className="text">
59 You aren't authorized to view this page
60 </div>
61 </div>
62 )
63 }
64
65 return (
66 <div className="hero">
67 <Head>
68 <title>Index Page</title>
69 </Head>
70 <div className="navbar">
71 {signOutButtonNode()}
72 {signInButtonNode()}
73 </div>
74 <div className="text">
75 Hello world
76 </div>
77 </div>
78 );
79 };
80
81 export const getServerSideProps = async ({ req }) => {
82 const session = await getSession({ req });
83 return {
84 props: {
85 session,
86 },
87 };
88 };
89
90 export default IndexPage;
Now, if we visit http://localhost:3000/, we should be able to view the following screen:
We can log in using our Gmail account once we click on the Sign In button. Once we're signed in, we should be able to view the following screen on http://localhost:3000/:
All the details of the authenticated user are present in the session prop of the page:
We can show the details of the authenticated user by fetching the details from the session prop. Also, the authenticated user will now be visible in the Strapi administration panel:
In this tutorial, we learned how we can authenticate a Next.js application using Strapi and NextAuth. We've worked with the REST API that Strapi provides out of the box. However, a similar solution can also be implemented for using GraphQL.
The code for this tutorial is available on GitHub. I've also created a boilerplate for using Strapi with Next.js. It'll help you get up and running with Strapi and Next.js really quickly. Feel free to give it a try.
Get started with Strapi by creating a project using a starter or trying our live demo. Also, consult our forum if you have any questions. We will be there to help you.
Nirmalya is a Developer, who likes designing, writing and building Open Source stuffs.