Next.js Discord

Discord Forum

Implementing onboarding using next-auth

Unanswered
Siamese Crocodile posted this in #help-forum
Open in Discord
Avatar
Siamese CrocodileOP
I want the user to be redirected to /onboarding after they've signed up for the first time using socials (google or github). Next Auth is a little hard for me to integrate so I'd really love some help!

import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { NextAuthOptions } from "next-auth";
import { db } from "./db";

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(db),
  session: {
    strategy: "jwt",
  },
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
};
this is my auth.ts file's code

43 Replies

Avatar
Siamese CrocodileOP
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url = env("DATABASE_URL")
  relationMode = "prisma"
}

model Account {
  id                 String  @id @default(cuid())
  userId             String
  type               String
  provider           String
  providerAccountId  String
  refresh_token      String?  @db.Text
  access_token       String?  @db.Text
  expires_at         Int?
  token_type         String?
  scope              String?
  id_token           String?  @db.Text
  session_state      String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
  hasOnboarded  Boolean   @default(false)
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}
this is my schema file that I just copied from the nextauth docs
please note that i've added another item called hasOnboarded Boolean @default(false) to the User model to check whether the user has onboarded or not.
I want to set the hasOnboarded value to true if the use has onboarded and then redirect them to the homepage everytime they access the onboard page unless they're signing up for the first time
how do I implement this?
I don't really know how to continue after this
I'm not using credentials login
Avatar
ncls.
You can setup a page where users will be sent after they first sign up in your NextAuth config. It's called newUser. [Reference](https://next-auth.js.org/configuration/pages)
Avatar
Siamese CrocodileOP
I have an onboarding page already setup. It's endpoint is '/onboarding'. I also have a page setup for users to signin. I just don't know how to proceed.
currently I only want them to sign in with their socials
Image
Avatar
ncls.
Define a newUser page in your NextAuth config. Check the reference I linked
Avatar
Siamese CrocodileOP
my bad. one sec
Avatar
ncls.
NextAuth will automatically check if they already signed up before or not and if not, redirect them to the page you defined
Avatar
Siamese CrocodileOP
oh, so I don't need to add another query to the table such has "hasOnboarded", right?
Avatar
ncls.
Nope, you don't
Avatar
Siamese CrocodileOP
got it
auth seems soo hard to setup and the nextauth docs seem like they arent updated for the app router and its a nightmare.
Avatar
ncls.
Not much has changed for use in the app router. I'm using app router as well and most things still apply
Avatar
Siamese CrocodileOP
I scrapped many projects in the last 6 months because I got stuck with authentication which made me go back to the tutorial hell
Avatar
ncls.
Authentication with NextAuth is actually pretty easy once you understood how it works
Avatar
Siamese CrocodileOP
I have another question
one sec
import { db } from '@/lib/db'
import { PrismaAdapter } from '@next-auth/prisma-adapter'
import { nanoid } from 'nanoid'
import { NextAuthOptions, getServerSession } from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(db),
  session: {
    strategy: 'jwt',
  },
  pages: {
    signIn: '/sign-in',
  },
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async session({ token, session }) {
      if (token) {
        session.user.id = token.id
        session.user.name = token.name
        session.user.email = token.email
        session.user.image = token.picture
        session.user.username = token.username
      }

      return session
    },

    async jwt({ token, user }) {
      const dbUser = await db.user.findFirst({
        where: {
          email: token.email,
        },
      })

      if (!dbUser) {
        token.id = user!.id
        return token
      }

      if (!dbUser.username) {
        await db.user.update({
          where: {
            id: dbUser.id,
          },
          data: {
            username: nanoid(10),
          },
        })
      }

      return {
        id: dbUser.id,
        name: dbUser.name,
        email: dbUser.email,
        picture: dbUser.image,
        username: dbUser.username,
      }
    },
    redirect() {
      return '/'
    },
  },
}

export const getAuthSession = () => getServerSession(authOptions)
this is the route.ts file i found on a github repo (Code with Antonio's Reddit clone tutorial)
why exactly are we doing what we're doing in the callback functions?
for example, I'm trying to build a community platform for my university and i want the user to have data like their Fullname, their semester, their engineering branch, whether or not they're a student or a faculty member and all that stuff that they have to fill in on the onboarding page. Only then i want them to be able to see the dashboard (home) page
Avatar
ncls.
You are adding custom properties. By default, NextAuth will store 3 things in your session/token: email, name and image which are all coming from the OAuth provider the user used.
If you want to add custom properties from your database, like username, your own image, access roles (admin, moderator etc.), you add them inside the callback functions
Avatar
Siamese CrocodileOP
async session({ token, session }) {
      if (token) {
        session.user.id = token.id
        session.user.name = token.name
        session.user.email = token.email
        session.user.image = token.picture
        session.user.username = token.username
      }

      return session
    },
in this function, where are the parameters token and session coming from? is token coming from the prisma schema?
i don't understand why we're assigning each session's value to the token
Avatar
ncls.
token is the token NextAuth created on sign up and session is the user session which you can get on the client side using the useSession hook
Avatar
Siamese CrocodileOP
understood
Avatar
ncls.
Because on the client side you can not decrypt the token but you can use the session so everything you want to use on the client side has to be added to the session
Avatar
Siamese CrocodileOP
so all the stuff on the left i.e, .id, . name, .email etc and stuff is coming from the prisma schema file, right?
and all the stuff on the right side are coming from the token that is created by nextauth?
Avatar
ncls.
Kind of
The token NextAuth creates only includes name, email and image. Everything else is added in the jwt callback from your database. You can then add those values into the session as well using the session callback
Avatar
Siamese CrocodileOP
return {
        id: dbUser.id,
        name: dbUser.name,
        email: dbUser.email,
        picture: dbUser.image,
        username: dbUser.username,
      }
this return statement from the jwt callback function, right?
Avatar
ncls.
Yes, that will be your JWT payload
Avatar
Siamese CrocodileOP
ohhh----
Avatar
ncls.
And that will be what your token variable will include in your session callback
Avatar
Siamese CrocodileOP
okay wow! thank you for that. now it kinda makes sense. the payload from jwt callback is assigned to our session which can then be used across our web app
redirect() {
      return '/'
    },
when I redirect a new user to /onboarding using the newUser object, which one takes precedence? will the newuser be redirected to '/' or '/onboarding'?
Avatar
ncls.
I actually don't know for custom redirects. I suggest letting NextAuth handle the redirect on sign in
Avatar
Siamese CrocodileOP
okay i made it work a little @ncls.
the user is successfully redirected to the onboarding page after authenticating