How to set up Auth0 and Neon Postgres in Next.js App Router
Tags: tutorial, authjs, typescript, nextjs
Published: 2024-02-13 09:34:54

Modern applications demand a balance of scalability, speed, and security to meet users’ ever-growing needs. Ensuring authentication and a robust database management system is crucial at the foundation of this triad. In this article, we’ll talk about the combination of Auth0 and Neon Postgres when you need to accommodate these two needs.

Auth0 is an identity platform that simplifies authentication and authorization functionalities across web, mobile, and native applications.

Neon is fully managed serverless Postgres that provides separate storage and computing to offer autoscaling, branching, and bottomless storage. Neon is open source under the Apache 2.0 licenses; you can check out the [neondatabase](https://github.com/neondatabase/neon?ref=hm) GitHub repo.

This guide provides a comprehensive walk-through of setting up Auth0 with a Postgres database like Neon by building a login page that routes to a profile page while implementing Neon Postgres as the data store for user information. This application uses the Next.js React framework with App Router and Tailwind for styling.

> Here is the link to the GitHub [repository](https://github.com/muyiwexy/auth_postgres?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity) containing all the code

## Prerequisites

To successfully complete this guide, you’ll need a few things:

- Proficiency with JavaScript frameworks such as [Next.js](https://nextjs.org/?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity) (**recommended**), React, etc.
- A [Neon account](https://console.neon.tech/sign_in)
- An [Auth0 account](https://auth0.com/signup?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity)
- [Node 18](https://nodejs.org/en/download) or later versions

## Setting up a Neon Postgres project

After signing up, you’ll note Neon's streamlined project setup; you’ll just need to provide three things:

- Project name
- Postgres version
- Database name

On the project setup page, there are more options to explore; for example, you can change the branch name to any other name, but we recommend leaving it as `main` for now. Next, click **Create project**.

![Neon sign-in](https://paper-attachments.dropboxusercontent.com/s_0F1F35A31B87D9FE2BC161DA638BA2CAFBC4BDCDC924430F421BE8C7E4BEB0A0_1700901557376_neon-sign-in.png)

![Create project](https://paper-attachments.dropboxusercontent.com/s_0F1F35A31B87D9FE2BC161DA638BA2CAFBC4BDCDC924430F421BE8C7E4BEB0A0_1700902018153_create_project.png)

![More options](https://paper-attachments.dropboxusercontent.com/s_0F1F35A31B87D9FE2BC161DA638BA2CAFBC4BDCDC924430F421BE8C7E4BEB0A0_1700902018073_explore_options.png)

On the homepage, you'll find a popup containing all the connection details required to connect your application(s) to a Neon database. Copy these details to a safe file, such as a JSON file.

![Connection details](https://paper-attachments.dropboxusercontent.com/s_0F1F35A31B87D9FE2BC161DA638BA2CAFBC4BDCDC924430F421BE8C7E4BEB0A0_1701099281693_connection_details.png)

## Creating a Next.js application and installing dependencies

To create a Next.js application, open your terminal in any folder of your choice and type the command below:

```javascript
npx create-next-app@latest <folder-name> --ts
```

This command creates a Next.js application with TypeScript as the preferred programming language.

Next, we need to install some dependencies. These include an Auth0 dependency and `dotenv` to store sensitive credentials in a `.env` file. To do this, run the command below:

```javascript
npm install @auth0/nextjs-auth0 dotenv
```

## Configuring Auth0 application settings

After signing into the Auth0 account you created earlier, click **Create Application** in the Auth0 dashboard, select **Regular Web Applications,** and fill in the **app name** to create traditional web applications that use redirects.

![dashboard](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702995071772_new.png)

![Regular web application](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702681964913_regular+web+application.png)

The following prompt asks what technology is being used for the project; we’ll select Next.js out of all the presented options for this project.

![Technology](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702682229341_technology.png)

Over at your just-created **Regular Web Applications**, you’ll need to configure the URLs for your app under the application URI. To do this, head to **Settings > Allowed Callback URLs** and type in the URL below within the input field:

```javascript
http://localhost:3000/api/auth/callback
```

Here, we use port `3000` as the port our application runs on, so make sure to select the port your application is running on.

> Note: you can add multiple URLs by separating them with a comma ","

The URL above completes the web application's authentication flow by triggering the `callback` route after successful authentication. The callback route `api/auth/callback` is integrated by Auth0 into the `@auth0/nextjs-auth0` package installed earlier, which will handle the callback flow.

Next, we’ll add the following URL in the **Allowed Logout URLs** input field:

```javascript
http://localhost:3000
```

Setting this allows a redirect back to our web application after logging out, and you can use a personal URL if your project requires it.

![Allowed callbacks](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702721836991_application+URIs.png)

Once you're done with that, save the changes and head to the top page of the **Settings** section. Make sure to note the **Domain, Client ID, and Secret,** as they will come in handy in the coming sections.

![Basic information](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702721836953_basic+information.png)

## Connecting NextJs to Auth0

To initiate a connection between Auth0 and Next.js, head back to the Next.js application we created and create a `.env.local` file in the root folder to store the **Domain, Client ID, and Secret** from Auth0. Then, paste the following variables into the file below:

```javascript
AUTH0_SECRET= use [openssl rand -hex 32] to generate a 32 bytes value
AUTH0_BASE_URL= http://localhost:3000
AUTH0_ISSUER_BASE_URL= https://AUTH0-DOMAIN
AUTH0_CLIENT_ID= AUTH0-CLIENT-ID
AUTH0_CLIENT_SECRET= AUTH0-CLIENT-SECRET
```

> Note: You can get these pages from [Auth0's Nextjs documentation](https://auth0.com/docs/quickstart/webapp/nextjs/01-login?utm_source=hackmamba&utm_medium=blog&utm_id=HMBcommunity) and replace the values with those of your Auth0 regular web app's values.mk

We will need to generate a 32-byte random value for the `AUTH0_SECRET`, and this can be done using Node's `crypto` module. The `crypto` module generates an arbitrary hexadecimal string of length 32. Thus, head to your terminal and type the command below to use the module:

```javascript
node -e "console.log(crypto.randomBytes(32).toString('hex'))"
```

## Creating an API route for Auth0

Next, we'll need to add the dynamic API route; we’ll do so by creating a `/api` directory within the `/app` directory. Within the now `/app/api` directory, create an `/auth` directory to handle all the authentication-related things within this project (create this directory instead if you already have an `/api`).

Within the `/auth` directory, create a new unique directory called `/[auth0]` and create a file `route.tsx` within the folder. Using a directory in this manner, `/[auth0] indicates` that the directory name **auth** is a dynamic segment.

In the `route.tsx` file, we will use the `handleAuth` method from the `@auth0/nextjs-auth0` to handle the authentication flow of our web application. The `handleAuth` creates multiple route handlers under the hood — this means that when we log initially into Auth0, the routes followed are **api-auth-login.** So, the `handleAuth` function will handle all the login flow based on that and log out the callback URLs set earlier.

So, let’s add the code below to the `route.tsx` to implement what we just explained above:

```javascript
import { handleAuth } from "@auth0/nextjs-auth0";
export const GET = handleAuth();
```

Next, we need to wrap our application in an Auth0 `UserProvider`. In the `layout.tsx` file from the base `/app` directory, we’ll import `UserProvider` from `@auth0/nextjs-auth0/client`, and then we'll wrap the body tag in a `UserProvider`.

So here is the updated `layout.tsx` file below:

```javascript
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { UserProvider } from "@auth0/nextjs-auth0/client";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
  title: "Auth0 and Neon Postgres",
  description: "Generated by create next app",
};
export default function RootLayout({
  children,
}: {
  children: React.ReactNode,
}) {
  return (
    <html lang="en">
      <UserProvider>
        <body className={inter.className}>{children}</body>
      </UserProvider>
    </html>
  );
}
```

We perform this operation using an Auth0 platform to create and authenticate users. Therefore, we will have to wrap our entire application in Auth0. At the moment, these are all the configurations needed for our web application. In the coming sections, we will create pages for logging in and a profile page to display certain user information.

## Building the pages and routes using Next.js and Auth0

This project will use the initial page (`/app/page.tsx`) and profile routes. The initial page route will be a form with email, password input, and a submit button. Also, there will be a button that triggers a connection to Auth0. For the profile area, a simple card will show the user's profile image, name, and email. The card will also contain a logout button that triggers a logout action from Auth0.

The idea is to give a clearer picture of how the **App Router** operates after successful authentication with Auth0. Earlier, we enabled **App Router** when we created our project — thus, route by creating another folder in the `/app` directory, let's call it `profile`. Within this `profile` directory, create a file called `page.tsx`. With that, you've successfully created a route with **App Router.**

Head back to the `/app/page.tsx` file, clear out the initial template, and replace it with the one below:

```javascript
export default function Login() {
  return (
    <main className="flex min-h-screen items-center justify-center">
      <div className="w-[300px] md:w-[600px] shadow-lg p-5">
        <p className="text-5xl">Login</p>
        <div className="h-[20px]" />
        <form>
          <input
            type="email"
            placeholder="E-mail"
            className="border-2 border-black w-full p-5 text-2xl rounded-lg focus:outline-blue-400"
            name="email"
          />
          <div className="h-[20px]" />
          <input
            type="password"
            placeholder="Password"
            className="border-2 border-black w-full p-5 text-2xl rounded-lg focus:outline-blue-400"
            name="password"
          />
          <div className="h-[40px]" />
          <div className="flex justify-end">
            <button className="border-2 border-black p-5 px-10 text-2xl rounded-lg">
              Try Me
            </button>
          </div>
        </form>
        <div className="h-[20px]" />
        <p className="flex justify-center font-bold text-2xl">Or</p>
        <div className="h-[20px]" />
        <button className="border-2 w-full border-black p-5 px-10 text-2xl rounded-lg">
          Sign in with Auth0
        </button>
      </div>
    </main>
  );
}
```

The code above is the login UI template mentioned earlier, and when we run the code, our application will look like the image below:

![Login template](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702846631032_nextjs+login.png)

Still within the `/app/pages.tsx` file, import `useUser` function from `@auth0/nextjs-auth0/client`. Then return `user, error, isLoading` from the `useUser` function in the `Login` function. The `user` object allows us to display user information after a successful login. In this case, where we experience any errors during signups or login, the `error` object will display the error message. `isLoading` is a Boolean parameter that lets us know when data is being loaded.

Also, we will use Next.js navigation to handle navigation using the `useRouter` function. To do this, we’ll import the `useRouter` function from `next/navigation,` then return `push`.

With this, we can use a condition to check if there is a `user` object, and if it is true, push it to the `/profile` route created earlier. Also, we will use the `useRouter` function to create a login link that will allow us to sign up and log in to our Auth0 application.

First, create a function that pushes to the route `api/auth/login` and set an `onClick` event to the function in the `Sign in with Auth0` button from our template code.

Below is the updated code for the `page.tsx`:

```javascript
"use client";
import { useRouter } from "next/navigation";
import { useUser } from "@auth0/nextjs-auth0/client";
export default function Login() {
  const { push } = useRouter();
  const { user, error, isLoading } = useUser();
  if (user) {
    push("/profile");
  }
  if (isLoading) {
    return <div>...loading</div>;
  }
  if (error) {
    return <div>{error.message}</div>;
  }
  const handleLogin = () => push("api/auth/login");
  return (
    <main className="flex min-h-screen items-center justify-center">
      <div className="w-[300px] md:w-[600px] shadow-lg p-5">
        <p className="text-5xl">Login</p>
        <div className="h-[20px]" />
        <form>
          <input
            type="email"
            placeholder="E-mail"
            className="border-2 border-black w-full p-5 text-2xl rounded-lg focus:outline-blue-400"
            name="email"
          />
          <div className="h-[20px]" />
          <input
            type="password"
            placeholder="Password"
            className="border-2 border-black w-full p-5 text-2xl rounded-lg focus:outline-blue-400"
            name="password"
          />
          <div className="h-[40px]" />
          <div className="flex justify-end">
            <button className="border-2 border-black p-5 px-10 text-2xl rounded-lg">
              Try Me
            </button>
          </div>
        </form>
        <div className="h-[20px]" />
        <p className="flex justify-center font-bold text-2xl">Or</p>
        <div className="h-[20px]" />
        <button
          onClick={handleLogin}
          className="border-2 w-full border-black p-5 px-10 text-2xl rounded-lg"
        >
          Sign in with Auth0
        </button>
      </div>
    </main>
  );
}
```

When you click the button above, an Auth0 login will be created successfully, but the objective is incomplete as there is currently nothing to display in the `/profile/page.tsx`. So, we’ll add a UI template code that contains a user image area, name, email, and a logout button.

Thus, head to the `/profile/page.tsx` file and paste the template code below to it:

```javascript
export default function Profile() {
  return (
    <main className="max-w-[1900px] flex flex-col mx-auto mt-5 rounded-2xl overflow-hidden">
      <div className="h-[250px] bg-black relative">
        <div className="h-[150px] w-[150px] rounded-full border-2 bg-gray-600 absolute -bottom-[75px] z-50 left-10 overflow-hidden">
          <img
            src={user?.picture ?? "default-image-url"}
            alt=""
            className="object-cover h-full, w-full"
          />
        </div>
      </div>
      <div className="h-[300px] relative transl bg-slate-600 p-5 flex justify-between">
        <div>
          <p className="mt-[90px] text-3xl font-bold">
            {user?.name ?? "default-name"}
          </p>
          <p className="mt-[20px] text-2xl">{user?.email ?? "default-email"}</p>
        </div>
        <div>
          <button className="border-2 border-black p-5 px-10 text-2xl rounded-lg cursor-pointer">
            Log Out
          </button>
        </div>
      </div>
    </main>
  );
}
```

When we run the code, the `/profile` route should look like the image below:

![Profile template](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702851241542_profile+page.png)

Still within the `/app/profile/page.tsx` file, we'll do similarly to what we did in the `/app/pages.tsx` file with a slight twist. Start by importing the `useUser` function and the `withPageAuthRequired` function from `@auth0/nextjs-auth0/client`. Then return `user, error, isLoading` from the `useUser` function in the `Profile` function.

Next, use the `useRouter` function to create a logout link to allow us to log out of our Auth0 application. To do this, create a function that pushes to the route `api/auth/logout` and set an `onClick` event to the function in the `Log Out` button from the `/profile` route template code.

The `withPageAuthRequired` is a higher-order component used to protect pages requiring authentication. So, we will need to wrap the `Profile` function with a map object that contains properties `onRedirecting` and `onError` that shows specific messages per the context.

Here is the updated code to the `/app/profile/page.tsx` file:

```javascript
"use client";
import { useUser, withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import { useRouter } from "next/navigation";
function Profile() {
  const { user, error, isLoading } = useUser();
  const { push } = useRouter();
  // arrow function that routes to the logout api from the [handleAuth] method
  const handleLogout = () => push("api/auth/logout");
  // handle isLoading state from Auth0
  if (isLoading) {
    return <div>...loading</div>;
  }
  // handle error state from Auth0
  if (error) {
    return <div>{error.message}</div>;
  }
  console.log(user?.email);

  // main profile pagelayout
  return (
    <main className="max-w-[1900px] flex flex-col mx-auto mt-5 rounded-2xl overflow-hidden">
      <div className="h-[250px] bg-black relative">
        <div className="h-[150px] w-[150px] rounded-full border-2 bg-gray-600 absolute -bottom-[75px] z-50 left-10 overflow-hidden">
          <img
            src={user?.picture ?? "default-image-url"}
            alt=""
            className="object-cover h-full, w-full"
          />
        </div>
      </div>
      <div className="h-[300px] relative transl bg-slate-600 p-5 flex justify-between">
        <div>
          <p className="mt-[90px] text-3xl font-bold">{user?.name}</p>
          <p className="mt-[20px] text-2xl">{user?.email}</p>
        </div>
        <div>
          <button
            onClick={handleLogout}
            className="border-2 border-black p-5 px-10 text-2xl rounded-lg cursor-pointer"
          >
            Log Out
          </button>
        </div>
      </div>
    </main>
  );
}
export default withPageAuthRequired(Profile, {
  onRedirecting: () => <div>Redirecting to Login ....</div>,
  onError: (error) => <div>{error.message}</div>,
});
```

With this, we have reached a milestone, as when we run our application with the command `npm run dev`, we can authenticate our application using Auth0.

{% embed 
https://app.opentape.io/share/6d311a32-a0de-46d9-ad32-6a69ba1c88b3 
%}

You’re likely wondering where this data is stored after logging in or signing up. Auth0 utilizes its database to store the data, but we'd like to change that by implementing Auth0 with a custom database — in this case, Neon Postgres. This process ensures user data is available in the primary data store (Neon) for other application development purposes.

The next section demonstrates how to use Neon as a custom database and sets up some database action scripts for direct communication between Auth0 and Neon Postgres. Let’s jump in!

## Setting up Neon as a custom database on Auth0

Head back to your Auth0 dashboard, and from there, select **Authentication → Database.**

![database](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702974865301_database.png)

In the Database section, create a database connection by clicking Create DB Connection, fill in your desired Connection name, and click **Create.**

![database connection](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702974693014_create+db+connection.png)

![database connection name](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702975111735_db+connection+name.png)

In your newly created connection, head to the **Custom Database** section, where all the actions will take place. Toggle the **Use my own database** option and scroll to the **Database Action Scripts** area.

![custom database](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702998959712_custom+database.png)

![DAS](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702854979465_DAS.png)

In the **Database Action Scripts** section, we can create custom serverless scripts that perform specific actions on our database. The **Login** script is mandatory; you can set the others as you please. For this tutorial, we will create templates for the six scripts in the image above, and here is their code below in succession:

**Login script**

This script is executed during each login to validate the user's authenticity.

```javascript
function login(email, password, callback) {
  const { Pool } = require("pg");
  const bcrypt = require("bcrypt");

  const pool = new Pool({
    connectionString: configuration.POSTGRES_URL,
  });

  const query =
    "SELECT id, nickname, email, password FROM users WHERE email = $1";
  pool.query(query, [email], function (err, result) {
    if (err || result.rows.length === 0)
      return callback(err || new WrongUsernameOrPasswordError(email));

    const user = result.rows[0];

    bcrypt.compare(password, user.password, function (err, isValid) {
      if (err || !isValid)
        return callback(err || new WrongUsernameOrPasswordError(email));

      return callback(null, {
        user_id: user.id,
        nickname: user.nickname,
        email: user.email,
      });
    });
  });
}
```

**Create script**

This script is executed after a user signs up. A database table row is created for the user with the information filled in during the signup process.

```javascript
function create(user, callback) {
  const { Pool } = require("pg");
  const bcrypt = require("bcrypt");

  const pool = new Pool({
    connectionString: configuration.POSTGRES_URL,
  });

  bcrypt.hash(user.password, 10, function (err, hashedPassword) {
    if (err) return callback(err);

    const query =
      "INSERT INTO users(nickname, email, password) VALUES ($1, $2, $3)";
    pool.query(
      query,
      [user.username, user.email, hashedPassword],
      function (err, result) {
        if (err) return callback(err);

        return callback(null);
      }
    );
  });
}
```

**Verify script**

This script verifies the email verification status of a user.

```javascript
function verify(email, callback) {
  const { Pool } = require("pg");
  const pool = new Pool({
    connectionString: configuration.POSTGRES_URL,
  });

  const query =
    "UPDATE users SET email_verified = true WHERE email_verified = false AND email = $1";
  pool.query(query, [email], function (err, result) {
    if (err) return callback(err);

    return callback(null, result && result.rowCount > 0);
  });
}
```

**Change password script**

This script changes the user's encrypted password on the database after following the **change password** during a password reset process.

```javascript
function changePassword(email, newPassword, callback) {
  const { Pool } = require("pg");
  const bcrypt = require("bcrypt");

  const pool = new Pool({
    connectionString: configuration.POSTGRES_URL,
  });

  bcrypt.hash(newPassword, 10, function (err, hash) {
    if (err) return callback(err);

    const query = "UPDATE users SET password = $1 WHERE email = $2";
    pool.query(query, [hash, email], function (err, result) {
      if (err) return callback(err);

      return callback(err, result && result.rowCount > 0);
    });
  });
}
```

**Get user script**

This script tests if a user exists in a database. For example, a scenario in which the user wants to change their password.

```javascript
function loginByEmail(email, callback) {
  const { Pool } = require("pg");

  const pool = new Pool({
    connectionString: configuration.POSTGRES_URL,
  });

  const query = "SELECT id, nickname, email FROM users WHERE email = $1";
  pool.query(query, [email], function (err, result) {
    if (err || result.rows.length === 0) return callback(err);

    const user = result.rows[0];

    return callback(null, {
      user_id: user.id,
      nickname: user.nickname,
      email: user.email,
    });
  });
}
```

**Delete script**

This Script is activated when the user is deleted from Auth0 — thus, the user data is subsequently removed from the database.

```javascript
function remove(id, callback) {
  const { Pool } = require("pg");

  const pool = new Pool({
    connectionString: configuration.POSTGRES_URL,
  });

  const query = "DELETE FROM users WHERE id = $1";
  pool.query(query, [id], function (err) {
    if (err) return callback(err);

    return callback(null);
  });
}
```

> Note: With Neon, you can manage your database using a few options, such as a [Command Line Interface (CLI)](https://neon.tech/docs/reference/cli-databases?ref=hm), the [Neon API](https://api-docs.neon.tech/reference/getting-started-with-neon-api?ref=hm), or a [Structured Query Language (SQL)](https://neon.tech/docs/get-started-with-neon/query-with-neon-sql-editor?ref=hm) tool.

After updating the **Database Action Scripts,** update your **Database settings** with some sensitive information you used to validate the scripts. For this tutorial, we used the Neon Postgres pooled connection URL.

To get this URL, head back to your Neon dashboard and copy out the Connection String from the connection detail area. Ensure to mark **Pooled Connection** as true before copying the details.

![connection detail](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702856218524_Neon.png)

Back in Auth0's **Database settings** area, create a key with the name `POSTGRES_URL` and value with the **Pooled Connection** string copied from Neon Postgres.

![Database settings](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702856510033_database+settings.png)

Still within your Auth0 **Database Connection,** head to **Applications** and toggle on the **Regular Web Applications**. We have successfully set Neon Postgres as a custom database for our Auth0 application.

![applications](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702975320200_applications.png)

## Creating `users` database table on Neon

With the project all but done, there is one tiny thing to add: creating the table for the user store. When creating the **Database Action Script,** we specified that we are querying a table called `users`, so let’s make that our table name.

For our table, we will create five fields: **id, nickname, email, password, and email_verified.** So, head to the [SQL Editor](https://console.neon.tech/app/projects/divine-sun-12551123/query) in Neon's console and run the query below:

```SQL
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE users(
    id UUID DEFAULT uuid_generate_v4(),
    nickname VARCHAR(255),
    email VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    email_Verified BOOLEAN DEFAULT FALSE
);
```

The query above creates a UUID column with a default value generated by `uuid_generate_v4()` function. It is followed by creating a `nickname` column — a variable character (**varchar**) column with a maximum length of 255 characters.

Also, it creates an `email` and `password` column, a non-null **varchar** column with a maximum length of 255 characters, and a unique constraint.

Finally, it creates an `email_Verified` column, a **Boolean** column with a default value of **false**.

When we initialize a signup flow with Auth0, here’s the result on the Neon Postgres database table:

![Neon table](https://paper-attachments.dropboxusercontent.com/s_66F25803C9BFBABB9A6CE4A7562B55E583C2FC70724987161257C860A4FBD952_1702860058717_neon.png)

## Conclusion

This tutorial taught us how to leverage security and database management by integrating Auth0 with Neon Postgres as a custom database. Pairing Auth0 and Neon Postgres represents a pivotal leap toward fortifying modern applications with scalability, speed, and security. By adopting this strategic combination, developers can ensure a seamless and secure authentication process through Auth0 while leveraging the robust database management capabilities of Neon Postgres.

## Resources

- [Configuring PostgreSQL as Auth0 Custom Database](https://auth0.com/blog/configuring-postgresql-as-auth0-custom-database/#Testing-the-Database)
- [Creating applications with Auth0](https://auth0.com/docs/get-started/auth0-overview/create-applications)
- [Neon documentation](https://neon.tech/docs/introduction?ref=hm)