#Next Auth authentication

245 messages · Page 1 of 1 (latest)

gilded canyon
#

So, i have been trying to figure this out all day. I'm freshly new to NextJS and im surprised i even got this far and always got confused on the session signup.

using Username,Password to login. That's working but can't get it to save to the session.

ideas?

import type { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcrypt";
import User from '../../../../models/user'


export const options: NextAuthOptions = {
    providers: [
        CredentialsProvider({
            name: "Credentials",
            credentials: {
                username: { label: 'Username', type: 'text', placeholder: 'Username' },
                password: { label: 'Password', type: 'password', placeholder: 'Password' }
            },
            async authorize(credentials, req) {
                const userCheck = await User.findOne({ where: { username: credentials?.username } })
                if (userCheck) {
                    bcrypt.compare(credentials?.password, userCheck.hashPassword, function (err, result) {
                        if (result === true) {
                            console.log(JSON.stringify(userCheck))
                            return JSON.stringify(userCheck);

                        } else throw Error('Sorry, password doesn\'t match')
                    })
                } else throw Error('Sorry, login failed')
            }
        })
    ],
    session: {
        strategy: 'jwt'
    },
    callbacks: {
        async signIn({ user, account, profile, email, credentials }) {
            return true
        }
    }
}
brittle merlinBOT
#

🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord

      🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in <id:customize>

      ✅ You can mark a message as the answer for your post with `Right click -> Apps -> Mark Solution`
      (if you don't see the option, try refreshing Discord with Ctrl + R)
timid stream
gilded canyon
#

Well, i probably need to save the username, email and id to the session? Just new to this stuff so it will take me a bit to understand it.

#

cause right now im getting spammed with

timid stream
timid stream
gilded canyon
#

That's weird as bcrypt is only called during the register and login. that's it

timid stream
#

I just googled your error and bcrypt turned up several times. Maybe it's something else but the error just honestly doesn't tell much

gilded canyon
#

then it's complaining about FS not being installed... but it is.

#

(╯°□°)╯︵ ┻━┻

#

i really wish i don't wanna restart this entire thing again :v

timid stream
#

Where are you requiring fs?

gilded canyon
#

No where.

#

It honestly could be my sequilite setup. as it popped up afterwords

timid stream
#

I never used SQLite

#

Can you show the fs error?

gilded canyon
#

not using SQLite 😛 using Mariadb with it

timid stream
#

Alright then. I use MariaDB as well with Prisma

gilded canyon
#

Yea... i tried prisma. I couldn't use it like sequilize for example user.ROW

gilded canyon
#

basically using the database reply with a spacific ROW (like password for example) and compare my bcrypt

timid stream
#

So in raw SQL something like SELECT password FROM users?

gilded canyon
#

Pretty much, for Prisma i was checking the username, then if it existed it would do the becrypt compare. couldn't get that part working so i converted over

timid stream
#

That's how you select specific rows in Prisma

const user = await prisma.users.findFirst({
  where: {
    username: username
  },
  select: {
    password: true
  }
});
gilded canyon
#

Then using it in bcrypt would be

bcrypt.compare(oldpassword, storePassword)``` and that part was causing me issues.
timid stream
#

Wait, I'll send you my authorize function so you can see how I did it

#
async authorize(credentials, req) {
  const { login, password } = credentials!;

  const user = await prisma.user.findFirst({
    where: {
      OR: [
        { email: login },
        { username: login },
      ],
    },
  });

  if (!user) return null;

  if (await bcrypt.compare(password, user.password)) {
    return {
      id: user.id,
      email: user.email,
      username: user.username,
      name: user.displayName,
      isVerified: user.verified,
    };
  }
  return null;
}
gilded canyon
#

my guy.... where were you like 2 days ago when i was asking for help xD

timid stream
gilded canyon
#

Fair. that's a valide reply 😛

timid stream
#

But as for now it needs a little @ts-ignore above it because "Type (...) => Promise<..> is not assignable to type (...) => Awaitable<...>". Was too lazy to fix that. It works anyway🤷‍♂️

gilded canyon
#

Ok well, mind if i snatch that idea? it's actually close to what i do.

gilded canyon
#

wait... what's with the credentials!

timid stream
#

I actually don't know if credentials can be undefined or similar lol

gilded canyon
#

MY GUY i have been using findUnique instead of findFirst slams face on desk

gilded canyon
#

yeah, just the thing with the user.password, the password was always red squigly underneath

timid stream
#

Theoretically

timid stream
gilded canyon
#

just finished and cleared my cache

#

getting that when ever i load my dashboard part. it's supposed to throw me to the login page cause my session doesn't exist

timid stream
#

Can you show me your dashboard code?

gilded canyon
#

it's plain as fuck.

'use client'
import React from 'react'
import styles from './page.module.css';
import { redirect } from 'next/navigation';
import useSWR from 'swr';
import { options } from '../api/auth/[...nextauth]/options';
import { getServerSession } from 'next-auth';

export default async function Dashboard() {
    const session = await getServerSession(options)


    return (
        <>
            {session ? (
                <div className={styles.container}>Dashboard {session.user}</div>
            ) : (
                redirect('/api/auth/signin')
            )
            }
        </>
    )
}
#

i'm gonna expand it once i get this login shit working :v

timid stream
#

Get rid of all the checking if the user is logged in

#

You wanna use middleware for that

gilded canyon
#

i thought the middleware was that method.

timid stream
#

The issue is btw that you are importing the options from NextAuth into a client component

timid stream
gilded canyon
timid stream
#

Get rid of this import { options } from '../api/auth/[...nextauth]/options';

gilded canyon
#

just updated it too

'use client'
import React from 'react'
import styles from './page.module.css';

export default async function Dashboard() {


    return (

        <div className={styles.container}>Dashboard {session.user}</div>

    )
}
timid stream
#

One sec

gilded canyon
#

ok THERE we go dashboard loads properly now

timid stream
#

This is what you want to use to get the session:

import { useSession } from 'next-auth/react';

const { data: session } = useSession();
gilded canyon
#

React Context is unavailable in Server Components

#

(╯°□°)╯︵ ┻━┻

#

Well, before we so father in... Thank you for helping me with this.

timid stream
#

What's the current full code?

gilded canyon
#
import React from 'react'
import styles from './page.module.css';
import { useSession } from 'next-auth/react';

export default async function Dashboard() {
    const { data: session } = useSession()

    return (

        <div className={styles.container}>Dashboard {session}</div>

    )
}```
#

but i can remove {session} and it goes away

timid stream
#

Add the 'use client' on top again

gilded canyon
#

or not xD

#

Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.

timid stream
#

Oooooh

#

Didn't see it

#

Delete the async from the function declaration

gilded canyon
#

boom done

timid stream
#

Should work now

#

If it does, we gonna set up the middleware

gilded canyon
#

yes it does 🙂

timid stream
#

Perfect. Now create a file called middleware.ts inside the same folder app is in.

gilded canyon
#

so root directory

timid stream
#

Sure

#

If you're ready, tell me

gilded canyon
#

yeh ready. i have seen something like this

#

right now it's just dashboard, then later on i can add in more with it or just use like /dashboard/* to make it all?

#

already got a NEXTAUTH_SECRET thing in

#

and it's in .env.local or should it be just .env

timid stream
#

/dashboard* or what you said. Not entirely sure atm

timid stream
gilded canyon
#

nah it loads it, which is nice

timid stream
gilded canyon
#

yes

#

brain is fried for coding so much in the past week xD

timid stream
#

XD

#

Middleware is done?

gilded canyon
#
export { default } from 'next-auth/middleware'

export const config = { matcher: ['/dashboard'] }```
timid stream
#

Looks fine but this will just protect the exact route /dashboard for now so /dashboard/settings e.g. would still be accessable without being logged in

gilded canyon
#

yeh that's perfecty fine, once i make more pages i'll be securing it

timid stream
#

Alright then. Should be either /dashboard*, /dashboard(.*) or probably both work

#

Anyway. Did you test it?

gilded canyon
#

well, the page is still loading, just gotta enable it (i think)

timid stream
#

Enable it?

gilded canyon
#

oh fuck, register is breaking one sec

#

well, you can type, i just gotta figure out why this is broken so hard xD

timid stream
#

How is it broken?

gilded canyon
#

again :v

timid stream
#

Stop importing options from your NextAuth config XD

#

You don't need it

gilded canyon
#

im nooooooot

import { React, useState } from 'react'
import style from './page.module.css'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import PasswordChecklist from 'react-password-checklist'
import bcrypt from 'bcrypt'
timid stream
#

Ooooh, don't import bcrypt then

gilded canyon
#

I'm using bcrypt :v

#

i don't send naked passwords

timid stream
#

Yeah, but you don't wanna hash the password client side

gilded canyon
#

(╯°□°)╯︵ ┻━┻

timid stream
#

The connection between the client and the server is secured by SSL anyway. Hashing it is just for secure storing in the database

#

So you want to hash it on the server especially because you can check it there as well in case you want the password to have a specific format (yk, the include one number, one uppercase thing)

gilded canyon
#

ahhh, alright well gimme a bit i gotta do my backend

timid stream
#

Sure

gilded canyon
#

there, i think i got it

timid stream
#

Test it so that you know

gilded canyon
#

One thing i HATE about this right now. is that prisma caches it, even though the databse doesn't have the username/email anymore it thinks it is :v

timid stream
gilded canyon
#

eh?

#
import { NextResponse } from 'next/server';
import crypto from 'node:crypto'
import { PrismaClient } from '@prisma/client';
import sendEmail from '../../../../components/mail/mailing';
import bcrypt from 'bcrypt'



export const POST = async (request) => {
    const prisma = new PrismaClient()
    const { username, email, password } = await request.json();
    let verified = crypto.randomBytes(32).toString('hex');
    const verifyEmail = `http://localhost:3001/verify/${verified}`;
    const hashPassword = bcrypt.hash(password, 10)
    const userExist = await prisma.users.findFirst({ where: { OR: [{ username }, { email }] } })

    if (userExist === null) {
        await prisma.users.create({ data: { username, email, hashPassword, verified } })
        await sendEmail(email, 'Biscuits Industrial Signup verification', verifyEmail)
        await prisma.$disconnect()
        return new NextResponse('Success!', { status: 201 })
    } else {
        await prisma.$disconnect()
        return new NextResponse("Sorry, username and or email already exists!", { status: 500 })
    }
};
#

that verify email thing will be changed xD

timid stream
# gilded canyon eh?

Change your prisma.schema file and when you are done, use npx prisma db push in the terminal to sync your database according to your schema file and prisma will then generate a new client so that your typing will be up to date

gilded canyon
#

my database is setup the way i wanna have it anyways.

timid stream
#

So in that case, make sure your schema file fits your database before you push

gilded canyon
#

i have the mysql database linked and pulled cause it's setup already to what i would like to have 😛

#

so i basically did the schema setup, prisma db pull and generate. then boom it's ready.

timid stream
#

Then your typing should be correct. Is your registration working again?

gilded canyon
#

bcrypt hash is being annoying :v

#

DUR i didn't await it

timid stream
#

Why?

#

XD

#

hashSync exists as well🤷‍♂️

timid stream
#

Tell me when you tested everything so we can do the final touches to your login system which would be loading all additional information into the session/token

#

If I didn't miss anything

gilded canyon
#

well, dashboard doesn't throw people around. so not sure about that one.

timid stream
#

Can you show me your file structure?

gilded canyon
timid stream
#

You have a src folder

#

And your app folder is inside there

gilded canyon
#

Yerp

#

that's how it was created

timid stream
#

So your middleware.ts has to go there as well

gilded canyon
#

Ah, so it's in the layout.ts location

timid stream
#

It has to be where the app (or pages) folder is

timid stream
#

Wait, no

#

Outside the app folder

#

Inside the src folder

#

But not inside the app folder

#

It should be /src/middleware.ts

gilded canyon
#

alright it doesn't like the /dashboard* or /*

timid stream
#

Why not?

gilded canyon
#

just doesn't like it

#

THERE it goes, throws to the login 🙂

timid stream
#

What did you change?

#

The matcher in the middleware is very trivial sadly

gilded canyon
#

it's in /src folder and it's just /dashboard

timid stream
#

I think it should actually be /dashboard/:path* then

#

Or /dashboard/(.*)

#

Same thing actually

gilded canyon
#

damn it, now when i register, it throws the error cause it loops and just does the loop for the check xD

timid stream
#

LOL, fix it

gilded canyon
#

im tryin, im seeing errors in my console saying it's connection local BUT it actually does the registration xD

timid stream
gilded canyon
timid stream
#

Port 465?

gilded canyon
#

yeah not even using that xD

#

funny thing is, it creates the account THEN throws that

timid stream
#

Did you maybe add a wrong connection string or something somewhere in your code?

#

Maybe some leftovers from sequelize?

gilded canyon
#

OH 465 is my email service

timid stream
#

XD

#

Well, then it's fine

#

So we can move on?

gilded canyon
#

LOL it's cause i don't have the login creds in. One sec

#

gonna remove a .env file... not sure if .env or .env.local

#

there we go, that's fixed

timid stream
#

Alright

#

To get all the info, I needed a while as well. I think 2 or 3 hours to find the solution lol so I'm just gonna give it to you

#
callbacks: {
  jwt: async ({ token, user, account, profile, isNewUser }: any) => {
    if (user) {
      token.user = user;
    }
    if (account) {
      token.accessToken = account.access_token;
      token.refreshToken = account.refresh_token;
    }
    return token;
  },
  session: ({ session, token }: any) => {
    token.accessToken
    return {
      ...session,
      user: {
        ...session.user,
        ...token.user,
        accessToken: token.accessToken,
        refreshToken: token.refreshToken,
      },
    };
  },
},
#

user is what your authorize function returns

#

On a successful login ofc

gilded canyon
#

yea... i got nothing near that xD

timid stream
#

I copy and pasted that as well once I found it XD

#

And modified it a tiny bit to fit my needs

gilded canyon
#

well, hopefully it works the same way. basically using the same data, id, username,email and such :v

#

basically doing this to get Eve Online stuff working.

timid stream
#

Huh?

gilded canyon
#

That's another thing im gonna have to setup later in the future xD

#

ok, i tried the login, i got no idea if it works xD

#

the fuck?

timid stream
#

Interesting

#

404 or what?

gilded canyon
#

that was the redirect to that page

timid stream
#

Yeah, it's supposed to do that

gilded canyon
#

and if i go to the dashboard it won't let me in xD

timid stream
#

Can you reach the login?

#

You can't, right?

gilded canyon
#

res i can

timid stream
gilded canyon
#
"use client"
import { React, useState } from 'react'
import style from './page.module.css'
import Link from 'next/link'
import { signIn } from 'next-auth/react'

const Login = () => {

    const handleSubmit = async (e) => {
        e.preventDefault()

        const username = e.target[0].value
        const password = e.target[1].value
        signIn('Credentials', { username, password })


    }
    return (
        <div className={style.container}>
            Login :D
            <form className={style.form} onSubmit={handleSubmit}>
                <input
                    type='text'
                    placeholder='username'
                    className={style.input}
                    required
                />
                <input
                    type='password'
                    placeholder='password'
                    className={style.input}
                    required
                />
                <button className={style.button}>Login</button>
            </form>
            <Link href='/dashboard/register'>Need an account?</Link>

        </div>
    )
}

export default Login
timid stream
#

Oh

#

Well

#

You know

gilded canyon
#

that's a redirect to the login page of when i tried to login

timid stream
#

So when you login, what happens?

gilded canyon
#

just throws me back into the login page

timid stream
gilded canyon
#

i have

    pages: ({
        signIn: '/dashboard/login'
    })
``` already
timid stream
#

Alright then

#

So you login at /dashboard/login. And then it throws you to the same page again or to /api/auth/signin?

gilded canyon
#

http://localhost:3000/dashboard/login?callbackUrl=http%3A%2F%2Flocalhost%3A3000%2Fdashboard%2Flogin

timid stream
#

Oh, well then

gilded canyon
#

yerp.

timid stream
#

The callbackUrl is where it wants to redirect you after a successfull login. You're getting redirected to the login lol

#

But

#

I don't think that's the issue

timid stream
gilded canyon
#

It was website console

timid stream
#

Yes. It's a failed request to your auth API

#

I wanna know why it failed

#

Hopefully you just passed incorrect credentials

#

Maybe check that lol

#

You can put a bunch of console.logs inside your authorize function to check what exactly is happening

gilded canyon
#

well, my authorize it's got red squigly under it atm xD

timid stream
#

Show me

gilded canyon
gilded canyon
#

won't let me screen shot it, leaves too fast

#

ah fuck i probably forgot that

timid stream
#

Just add a // @ts-ignore above it

gilded canyon
#

would you like to do a video call

timid stream
#

Alright

gilded canyon
#

This was resolved in a DM video call Thank you!