DEV Community

Cover image for Authentication with credentials using Next-Auth and MongoDB - Part 2
Mainak Das
Mainak Das

Posted on • Edited on

Authentication with credentials using Next-Auth and MongoDB - Part 2

In the last part, I created the signup, login and route along with the and the connection of frontend to the backend. Also, I created the sign-in logic using next-auth.

In this part, I'll mainly focus on the frontend connection using next-auth.

Posting sign in logic

The next-auth client gives us both signIn() and signOut() hooks that will make our coding a whole lot easier and our only work is to provide the authentication type we will use to sign-in (in our case i.e credentials).

Using the signIn() method ensures the user ends back on the page they started on after completing a sign-in flow. It will also handle CSRF Tokens for you automatically when signing in with email.

The sign-in hook will always return a Promise containing an error key:value pair that will tell us if the authentication is successful or not.

You can look into more detail here.

import { signIn } from 'next-auth/client';
//...
const status = await signIn('credentials', {
                redirect: false,
                email: email,
                password: password,
            });
            console.log(status);
Enter fullscreen mode Exit fullscreen mode

And that's our sign-in logic in place.

But wait, it's not all

Suppose, you're signed in but trying to access the route .../auth which normally shows us the sign-in or sign-up form.

To protect that route, next-auth also gives us a getSession() hook to check for the session and determine whether a user is signed in or not.

import { getSession } from 'next-auth/client';
Enter fullscreen mode Exit fullscreen mode

NextAuth.js provides a getSession() method which can be called a client or server-side to return a session.

It calls /api/auth/session and returns a promise with a session object, or null if no session exists.

More info here

Now, let's add this to our .../auth route:

We will use useEffect() and useState() hooks to tell user that something is loading. As getSession() returns a promise we need a then chain for getting the session object. If there is a session we will use next/router to redirect the users to / page.

//...
const [loading, setLoading] = useState(true);
    const router = useRouter();
    useEffect(() => {
        getSession().then((session) => {
            if (session) {
                router.replace('/');
            } else {
                setLoading(false);
            }
        });
    }, []);
    if (loading) {
        return <p>Loading...</p>;
    }
//...
Enter fullscreen mode Exit fullscreen mode

Protect a secured route

On the change password page, we need an authenticated user to do the action, and if any unauthenticated user visits ../profile they will be redirected to the auth page for sign-in or sign-up.

The getSession() hook can also be used on the server to check for sessions and do any redirect based on that.

We will use the hook along with getServerSideProps for checking the session of the user trying to access.

NOTE

When calling getSession() server side, you need to pass {req} or context object.

For securing .../profile page:

export async function getServerSideProps(context) {
    const session = await getSession({ req: context.req });
    if (!session) {
        return {
            redirect: {
                destination: '/auth',
                permanent: false,
            },
        };
    }
    return {
        props: { session },
    };
}
Enter fullscreen mode Exit fullscreen mode

With all the sign-in and sign-up logic in place, now we will look into the Header for showing and hiding the tabs based on user sign-in or not. And finally the sign-out logic.

Dynamic navbar tabs

The useSession hook from next-auth is the best way to check for an authenticated user. The hook gives us a session and loading state that will be updated based on fetching the users' session.

import { useSession } from 'next-auth/client';
Enter fullscreen mode Exit fullscreen mode

We will use the session to show and hide the tabs.

function MainNavigation() {
    const [session, loading] = useSession();
    return (
        <header className={classes.header}>
            <Link href='/'>
                <a>
                    <div className={classes.logo}>Next Auth</div>
                </a>
            </Link>
            <nav>
                <ul>
                    {!session && !loading && (
                        <li>
                            <Link href='/auth'>Login</Link>
                        </li>
                    )}
                    {session && (
                        <li>
                            <Link href='/profile'>Profile</Link>
                        </li>
                    )}
                    {session && (
                        <li>
                            <button >Logout</button>
                        </li>
                    )}
                </ul>
            </nav>
        </header>
    );
}

export default MainNavigation;
Enter fullscreen mode Exit fullscreen mode

After noticing a bit we will see there's a flickering in the navbar tabs. That's because it's checking for the session twice. Next-auth has a workaround for this also. It provides a <Provider> component that shares the session object across multiple components and as a result, useSession doesn't have to check for session twice.

import { Provider } from 'next-auth/client';
Enter fullscreen mode Exit fullscreen mode

Using the supplied React <Provider> allows instances of useSession() to share the session object across components, by using React Context under the hood.

This improves performance, reduces network calls, and avoids page flicker when rendering. It is highly recommended and can be easily added to all pages in Next.js apps by using pages/_app.js.

We can pass the session object to the <Provider> component as a prop to avoid checking twice.

If you pass the session page prop to the <Provider> you can avoid checking the session twice on pages that support both server and client-side rendering.

Let's add this to our _app.js:

<Provider session={pageProps.session}>
    <Layout>
      <Component {...pageProps} />
    </Layout>
</Provider>
Enter fullscreen mode Exit fullscreen mode

Now, the header will no longer flicker.

Let's check the sign-out logic.

Sign Out

Next-auth also gives us a signOut() hook that we can attach with any element onClick() prop and it will just sign us out. It's as easy as that.

More info here.

<li>
    <button onClick={signOut}>Logout</button>
</li>
Enter fullscreen mode Exit fullscreen mode

And that's how we implement authentication with credentials in Next.js.

Top comments (12)

Collapse
 
shakvilla profile image
Shakvilla

I have an issue after using this logic. I modified it a little, where a user creates a client but when i login with client details, and try to fetch data of logged in client, I'm unable to fetch data of the current client, i figured I'm not able to implement a logic to allow data fetching for current user, do you think you can help out with the logic, i followed your tuts and implemented authentication and its working, just fetching data and displaying for current logged in user

Collapse
 
dawnind profile image
Mainak Das

Can you atleast post some snippets or source code so that I can understand a bit better !

Collapse
 
deathreboot profile image
Death-reboot

hey can you share Source Code of this

Collapse
 
dawnind profile image
Mainak Das

Here you go:
Authentication with credentials using Next-Auth and MongoDB

Add MongoDB connect url in a .env file as MONGO_URI

Collapse
 
muhrizqiardi profile image
muhrizqiardi

Hey, just wanted to say thank you for writing this article. This helped me a lot for my assignment!

Collapse
 
dawnind profile image
Mainak Das

You're welcome :) glad to help

Collapse
 
nizarfadlan profile image
Nizar Izzuddin Yatim Fadlan

How to multiple role use custom session and jwt?

Collapse
 
regisnut profile image
Regisnut

Hi, how do you handle signup with others data such as tel, adress, city... and also, how do you handle reset/forgot password??
THAnks.

Collapse
 
dawnind profile image
Mainak Das

For signup with other data you've to assign those to the user object before posting it to the database.
You've to implement your own reset or forgot password strategy, for example
Change password

Collapse
 
j0hannr profile image
j0hannr

Thanks for the great tutorial!

I Forked your Project to use it with Prisma instead of MangoDB
github.com/j0hannr/next-auth-crede...
And Updated to NextAuth v4

Collapse
 
richardpickett profile image
Richard Pickett

wheatear = whether

Collapse
 
dawnind profile image
Mainak Das

thanks man :)