Open rrsartneoai opened 4 months ago
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.
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.
'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>
)
}
<LoginButton
text="Login with Google"
showGoogleIcon={true}
provider="google"
/>
auth.config.ts
We need to add the Google OAuth provider configuration to the auth.config.ts
file.
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
AUTH_SECRET=your_auth_secret
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
auth.ts
We need to integrate Google OAuth into the existing authentication logic by updating the auth.ts
file.
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
}
}
})
package.json
We need to add the necessary dependencies for Google OAuth and NextAuth.
{
"dependencies": {
"next-auth": "5.0.0-beta.4",
"@next-auth/google": "^1.0.0"
}
}
pnpm install
components/login-form.tsx
We need to include the Google sign-in button in the login form.
'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:
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