rrsartneoai / ai-chatbot

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

Task Tile #2

Open rrsartneoai opened 4 months ago

rrsartneoai commented 4 months ago

What - description of what you me to do Example: Hey @autopilot implement a Google sign-in on my website. Make changes to the front end and the back end of the application

Why - explain why this is important Example: I want to allow users to signup and login using their Google account

codeautopilot[bot] commented 4 months ago

Potential solution

To implement Google sign-in on your website, we need to make changes to both the front end and the back end of the application. This involves updating the login button component to include a Google sign-in option, configuring the authentication settings to support Google OAuth, and ensuring the necessary dependencies are installed.

How to implement

1. Update components/login-button.tsx

We need to update the LoginButton component to include a new button for Google sign-in. This involves updating the LoginButtonProps interface, modifying the component to handle Google sign-in logic, and adding a new button for Google sign-in.

Updated Code:

'use client'

import * as React from 'react'
import { signIn } from 'next-auth/react'

import { cn } from '@/lib/utils'
import { Button, type ButtonProps } from '@/components/ui/button'
import { IconGitHub, IconGoogle, IconSpinner } from '@/components/ui/icons'

interface LoginButtonProps extends ButtonProps {
  showGithubIcon?: boolean
  showGoogleIcon?: boolean
  text?: string
  provider?: 'github' | 'google'
}

export function LoginButton({
  text = 'Login with GitHub',
  showGithubIcon = true,
  showGoogleIcon = false,
  provider = 'github',
  className,
  ...props
}: LoginButtonProps) {
  const [isLoading, setIsLoading] = React.useState(false)
  return (
    <Button
      variant="outline"
      onClick={() => {
        setIsLoading(true)
        signIn(provider, { callbackUrl: `/` })
      }}
      disabled={isLoading}
      className={cn(className)}
      {...props}
    >
      {isLoading ? (
        <IconSpinner className="mr-2 animate-spin" />
      ) : provider === 'github' && showGithubIcon ? (
        <IconGitHub className="mr-2" />
      ) : provider === 'google' && showGoogleIcon ? (
        <IconGoogle className="mr-2" />
      ) : null}
      {text}
    </Button>
  )
}

Usage Example:

<LoginButton
  text="Login with Google"
  showGoogleIcon={true}
  provider="google"
/>

2. Update auth.config.ts

We need to add the Google OAuth provider configuration to the auth.config.ts file.

Updated Code:

import type { NextAuthConfig } from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'

export const authConfig = {
  secret: process.env.AUTH_SECRET,
  pages: {
    signIn: '/login',
    newUser: '/signup'
  },
  callbacks: {
    async authorized({ auth, request: { nextUrl } }) {
      const isLoggedIn = !!auth?.user
      const isOnLoginPage = nextUrl.pathname.startsWith('/login')
      const isOnSignupPage = nextUrl.pathname.startsWith('/signup')

      if (isLoggedIn) {
        if (isOnLoginPage || isOnSignupPage) {
          return Response.redirect(new URL('/', nextUrl))
        }
      }

      return true
    },
    async jwt({ token, user }) {
      if (user) {
        token = { ...token, id: user.id }
      }

      return token
    },
    async session({ session, token }) {
      if (token) {
        const { id } = token as { id: string }
        const { user } = session

        session = { ...session, user: { ...user, id } }
      }

      return session
    }
  },
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET
    })
  ]
} satisfies NextAuthConfig

Environment Variables:

AUTH_SECRET=your_auth_secret
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret

3. Update auth.ts

We need to integrate Google OAuth into the existing authentication logic by updating the auth.ts file.

Updated Code:

import NextAuth from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import GoogleProvider from 'next-auth/providers/google'
import { authConfig } from './auth.config'
import { z } from 'zod'
import { getStringFromBuffer } from './lib/utils'
import { getUser } from './app/login/actions'

