Getting started with NextAuthv5 Credentials Provider
Published: 2023-12-22 19:40:02

---
title: Getting started with NextAuthv5 Credentials Provider
published: true
description: 
tags: 
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-22 19:18 +0000
---

> Note that this tutorial heavily references the official Vercel tutorial for using NextJS and NextAuth https://nextjs.org/learn/dashboard-app/adding-authentication

This tutorial is for using [NextAuth v5](https://authjs.dev/) to allow users to sign in via email/password on a custom signin page. You can also reference the official guide for [transitioning from v4](https://authjs.dev/guides/upgrade-to-v5). Note that v5 is still in beta so [documentation](https://authjs.dev/reference/nextjs#auth) is still a work-in-progress. So, after having some difficulty myself, I wrote this guide to help others who may be trying to figure out setting up a standard email/password authentication flow from scratch

In this tutorial, we will:

- Setup Next Auth with Credentials provider
- Setup a custom login page
- Use middleware to setup protected pages

First, create a new app

```
npx create-next-app@latest
```

- Choose default options for each (including using the `/src` folder)
- Remove default styles from `/src/app/globals.css` and leave the following

```jsx
@tailwind base;
@tailwind components;
@tailwind utilities;
```

Install next-auth v5

```
npm i next-auth@beta
```

Add `.env` file

Generate app secret

`openssl rand -base64 32`

Add env setting to .env file

```jsx
AUTH_SECRET=<generated_secret>
```

Now that NextAuth supports route handlers (in app directory), it is often recommended to setup an api route (eg at `/app/api/auth/[...nextauth]/route.ts`) in order to utilize the built-in authentication pages as well as GET, POST handlers which are useful for the OAuth/email providers. However, since we're using the Credentials provider with a custom login page, we can use our own custom configuration (inspired by Vercel's [dashboard tutorial](https://nextjs.org/learn/dashboard-app/adding-authentication))

First, let's create a configuration file for NextAuth and name it`auth.config.ts`

```jsx
import type { NextAuthConfig } from 'next-auth';

export const authConfig = {
  pages: {
    signIn: '/login',
  },
  callbacks: {
    authorized({ auth, request: { nextUrl } }) {
      const isLoggedIn = !!auth?.user;
      const isOnDashboard = nextUrl.pathname.startsWith('/dashboard');
      if (isOnDashboard) {
        if (isLoggedIn) return true;
        return false; // Redirect unauthenticated users to login page
      } else if (isLoggedIn) {
        return Response.redirect(new URL('/dashboard', nextUrl));
      }
      return true;
    },
  },
  providers: [], // Add providers with an empty array for now
} satisfies NextAuthConfig;
```

Let's configure Next Auth integration to use Credentials Provider using `auth.ts`. Note that we're exporting helpful functions signIn, signOut and auth from the configuration. We'll be using these functions throughout the app

```jsx
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';
import Credentials from 'next-auth/providers/credentials';

export const { auth, signIn, signOut } = NextAuth({
  ...authConfig,
  pages: {
    signIn: '/login',
  },
  providers: [
    Credentials({
      credentials: {
        email: {},
        password: {},
      },
      async authorize(credentials: any) {
        console.log(credentials);
        if (credentials) {
          //Return a valid user object
          return {
            id: '12345',
            name: 'testuser',
            email: credentials.email,
          };
        } else {
          return null;
        }
      },
    }),
  ],
  callbacks: {
    async jwt({ token, user, account, profile, trigger, session }) {
      if (user && trigger === 'signIn') {
        token.user = user;
      }
      return token;
    },
    async session({ session, token }) {
      session.user = token.user as any;
      return session;
    },
  },
  session: { strategy: 'jwt' },
});
```

- Note that we are using jwt strategy because NextAuth does not by default support storing user sessions and tokens in the database when using CredentialsProvider
- We have set `/login` page as our signin page for NextAuth. This will be used when NextAuth is redirecting the user after logout

Add a middleware which will redirect the user based on their logged in status and protect authenticated pages

```jsx
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';

export default NextAuth(authConfig).auth;

export const config = {
  // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher
  matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
};
```

Now in order to test, let’s create a login page at `/login/page.tsx`

We’ll use a server action in order to trigger the `signIn` function provided by NextAuth

```jsx
import { signIn } from '@/auth';

const Login = () => {
  const handleSignIn = async (formData: FormData) => {
    'use server';
    const email = formData.get('email');
    const password = formData.get('password');
    await signIn('credentials', {
      email,
      password,
    });
  };
  return (
    <div>
      <h1>Login</h1>
      <form action={handleSignIn}>
        <input
          className="border border-gray-400 block"
          name="email"
          type="email"
        />
        <input
          className="border border-gray-400 block"
          name="password"
          type="password"
        />
        <button type="submit">Sign In</button>
      </form>
    </div>
  );
};

export default Login;
```

and then a dashboard page at `/dashboard/page.tsx`

we’ll display some information from the current user session and we’ll use a server action in order to trigger the `signOut` function provided by NextAuth

```jsx
import { signOut } from '../../auth';
import { PowerIcon } from '@heroicons/react/24/outline';
import { auth } from '../../auth';

const Dashboard = async () => {
  const session = await auth();
  return (
    <div>
      <h1>Dashboard</h1>
      <div>{session ? <p>Welcome back, {session?.user?.name}</p> : null}</div>
      <form
        action={async () => {
          'use server';
          await signOut();
        }}
      >
        <button className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
          <PowerIcon className="w-6" />
          <div className="hidden md:block">Sign Out</div>
        </button>
      </form>
    </div>
  );
};

export default Dashboard;
```

We can now test the functionality by entering a valid email and password in the login form on `/login`

If successful, you should be redirected to the `/dashboard`

On the dashboard page, you should be able to test the logout functionality. If successful, you will be redirected to the `/login` page

You can also test whether or not the page protection is working by trying to visit `/dashboard` page when logged out. You should be redirected to the `/login` page

So far, we’re able to allow a user to create a session by entering their user information but that’s not exactly a real-world scenario where you would authenticate the user credentials against a database or other authentication service. 

In the next tutorial, we will connect to database using DrizzleORM and Planetscale to fetch user details

Please let me know if you'd like an example repo of the above and if I can clarify any of the steps, please don't hesitate to let me know