DEV Community

Louie Heaton
Louie Heaton

Posted on • Edited on

NextJS User Login & Registration (AWS Cognito)

The purpose of this project is to build a boilerplate for NextJS which will allow me to quickly start with a base which already has a login and register system built. Sounds great hey! P.S this project will be available as an open source on Github and any contributions are more than welcome!


Getting started

To begin with I started by creating just a basic NextJS using the NPX command.

npx create-next-app next-auth-boilerplate
Enter fullscreen mode Exit fullscreen mode

I created the project using typescript, eslint and left the rest as default. (I'm not using tailwind in this example).

After doing so I opened up the folder in VS Code, you can also do code next-auth-boilerplate as well to make it quicker 😉


Housekeeping

Of course, we want to make sure that everything is pretty and follows standards in the code so I went ahead and installed prettier, commitlint and husky. (Not going to go into depth about these here for this article but you can view the git repo to view all the config files I setup etc...)

npm i -D husky @commitlint/cli @commitlint/config-conventional prettier
Enter fullscreen mode Exit fullscreen mode

File structure

So the structure I went for, is as follows...

(I have only included files and folders I have made so config files, package.json files etc.. should be there regardless)

├── layouts
│   ├── Footer.tsx
│   ├── Head.tsx
│   └── Header.tsx
├── pages
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── register.tsx
│   └── login.tsx
├── styles
│   ├── Global.scss
│   └── Reset.scss
├── utils
│   ├── Global.scss
│   └── Reset.scss
├── public
├── templates
└── .env
Enter fullscreen mode Exit fullscreen mode

Prepping for AWS Cognito

So I am going to use AWS Cognito to handle the user database. AWS Cognito has a very generous free tier which allows for 50,000 monthly active users for free! Seeing as that is quite a lot, I don't think I'll be paying a single penny on any of my projects for years to come... It's a good, safe option if you are looking for something and just makes it so much easier to build login systems.

I'm starting off by installing the Amplify SDK..

npm i @aws-amplify/ui-react aws-amplify
Enter fullscreen mode Exit fullscreen mode

After doing this, I am then going to go into my .env file and add the following variables.

AWS_COGNITO_REGION='{ENTER YOUR REGION e.g eu-west-1}'
AWS_COGNITO_POOL_ID='{ENTER POOL ID}'
AWS_COGNITO_APP_CLIENT_ID='{ENTER CLIENT ID}'
Enter fullscreen mode Exit fullscreen mode

So head on over to Cognito and setup a user pool. Notes on each of these variables...

AWS_COGNITO_REGION - You should be able to see this in the pool ID in the overview section of the user pool.

AWS_COGNITO_POOL_ID - This should be visible after clicking on your user pool in the overview.

AWS_COGNITO_APP_CLIENT_ID - You can find this in your user pool overview under the tab "App integration" and scroll down to the bottom and it should be there.

Setting up a Cognito User Pool for the first time is a struggle, but there are plenty of guides from AWS to assist with this. If you want me to adapt this tutorial to include setting up Cognito, let me know in the comments!

We just need to do a slight change to the next.config.js file to include your ENV to make it available where required. In your module exports add the following...

env: {
    AWS_COGNITO_REGION: process.env.AWS_COGNITO_REGION,
    AWS_COGNITO_POOL_ID: process.env.AWS_COGNITO_POOL_ID,
    AWS_COGNITO_APP_CLIENT_ID: process.env.AWS_COGNITO_APP_CLIENT_ID,
},
Enter fullscreen mode Exit fullscreen mode

Finally, I'm going to just setup the Amplify config... Create a file in /utils/aws/Amplify.ts and then copy in the following code to construct a Amplify Auth instance...

import { Amplify } from 'aws-amplify';

Amplify.configure({
    Auth: {
        region: process.env.AWS_COGNITO_REGION,
        userPoolId: process.env.AWS_COGNITO_POOL_ID,
        userPoolWebClientId: process.env.AWS_COGNITO_APP_CLIENT_ID,
    },
});
Enter fullscreen mode Exit fullscreen mode

and then go into /pages/_app.tsx and add an import statement at the top as follows...

import '@/utils/aws/Amplify';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
    return <Component {...pageProps} />;
}
Enter fullscreen mode Exit fullscreen mode

Creating the login/register page

Next is to create the login and signup page. In /pages/login.tsx copy in this premade code from AWS with a few modifications...

import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import { Auth } from 'aws-amplify';
import { useRouter } from 'next/router';
import { useEffect } from 'react';

const Login = () => {
    const router = useRouter();
    const { user } = useAuthenticator((context) => [context.user]);

    useEffect(() => {
        const checkUser = async () => {
            try {
                const user = await Auth.currentAuthenticatedUser();
                if (user) {
                    router.push('/dashboard');
                }
            } catch (error) {
                // User is not logged in
            }
        };
        checkUser();
    }, [router, user]);

    return <Authenticator />;
};

export default Login;
Enter fullscreen mode Exit fullscreen mode

Creating an authentication HOC

Now we are going to go ahead and create HOC... (Basically, just a wrapper).

Create a file as such /utils/withAuth.tsx and copy and paste the following code.

import { useAuthenticator } from '@aws-amplify/ui-react-core';
import { Auth } from 'aws-amplify';
import { useRouter } from 'next/router';
import { useEffect } from 'react';

const withAuth = (WrappedComponent: any) => {
    const WrapperComponent = (props: any) => {
        const router = useRouter();
        const { user } = useAuthenticator((context) => [context.user]);

        useEffect(() => {
            const checkAuth = async () => {
                try {
                    await Auth.currentAuthenticatedUser();
                } catch (err) {
                    router.push('/login');
                }
            };

            checkAuth();
        }, [router, user]);

        return <WrappedComponent {...props} />;
    };

    return WrapperComponent;
};

export default withAuth;
Enter fullscreen mode Exit fullscreen mode

Basically, what we are doing here is adding a default useEffect to monitor if the users status changes, i.e logged in/logged out and then redirect them to the login page if they need to log in again. We are using the Amplify Authenticator context here to manage states.


Add Authenticator Provider Context

Talking about context's, we need to wrap the Authenticator Provider to the /pages/_app.tsx file. And update to look like the following code 👀..

import '@/utils/aws/Amplify';
import { Authenticator } from '@aws-amplify/ui-react';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
    return (
        <Authenticator.Provider>
            <Component {...pageProps} />
        </Authenticator.Provider>
    );
}
Enter fullscreen mode Exit fullscreen mode

Protected pages

Finally, all you have to do now is add all the protected pages you wish but make sure that your export function has the withAuth() HOC wrapped around it! Here's an example of the dashboard...

import withAuth from '@/utils/withAuth';
import { Auth } from 'aws-amplify';

const Dashboard = () => {
    return (
        <div>
            <h1>Dashboard Page</h1>
            <button onClick={() => Auth.signOut()}>Sign out</button>
        </div>
    );
};

export default withAuth(Dashboard);
Enter fullscreen mode Exit fullscreen mode

Find the code on Github :)

Sphinx ByDigital

Hope you enjoyed 🎉

Top comments (0)