export const { auth, signIn, signOut } = NextAuth({
  ...authConfig,
  providers: [
    Credentials({
      async authorize(credentials) {
        const parsedCredentials = z
          .object({
            email: z.string().email(),
            password: z.string().min(6)
          })
          .safeParse(credentials)

        if (parsedCredentials.success) {
          const { email, password } = parsedCredentials.data
          const user = await getUser(email)

          if (!user) return null

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

          if (hashedPassword === user.password) {
            return user
          } else {
            return null
          }
        }

        return null
      }
    }),
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET
    })
  ],
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      return true
    },
    async redirect({ url, baseUrl }) {
      return baseUrl
    },
    async session({ session, user }) {
      return session
    },
    async jwt({ token, user, account, profile, isNewUser }) {
      return token
    }
  }
})

4. Update package.json

We need to add the necessary dependencies for Google OAuth and NextAuth.

Updated Code:

{
  "dependencies": {
    "next-auth": "5.0.0-beta.4",
    "@next-auth/google": "^1.0.0"
  }
}

Install Dependencies:

pnpm install

5. Update components/login-form.tsx

We need to include the Google sign-in button in the login form.

Updated Code:

'use client'

import { useFormState, useFormStatus } from 'react-dom'
import { authenticate } from '@/app/login/actions'
import Link from 'next/link'
import { useEffect } from 'react'
import { toast } from 'sonner'
import { IconSpinner } from './ui/icons'
import { getMessageFromCode } from '@/lib/utils'
import { useRouter } from 'next/navigation'
import { LoginButton } from './login-button'

export default function LoginForm() {
  const router = useRouter()
  const [result, dispatch] = useFormState(authenticate, undefined)

  useEffect(() => {
    if (result) {
      if (result.type === 'error') {
        toast.error(getMessageFromCode(result.resultCode))
      } else {
        toast.success(getMessageFromCode(result.resultCode))
        router.refresh()
      }
    }
  }, [result, router])

  return (
    <form
      action={dispatch}
      className="flex flex-col items-center gap-4 space-y-3"
    >
      <div className="w-full flex-1 rounded-lg border bg-white px-6 pb-4 pt-8 shadow-md  md:w-96 dark:bg-zinc-950">
        <h1 className="mb-3 text-2xl font-bold">Please log in to continue.</h1>
        <div className="w-full">
          <div>
            <label
              className="mb-3 mt-5 block text-xs font-medium text-zinc-400"
              htmlFor="email"
            >
              Email
            </label>
            <div className="relative">
              <input
                className="peer block w-full rounded-md border bg-zinc-50 px-2 py-[9px] text-sm outline-none placeholder:text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950"
                id="email"
                type="email"
                name="email"
                placeholder="Enter your email address"
                required
              />
            </div>
          </div>
          <div className="mt-4">
            <label
              className="mb-3 mt-5 block text-xs font-medium text-zinc-400"
              htmlFor="password"
            >
              Password
            </label>
            <div className="relative">
              <input
                className="peer block w-full rounded-md border bg-zinc-50 px-2 py-[9px] text-sm outline-none placeholder:text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950"
                id="password"
                type="password"
                name="password"
                placeholder="Enter password"
                required
                minLength={6}
              />
            </div>
          </div>
        </div>
        <LoginButton />
        <LoginButton
          text="Login with Google"
          showGoogleIcon={true}
          provider="google"
        />
      </div>

      <Link
        href="/signup"
        className="flex flex-row gap-1 text-sm text-zinc-400"
      >
        No account yet? <div className="font-semibold underline">Sign up</div>
      </Link>
    </form>
  )
}

function LoginButton() {
  const { pending } = useFormStatus()

  return (
    <button
      className="my-4 flex h-10 w-full flex-row items-center justify-center rounded-md bg-zinc-900 p-2 text-sm font-semibold text-zinc-100 hover:bg-zinc-800 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200"
      aria-disabled={pending}
    >
      {pending ? <IconSpinner /> : 'Log in'}
    </button>
  )
}

