Authentication Using NextJS 13 / React & Amazon AWS Cognito
Tags: aws, nextjs, identityserviceproviders
Published: 2023-03-06 00:00:53

Building a progressive web app? NextJS is the perfect choice for building a one-hundred percent self-contained web app. It just makes like so much easier with built-in filesystem-based routing, automatic image optimization (when hosting on Vercel), and a fully-functional built-in express-based API. You can literally spin up an app with `create-next-app` in seconds!

Authentication is always a grave concern when it comes to building production web apps, especially when there's potentially sensitive user data involved. Most large companies have a single-sign-on (SSO) service that is typically integrated with their central user directory (i.e. Active Directory), but this requires a Microsoft Azure account and an insane amount of configuration, and is not ideal for small- to medium-sized businesses that don't need local workstation logins to be integrated with web-based apps.

There is a wonderful library for NextJS called `next-auth`, which aggregates several identity providers with your NextJS app. The list of providers is always growing. Currently, in 2023, the list includes: 42 School, Amazon Cognito, Apple, Atlassian, Auth0, Authentik, Azure Active Directory, Azure Active Directory B2C, Battle.net, Box, BoxyHQ SAML, Bungie, Coinbase, Discord, Dropbox, DuendeIdentityServer6, EVE Online, Facebook, FACEIT, Foursquare, Freshbooks, FusionAuth, GitHub, GitLab, Google, HubSpot, IdentityServer4, Instagram, Kakao, Keycloak, LINE, LinkedIn, Mail.ru, Mailchimp, Medium, Naver, Netlify, Okta, OneLogin, Osso, Osu!, Patreon, Pinterest, Pipedrive, Reddit, Salesforce, Slack, Spotify, Strava, Todoist, Trakt, Twitch, Twitter, United Effects, VK, Wikimedia, WordPress.com, WorkOS, Yandex, Zitadel, Zoho, and Zoom.

In this guide, I'm going to show you how to create a NextJS app complete with a next-auth-based authentication flow, and using AWS Cognito as the identity provider. If you are unfamiliar with how to create an AWS Cognito user pool, please my previous article, [*How to Create an Amazon AWS Cognito User Pool*](https://designly.biz/blog/post/how-to-create-an-amazon-aws-cognito-user-pool).

