✨ Do this first if you are using an Auth Provider 🧙‍♂️ 🫵
Tags: webdev, javascript, opensource, programming
Published: 2024-06-30 13:16:24

In this article, I will show you how you can safely backup your users data in your own database when using an Authentication Provider.

![What GIF](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMzdvamtrdmh3OG82ZGhyZDZkdHcwbjl2ODZ0OGtmbmJodmdic2YwZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/gd09Y2Ptu7gsiPVUrv/giphy.gif)

## Why keep a backup in the first place? 🤔

Did you ever consciously think that when using an auth provider, you are essentially storing your users' information with them and you do not have access to the user data outside of them (not even in your own database)? 😳

The main purpose of Auth Providers is to abstract away the user authentication logic, but in doing so, you are also completely giving your users' data to them without retaining any control yourself.

What if a new intern joins your auth provider company and mistakenly deletes the production database? This is extremely rare, but the chances are not zero. Not only could they shut down their company, but you would also lose all your users' data. They might have some backup set up to prevent such scenarios, but you never know how things are implemented under the hood in another company.

Many of you don’t even think of this and get started with using one of the providers just because they are **easier** 🤷‍♂️ to get up and running.

> If you are using one then I am pretty sure that you don’t even have a User table in your database. Did I guess right? 🤨

![Shocked](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExa3p3dmp2cmh4enJneW11eDBmejB4cm52enlobnkzbjNrZzllb3VxdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/KpSrCxhoZQWjJVVnrd/giphy.gif)

If this realization hits 🫠, then continue with the article as I show you how to safely keep a backup of the user’s data.

***

## Setting up the Project with Kinde 🚀

> ℹ️ If you already have a project that uses an Auth Provider, feel free to skip this section.

