vercel / ai-chatbot

A full-featured, hackable Next.js AI chatbot built by Vercel
https://chat.vercel.ai
Other
5.6k stars 1.67k forks source link

Authentication not working #262

Open rogerodipo opened 4 months ago

rogerodipo commented 4 months ago

Authentication not working - results in Something went wrong, please try again!

jeremyphilemon commented 4 months ago

@rogerodipo can you share more details?

Are you seeing this error

JazzuLu commented 4 months ago

the same error. when i all env set & deploy success. it still cannot signup and login.

JazzuLu commented 4 months ago

I understand why this issue occurred. It's because the 'users' table was not initialized during project setup. The project uses Vercel/PostgreSQL, which is a relational database. Therefore, at the very beginning, we need to check if the 'users' table exists, and if not, create a new one. Code maybe like this: Method in app/signup/actions.ts:

async function checkIfUsersTableExists() {
  const client = await db.connect()
  try {
    const res = await client.sql`
        SELECT EXISTS (SELECT 1
                       FROM information_schema.tables
                       WHERE table_schema = 'public'
                         AND table_name = 'users');
    `
    return res.rows[0].exists;
  } finally {
    client.release();
  }
}

async function createUsersTable() {
  const client = await db.connect()
  try {
    await client.query(`
        CREATE TABLE IF NOT EXISTS users
        (
            id
            SERIAL
            PRIMARY
            KEY,
            email
            TEXT
            UNIQUE
            NOT
            NULL,
            password
            TEXT
            NOT
            NULL,
            salt
            TEXT
            NOT
            NULL
        );
    `);
  } finally {
    client.release();
  }
}

All code in app/signup/actions.ts:

'use server'

import {signIn} from '@/auth'
import {db} from '@vercel/postgres'
import {getStringFromBuffer} from '@/lib/utils'
import {z} from 'zod'
import {AuthResult} from '@/lib/types'

async function checkIfUsersTableExists() {
  const client = await db.connect()
  try {
    const res = await client.sql`
        SELECT EXISTS (SELECT 1
                       FROM information_schema.tables
                       WHERE table_schema = 'public'
                         AND table_name = 'users');
    `
    return res.rows[0].exists;
  } finally {
    client.release();
  }
}

async function createUsersTable() {
  const client = await db.connect()
  try {
    await client.query(`
        CREATE TABLE IF NOT EXISTS users
        (
            id
            SERIAL
            PRIMARY
            KEY,
            email
            TEXT
            UNIQUE
            NOT
            NULL,
            password
            TEXT
            NOT
            NULL,
            salt
            TEXT
            NOT
            NULL
        );
    `);
  } finally {
    client.release();
  }
}

export async function signup(
  _prevState: AuthResult | undefined,
  formData: FormData
) {
  const email = formData.get('email') as string
  const password = formData.get('password') as string

  const parsedCredentials = z
    .object({
      email: z.string().email(),
      password: z.string().min(6)
    })
    .safeParse({
      email,
      password
    })

  console.log('parsedCredentials::', parsedCredentials)

  if (!await checkIfUsersTableExists()) {
    console.log('creating users table')
    await createUsersTable()
  }

  if (parsedCredentials.success) {
    const salt = crypto.randomUUID()

    const encoder = new TextEncoder()
    const saltedPassword = encoder.encode(password + salt)
    const hashedPasswordBuffer = await crypto.subtle.digest(
      'SHA-256',
      saltedPassword
    )
    const hashedPassword = getStringFromBuffer(hashedPasswordBuffer)

    const client = await db.connect()

    try {
      await client.sql`
          INSERT INTO users (email, password, salt)
          VALUES (${email}, ${hashedPassword}, ${salt}) ON CONFLICT (id) DO NOTHING;
      `

      await signIn('credentials', {
        email,
        password,
        redirect: false
      })

      return {type: 'success', message: 'Account created!'}
    } catch (error) {
      const {message} = error as Error

      if (
        message.startsWith('duplicate key value violates unique constraint')
      ) {
        return {type: 'error', message: 'User already exists! Please log in.'}
      } else {
        return {
          type: 'error',
          message: 'Something went wrong! Please try again.'
        }
      }
    } finally {
      client.release()
    }
  } else {
    return {
      type: 'error',
      message: 'Invalid entries, please try again!'
    }
  }
}
rogerodipo commented 4 months ago

With the new setup. I can't seem to sign people up. I get this error

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'previousSibling') at CollectAutofillContentService.recursivelyGetTextFromPreviousSiblings (bootstrap-autofill-overlay.js:4710:30) at CollectAutofillContentService.createAutofillFieldLeftLabel (bootstrap-autofill-overlay.js:4603:39) at CollectAutofillContentService. (bootstrap-autofill-overlay.js:4174:40) at Generator.next () at fulfilled (bootstrap-autofill-overlay.js:4104:58)