By following these steps, you will successfully implement Google sign-in functionality on your website.

Click here to create a Pull Request with the proposed solution

Files used for this task:

Changes on components/login-button.tsx To add a new button for Google sign-in in the `components/login-button.tsx` file, you need to make the following changes: 1. **Update the `LoginButtonProps` interface** to include a new prop for Google sign-in. 2. **Modify the `LoginButton` component** to handle the Google sign-in logic. 3. **Add a new button for Google sign-in** with the necessary `onClick` handler to initiate the Google OAuth flow. Here's the updated code: ```typescript 'use client' import * as React from 'react' import { signIn } from 'next-auth/react' import { cn } from '@/lib/utils' import { Button, type ButtonProps } from '@/components/ui/button' import { IconGitHub, IconGoogle, IconSpinner } from '@/components/ui/icons' // Assuming you have an IconGoogle component interface LoginButtonProps extends ButtonProps { showGithubIcon?: boolean showGoogleIcon?: boolean // New prop for Google icon text?: string provider?: 'github' | 'google' // New prop to specify the provider } export function LoginButton({ text = 'Login with GitHub', showGithubIcon = true, showGoogleIcon = false, // Default to false provider = 'github', // Default to GitHub className, ...props }: LoginButtonProps) { const [isLoading, setIsLoading] = React.useState(false) return ( ) } ``` ### Explanation: 1. **Interface Update**: Added `showGoogleIcon` and `provider` props to the `LoginButtonProps` interface. 2. **Component Update**: - Added a new prop `provider` to specify which OAuth provider to use (`github` or `google`). - Modified the `onClick` handler to use the `provider` prop. - Added conditional rendering for the Google icon using `showGoogleIcon`. ### Usage Example: To use the updated `LoginButton` component for Google sign-in, you can now specify the `provider` and `showGoogleIcon` props: ```typescript ``` This will render a button that initiates the Google OAuth flow when clicked.
Changes on auth.config.ts To add Google OAuth provider configuration to the `auth.config.ts` file, you need to include the Google provider with the necessary client ID and client secret. Here's a step-by-step guide to achieve this: 1. **Install the necessary dependencies**: Ensure you have the `next-auth` and `@next-auth/google` packages installed. If not, you can add them to your `package.json` and run `npm install` or `yarn add`. ```bash npm install next-auth @next-auth/google ``` 2. **Update the `auth.config.ts` file**: Modify the file to include the Google provider configuration. You'll need to add the client ID and client secret, which you can obtain from the Google Developer Console. ```typescript import type { NextAuthConfig } from 'next-auth' import GoogleProvider from 'next-auth/providers/google' export const authConfig = { secret: process.env.AUTH_SECRET, pages: { signIn: '/login', newUser: '/signup' }, callbacks: { async authorized({ auth, request: { nextUrl } }) { const isLoggedIn = !!auth?.user const isOnLoginPage = nextUrl.pathname.startsWith('/login') const isOnSignupPage = nextUrl.pathname.startsWith('/signup') if (isLoggedIn) { if (isOnLoginPage || isOnSignupPage) { return Response.redirect(new URL('/', nextUrl)) } } return true }, async jwt({ token, user }) { if (user) { token = { ...token, id: user.id } } return token }, async session({ session, token }) { if (token) { const { id } = token as { id: string } const { user } = session session = { ...session, user: { ...user, id } } } return session } }, providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET }) ] } satisfies NextAuthConfig ``` 3. **Set environment variables**: Ensure you have the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` set in your environment variables. You can add them to your `.env` file: ```env AUTH_SECRET=your_auth_secret GOOGLE_CLIENT_ID=your_google_client_id GOOGLE_CLIENT_SECRET=your_google_client_secret ``` By following these steps, you will have successfully added the Google OAuth provider configuration to your `auth.config.ts` file. This will allow users to sign in using their Google accounts.
Changes on auth.ts To integrate Google OAuth into your existing authentication logic, you'll need to update the `auth.ts` file to include the Google OAuth provider. Here's a step-by-step guide to achieve this: 1. **Install Dependencies**: Ensure you have the necessary dependencies installed. You can add them to your `package.json` and run `npm install` or `yarn add`: ```json "dependencies": { "next-auth": "^4.0.0", "@next-auth/google": "^4.0.0" } ``` 2. **Update `auth.ts`**: Modify the `auth.ts` file to include the Google OAuth provider configuration. You'll need to import the Google provider from `next-auth/providers/google` and add it to the `providers` array. Here's the updated code for `auth.ts`: ```typescript import NextAuth from 'next-auth' import Credentials from 'next-auth/providers/credentials' import GoogleProvider from 'next-auth/providers/google' import { authConfig } from './auth.config' import { z } from 'zod' import { getStringFromBuffer } from './lib/utils' import { getUser } from './app/login/actions' export const { auth, signIn, signOut } = NextAuth({ ...authConfig, providers: [ Credentials({ async authorize(credentials) { const parsedCredentials = z .object({ email: z.string().email(), password: z.string().min(6) }) .safeParse(credentials) if (parsedCredentials.success) { const { email, password } = parsedCredentials.data const user = await getUser(email) if (!user) return null const encoder = new TextEncoder() const saltedPassword = encoder.encode(password + user.salt) const hashedPasswordBuffer = await crypto.subtle.digest( 'SHA-256', saltedPassword ) const hashedPassword = getStringFromBuffer(hashedPasswordBuffer) if (hashedPassword === user.password) { return user } else { return null } } return null } }), GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET }) ], callbacks: { async signIn({ user, account, profile, email, credentials }) { // Custom logic to handle user sign-in return true }, async redirect({ url, baseUrl }) { // Custom logic to handle redirects return baseUrl }, async session({ session, user }) { // Custom logic to handle session return session }, async jwt({ token, user, account, profile, isNewUser }) { // Custom logic to handle JWT return token } } }) ``` ### Explanation: 1. **Import GoogleProvider**: Import the Google provider from `next-auth/providers/google`. 2. **Add GoogleProvider to Providers Array**: Add the Google provider to the `providers` array with `clientId` and `clientSecret` fetched from environment variables. 3. **Callbacks**: Optionally, you can add custom logic in the `callbacks` section to handle sign-in, redirect, session, and JWT. ### Environment Variables: Ensure you have the Google OAuth credentials set in your environment variables: ```env GOOGLE_CLIENT_ID=your-google-client-id GOOGLE_CLIENT_SECRET=your-google-client-secret ``` ### Next Steps: 1. **Update `auth.config.ts`**: Ensure that `auth.config.ts` is correctly configured to support the new provider. 2. **Frontend Integration**: Update the frontend components (`login-button.tsx` and `login-form.tsx`) to include the Google sign-in button and handle the OAuth flow. By following these steps, you will successfully integrate Google OAuth into your existing authentication logic.
Changes on package.json To implement Google sign-in functionality, we need to add the necessary dependencies for Google OAuth and NextAuth in the `package.json` file. Here are the steps to update the `package.json` file: 1. **Add `@next-auth/google` dependency**: This package provides the Google OAuth provider for NextAuth. 2. **Ensure `next-auth` is up-to-date**: You already have `next-auth` listed, but make sure it is the latest version compatible with the Google provider. Here is the updated `package.json` with the necessary dependencies: ```json { "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "lint:fix": "next lint --fix", "preview": "next build && next start", "seed": "node -r dotenv/config ./scripts/seed.mjs", "type-check": "tsc --noEmit", "format:write": "prettier --write \"{app,lib,components}/**/*.{ts,tsx,mdx}\" --cache", "format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache" }, "dependencies": { "@ai-sdk/openai": "^0.0.9", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-separator": "^1.0.3", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tooltip": "^1.0.7", "@vercel/analytics": "^1.1.2", "@vercel/kv": "^1.0.1", "@vercel/og": "^0.6.2", "ai": "^3.1.5", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "d3-scale": "^4.0.2", "date-fns": "^3.3.1", "focus-trap-react": "^10.2.3", "framer-motion": "^10.18.0", "geist": "^1.2.1", "nanoid": "^5.0.4", "next": "14.2.3", "next-auth": "5.0.0-beta.4", "next-themes": "^0.2.1", "openai": "^4.24.7", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.5.3", "react-markdown": "^8.0.7", "react-syntax-highlighter": "^15.5.0", "react-textarea-autosize": "^8.5.3", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", "sonner": "^1.4.3", "usehooks-ts": "^2.16.0", "zod": "^3.22.4", "@next-auth/google": "^1.0.0" // Add this line }, "devDependencies": { "@tailwindcss/typography": "^0.5.10", "@types/d3-scale": "^4.0.8", "@types/node": "^20.11.5", "@types/react": "^18.2.48", "@types/react-dom": "^18.2.18", "@types/react-syntax-highlighter": "^15.5.11", "@typescript-eslint/parser": "^6.19.0", "autoprefixer": "^10.4.17", "dotenv": "^16.4.5", "eslint": "^8.56.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-tailwindcss": "^3.14.0", "postcss": "^8.4.33", "prettier": "^3.2.4", "tailwind-merge": "^2.2.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", "typescript": "^5.3.3" }, "packageManager": "pnpm@8.6.3" } ``` ### Steps to Follow: 1. **Update `package.json`**: Add the `@next-auth/google` dependency as shown above. 2. **Install Dependencies**: Run `pnpm install` to install the new dependencies. This will ensure that your project has all the necessary packages to implement Google OAuth using NextAuth.
Changes on components/login-form.tsx To include the Google sign-in button in the login form and ensure it is styled appropriately and functions correctly, follow these steps: 1. **Import the necessary components and hooks**: - Import the `GoogleLoginButton` component (which you will create in `components/login-button.tsx`). - Import any necessary hooks or utilities for handling the Google OAuth flow. 2. **Add the Google sign-in button to the form**: - Place the `GoogleLoginButton` component within the form, ensuring it is styled consistently with the existing elements. 3. **Ensure the button initiates the Google OAuth flow**: - The `GoogleLoginButton` should have an `onClick` handler that triggers the Google OAuth process. Here is the updated code for `components/login-form.tsx`: ```typescript 'use client' import { useFormState, useFormStatus } from 'react-dom' import { authenticate } from '@/app/login/actions' import Link from 'next/link' import { useEffect } from 'react' import { toast } from 'sonner' import { IconSpinner } from './ui/icons' import { getMessageFromCode } from '@/lib/utils' import { useRouter } from 'next/navigation' import GoogleLoginButton from './login-button' // Import the GoogleLoginButton component export default function LoginForm() { const router = useRouter() const [result, dispatch] = useFormState(authenticate, undefined) useEffect(() => { if (result) { if (result.type === 'error') { toast.error(getMessageFromCode(result.resultCode)) } else { toast.success(getMessageFromCode(result.resultCode)) router.refresh() } } }, [result, router]) return (

Please log in to continue.

{/* Add the GoogleLoginButton component */}
No account yet?
Sign up
) } function LoginButton() { const { pending } = useFormStatus() return ( ) } ``` ### Notes: - Ensure that the `GoogleLoginButton` component is correctly implemented in `components/login-button.tsx` to handle the Google OAuth flow. - The styling of the `GoogleLoginButton` should match the existing buttons for a consistent user experience. - Make sure to handle any necessary state or side effects related to the Google OAuth process within the `GoogleLoginButton` component.