I will show you an example in a sample **Next.js** application with one of the popular auth provider called [Kinde](https://kinde.com).

The steps are going to be fairly the same when using any other providers as well.

Run the following command to bootstrap a new Next.js application with **Tailwind**, **Eslint** and **Typescript** support:

```bash
bunx create-next-app@latest --tailwind --eslint --typescript
```

> The above command uses [bun](https://bun.sh/) as the package manager. If you don’t have it installed, you can go ahead with npm, pnpm or yarn.

### Setting up Kinde Authentication

Make sure to have the necessary kinde package installed with the below command:

```bash
bun i @kinde-oss/kinde-auth-nextjs
```

Create a new project in Kinde and copy all the environment variables into `.env` file in your project.

```
KINDE_CLIENT_ID=<your_kinde_client_id>
KINDE_CLIENT_SECRET=<your_kinde_client_secret>
KINDE_ISSUER_URL=https://<your_kinde_subdomain>.kinde.com
KINDE_SITE_URL=http://localhost:3000
KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
KINDE_POST_LOGIN_REDIRECT_URL=http://localhost:3000/dashboard
```

Note the `KINDE_POST_LOGIN_REDIRECT_URL` variable. This variable ensures that once the user is authenticated in Kinde, they are redirected to the `/dashboard` endpoint.

Make sure to change it according to your needs. Our code assumes that the user is redirected to `/dashboard` once they successfully log in.

Now, we need to set up the Kinde Auth Router Handlers. In the `app/app/api/auth/[kindeAuth]/route.ts`, add the following code:

```typescript
import {handleAuth} from "@kinde-oss/kinde-auth-nextjs/server";
export const GET = handleAuth();
```

This will setup the necessary route handler to add Kinde Authentication to our application.

***

## Setting up the Database Model 🛠️

> ℹ️ I will be using MongoDB as the database and Prisma as the ORM. If you prefer any other Prisma alternatives like Drizzle or Mongoose, feel free to proceed with them.

Run the following command to install Prisma as a development dependency:

```bash
bun i prisma @prisma/client --save-dev
```

Now, initialize Prisma with the following command:

```bash
bunx prisma init
```

After you run this command, a new `schema.prisma` file should be created in the `prisma` folder at the root of your project.

Modify the `schema.prisma` file to include a new User model. The fields in the model can vary based on the information your auth provider provides upon successful user creation.

```Prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}

model User {
  id       String  @id @map("_id") @db.String
  email    String  @unique
  username String  @unique
  name     String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

// If you are adding this change on top of your exising Prisma Schema,
// you will have the rest of your models here...
```

Now that we have our model ready, we need to push it to our database. For this, we need the connection URL.

If you already have a connection URL, that's great. If not, and you're following along, create a new cluster in **MongoDB Atlas** and obtain the database connection string. Then, add a new variable named `DATABASE_URL` with the connection string value to the `.env` file.

```env
DATABASE_URL=<db-connection-string>

// Rest of the environment variables...
```

Now, we need to setup the `PrismaClient` which we can use to query our database. Create a new file `index.ts` in the `/src/db` directory with the following lines of code:

```typescript
import { PrismaClient } from "@prisma/client";

declare global {
  // eslint-disable-next-line no-var
  var cachedPrisma: PrismaClient;
}

let prisma: PrismaClient;

if (process.env.NODE_ENV === "production") prisma = new PrismaClient();
else {
  if (!global.cachedPrisma) global.cachedPrisma = new PrismaClient();
  prisma = global.cachedPrisma;
}

export const db = prisma;
```

In development environment, the code initializes `PrismaClient` once and caches it globally to optimize resource usage. In production, it creates a new `PrismaClient` instance per request.

Run the following command to push your changes in your schema to the database.

```bash
bunx prisma db push
```

Now, to have the updated types work in the IDE, run the following command to generate new types based on our updated schema.

```bash
bunx prisma generate
```

Now, this is all that we need to setup our application database and the authentication part.

***

## Setting up the Backup 📥

> ℹ️ Everything we've done up to this step is about setting up the basic project structure. In this section, we will look into the main logic of how to store user information once the user signs up for the first time in our application.

This is how the user data backup to our database architecture is going to look like:

![Project auth callback architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2lgsi9hud66bxei91d7.png)

Every time a new user signs up, they are redirected to the `/dashboard` page. There, we check if the user exists in our database. If not, they are redirected to the `/auth/callback` endpoint, and the user is created in our database. If they exist, the application continues as usual.

In the root `page.tsx` file add these lines of code:

> ℹ️ I am using Kinde as an auth provider. The code to check user authentication will vary depending on the one you are using but the logic should be the same. If you are following along, copy and paste this code.

```typescript
"use client";

import {
  LoginLink,
  LogoutLink,
  useKindeBrowserClient,
} from "@kinde-oss/kinde-auth-nextjs";

export default function Home() {
  const { isAuthenticated } = useKindeBrowserClient();

  return (
    <main className="flex justify-center p-24">
      {!isAuthenticated ? (
        <LoginLink className="p-10 text-zinc-900 text-2xl font-semibold rounded-lg bg-zinc-100">
          Log in
        </LoginLink>
      ) : (
        <LogoutLink className="p-10 text-zinc-900 text-2xl font-semibold rounded-lg bg-zinc-100">
          Log out
        </LogoutLink>
      )}
    </main>
  );
}
```

This is the home page of our application, with only the **login** and **logout** buttons, based on whether the user is authenticated.

Once the user successfully logs in, they will be redirected to the `/dashboard` page.

Add the following lines of code in the `/app/dashboard/page.tsx` file:

```typescript
import { db } from "@/db";
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { redirect } from "next/navigation";

const Page = async () => {
  const { getUser } = getKindeServerSession();
  const user = await getUser();

  if (!user?.id) redirect("/api/auth/login");

  const userInDB = await db.user.findUnique({
    where: {
      id: user.id,
    },
  });

  if (!userInDB) redirect("/auth/callback");

  return (
    <div className="flex flex-col justify-center items-center sm:mt-36 w-full mt-20">
      <h1 className="font-semibold text-zinc-900 text-2xl">
        You are authenticated
      </h1>
      <p className="font-medium text-xl text-zinc-700 text-center">
        The user has also been created in the DB
      </p>
    </div>
  );
};

export default Page;
```

We check if the user is authenticated or not. If not, we redirect the user to the Kinde login page.

If they are authenticated but the user does not exist in the database, we redirect them to the `/auth/callback` endpoint where a new user is created in our database with the current logged-in user details.

Add the following lines of code in the `/src/app/auth/callback/page.tsx`:

```typescript
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
import { redirect } from "next/navigation";
import { db } from "@/db";

const Page = async () => {
  const { getUser } = getKindeServerSession();
  const user = await getUser();

  if (!user?.id || !user?.email) redirect("/");

  const name =
    user?.given_name && user?.family_name
      ? `${user.given_name} ${user.family_name}`
      : user?.given_name || null;

  const userInDB = await db.user.findFirst({
    where: {
      id: user.id,
    },
  });

  if (!userInDB) {
    await db.user.create({
      data: {
        id: user.id,
        email: user.email,
        ...(name && { name }),
      },
    });
  }

  redirect("/dashboard");
};

export default Page;
```

Here, we check if the user is in our database. If they exist, we redirect them to the `/dashboard` page. If the user does not exist, we create a new user in our database with their details and then redirect to the `/dashboard` page.

That's it! 🎉 These steps ensure that the user details in the auth provider are synced with our database and are not only stored in the auth provider.

***

## **Wrap Up!** ✨

By now, you have a general idea of how you can backup your user information in your own database when you are using an Authentication Provider.

The documented source code for this article is available here:

https://github.com/shricodev/blogs/tree/main/auth-callback-auth-provider

Thank you so much for reading! 🎉 🫡

> Drop down your thoughts in the comment section below. 👇

{% cta https://linktr.ee/shricodev
 %} Follow me on Socials ✌️ {% endcta %}

{% embed https://dev.to/shricodev %}