This article is part of a larger public coding project called *next-letter*, an open-source bulk mailing app that I am creating (more articles to follow). The code samples provided are from [that repository](https://github.com/designly1/next-letter). So, if you want to just clone it and play around, please feel free to do so.

Ok, let's begin. I'm going to assume you already have spun up a Next app using `create-next-app`. The first thing we want to do is install `npm i next-auth`. Next, we need to create an API route for next-auth to handle our sign-in and sign-out requests:

```js
// @/pages/api/[...nextauth].js

import NextAuth from "next-auth"
import CognitoProvider from "next-auth/providers/cognito"

export const authOptions = {
    providers: [
        CognitoProvider({
            clientId: process.env.NEXT_PUBLIC_COGNITO_CLIENT_ID,
            clientSecret: process.env.NEXT_PUBLIC_COGNITO_CLIENT_SECRET,
            issuer: process.env.NEXT_PUBLIC_COGNITO_ISSUER,
        }),
    ],
    theme: {
        colorScheme: "dark", // "auto" | "dark" | "light"
        brandColor: "#000", // Hex color code
        logo: "https://cdn.designly.biz/images/designly-logo-300.webp", // Absolute URL to image
        buttonText: "#fff" // Hex color code
    }
}

export default NextAuth(authOptions)
```

Ok, let's look at this code. We pull the `CognitoProvider` from next-auth and provide it with `clientId`, `clientSecret`, and `issuer`. The client secret is something you created when you created your user pool. The client ID can be found under the app integration tab of your user pool in the console:

![finding your client ID](https://cdn.designly.biz/blog_files/authentication-using-nextjs-13-react-and-amazon-aws-cognito/image01.jpg)

As for `issuer`, it should be formatted as follows: `https://cognito-idp.[region].amazonaws.com/[user_pool_id]`, where `[region]` is your AWS region slug (e.g. us-east-2), and `[user-pool-id]` is your... well... your user pool ID. You can find that here:

![finding your user pool ID](https://cdn.designly.biz/blog_files/authentication-using-nextjs-13-react-and-amazon-aws-cognito/image02.jpg)

As for the theme configuration, that's part of the larger scope of next-letter, and is totally optional.

Next, we need to edit `_app.js` to wrap our app in the `SessionProvider`:

```js
// @/pages/_app.js

import '@/styles/globals.scss'
import { Inter } from 'next/font/google'
import { SessionProvider } from "next-auth/react"
import { ThemeProvider, createTheme } from '@mui/material/styles';
import NextNProgress from "nextjs-progressbar";

const inter = Inter({ subsets: ['latin'] });

const darkTheme = createTheme({
  palette: {
    mode: 'dark',
  },
});

export default function App({
  Component,
  pageProps: { session, ...pageProps }
}) {
  return (
    <SessionProvider session={session}>
      <NextNProgress />
      <ThemeProvider theme={darkTheme}>
        <Component className={inter.className} {...pageProps} />
      </ThemeProvider>
    </SessionProvider>
  )
}
```

Now, we need a way to authenticate users. The easiest way to accomplish that is to create a  layout that checks to see if the user is logged and conditionally renders components based on that. We'll call it `AuthLayout`:

```js
// @/components/Layout/AuthLayout.js

import React, { useEffect } from 'react'
import Head from 'next/head'
import MenuBar from './MenuBar'
import LoginScreen from '../Auth/LoginScreen'

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

export default function AuthLayout({ children, title }) {
    const { data: session } = useSession();

    let titleConcat = "nLetter";
    if (title) titleConcat += " | " + title;

    if (session) {
        return (
            <>
                <Head>
                    <title>{titleConcat}</title>
                </Head>
                <main className="h-screen bg-bg text-white/90">
                    <header className="mb-10">
                        <MenuBar />
                    </header>
                    <div className="flex flex-col">
                        {children}
                    </div>
                </main>
            </>
        )
    } else {
        return <LoginScreen />
    }
}
```

And if you're curious, here's the code for `LoginScreen`:

```js
// @/components/Auth/LoginScreen.js

import React from 'react'
import Link from 'next/link'

export default function LoginScreen() {
    return (
        <div className="h-screen bg-bg flex flex-col text-white/90">
            <div className="m-auto bg-bg-700 px-4 md:px-10 py-20 md:rounded-3xl w-full md:w-fit flex flex-col">
                <h1 className="text-2xl text-center mb-10">You Are Not Logged In</h1>
                <Link className="btn-primary" href="/api/auth/signin">Sign In</Link>
            </div>
        </div>
    )
}
```

It's basically just a link to the sign-in API route.

That's just about it! If you want to create a sign-out button, simply link to `/api/auth/signout`. It's that simple! Be sure to stay tuned for the follow-up article on how to create a bulk mailing app using NextJS and AWS Simple Email Service (SES).

Thank you for taking the time to read my article and I hope you found it useful (or at the very least, mildly entertaining). For more great information about web dev, systems administration and cloud computing, please read the [Designly Blog](https://designly.biz/blog). Also, please leave your comments! I love to hear thoughts from my readers.

I use [Hostinger](https://hostinger.com?REFERRALCODE=1J11864) to host my clients' websites. You can get a business account that can host 100 websites at a price of $3.99/mo, which you can lock in for up to 48 months! It's the best deal in town. Services include PHP hosting (with extensions), MySQL, Wordpress and Email services.

Looking for a web developer? I'm available for hire! To inquire, please fill out a [contact form](https://designly.biz/contact).