this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(auth): google

+35 -113
+29 -96
src/app/auth/page.tsx
··· 9 9 FieldGroup, 10 10 FieldLabel, 11 11 } from "@/components/ui/field"; 12 - import { Input } from "@/components/ui/input"; 13 - import { useRef, useState } from "react"; 14 12 import { authClient } from "@/lib/auth-client"; 15 - import { 16 - Dialog, 17 - DialogContent, 18 - DialogDescription, 19 - DialogHeader, 20 - DialogTitle, 21 - } from "@/components/ui/dialog"; 22 - import { Spinner } from "@/components/ui/spinner"; 23 - 24 - type ModalState = { 25 - title: string; 26 - message: string; 27 - } | null; 28 13 29 14 export default function Auth() { 30 - const email = useRef<HTMLInputElement>(null); 31 - const [isLoading, setIsLoading] = useState(false); 32 - const [modalContent, setModalContent] = useState<ModalState>(null); 33 - 34 - const handleLogin = async (e: React.FormEvent) => { 35 - e.preventDefault(); 36 - if (!email.current) return; 37 - setIsLoading(true); 38 - 39 - const { data, error } = await authClient.signIn.magicLink({ 40 - email: email.current?.value || "", 41 - callbackURL: "/", 42 - }); 43 - 44 - email.current.value = ""; 45 - setIsLoading(false); 46 - 47 - if (error) 48 - setModalContent({ 49 - title: "Connection Failed", 50 - message: error.message || "An error occurred while sending the link.", 51 - }); 52 - else 53 - setModalContent({ 54 - title: "Check your email", 55 - message: "We've sent you a magic link to sign in.", 56 - }); 57 - }; 58 - 59 15 return ( 60 - <div className="flex flex-col h-screen justify-center gap-6"> 61 - <form onSubmit={handleLogin}> 62 - <FieldGroup> 63 - <div className="flex flex-col items-center gap-2"> 64 - <HugeiconsIcon icon={TicketStarIcon} className="size-6" /> 65 - <h1 className="text-xl font-bold">Welcome to Vouch</h1> 66 - <FieldDescription>Sign in to continue</FieldDescription> 67 - </div> 68 - <Field> 69 - <FieldLabel htmlFor="email">Email</FieldLabel> 70 - <Input 71 - id="email" 72 - type="email" 73 - ref={email} 74 - placeholder="alexandre@hallaine.com" 75 - required 76 - /> 77 - </Field> 78 - <Field> 79 - <Button type="submit" disabled={isLoading}> 80 - {isLoading && <Spinner />} 81 - Login 82 - </Button> 83 - </Field> 84 - {/* <FieldSeparator>Or</FieldSeparator> 85 - <Field className="grid gap-4 sm:grid-cols-2"> 86 - <Button variant="outline" type="button"> 87 - <HugeiconsIcon icon={AppleIcon} className="size-6" /> 88 - Continue with Apple 89 - </Button> 90 - <Button variant="outline" type="button"> 91 - <HugeiconsIcon icon={GoogleIcon} className="size-6" /> 92 - Continue with Google 93 - </Button> 94 - </Field> */} 95 - </FieldGroup> 96 - </form> 97 - {/* <FieldDescription className="px-6 text-center"> 98 - By clicking continue, you agree to our <a href="#">Terms of Service</a> and <a href="#">Privacy Policy</a>. 99 - </FieldDescription> */} 100 - 101 - <Dialog 102 - open={!!modalContent} 103 - onOpenChange={(open) => !open && setModalContent(null)} 104 - > 105 - <DialogContent> 106 - <DialogHeader> 107 - <DialogTitle>{modalContent?.title}</DialogTitle> 108 - <DialogDescription>{modalContent?.message}</DialogDescription> 109 - </DialogHeader> 110 - </DialogContent> 111 - </Dialog> 16 + <div className="flex flex-col h-screen justify-center gap-6 w-md mx-auto"> 17 + <FieldGroup> 18 + <div className="flex flex-col items-center gap-2"> 19 + <HugeiconsIcon icon={TicketStarIcon} /> 20 + <FieldLabel className="text-xl font-bold"> 21 + Welcome to Vouch 22 + </FieldLabel> 23 + <FieldDescription className="text-center"> 24 + Sign in using a third-party provider. 25 + </FieldDescription> 26 + </div> 27 + <Field> 28 + <Button 29 + variant="outline" 30 + type="button" 31 + onClick={() => { 32 + authClient.signIn.social({ 33 + provider: "google", 34 + }); 35 + }} 36 + > 37 + Continue with Google 38 + </Button> 39 + </Field> 40 + </FieldGroup> 41 + <FieldDescription className="px-6 text-center"> 42 + By clicking continue, you agree to our <a href="#">Terms of Service</a>{" "} 43 + and <a href="#">Privacy Policy</a>. 44 + </FieldDescription> 112 45 </div> 113 46 ); 114 47 }
+6 -17
src/lib/auth.ts
··· 1 1 import { betterAuth } from "better-auth"; 2 2 import { drizzleAdapter } from "better-auth/adapters/drizzle"; 3 - import { magicLink } from "better-auth/plugins"; 4 3 import { headers } from "next/headers"; 5 4 import * as schema from "@/db/schema"; 6 5 import { db } from "@/lib/db"; 7 6 import { sendEmail } from "@/lib/email"; 8 7 9 - //TODO: replace login with Google 10 8 export const auth = betterAuth({ 11 9 database: drizzleAdapter(db, { 12 10 provider: "pg", ··· 26 24 }); 27 25 }, 28 26 }, 29 - plugins: [ 30 - magicLink({ 31 - sendMagicLink: async ({ email, token, url }, request) => { 32 - await sendEmail({ 33 - to: email, 34 - subject: "Sign in to Vouch", 35 - html: ` 36 - <h1>Welcome to Vouch</h1> 37 - <p>Click the link below to sign in:</p> 38 - <a href="${url}">Sign in now</a> 39 - `, 40 - }); 41 - }, 42 - }), 43 - ], 27 + socialProviders: { 28 + google: { 29 + clientId: process.env.GOOGLE_CLIENT_ID as string, 30 + clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, 31 + }, 32 + }, 44 33 }); 45 34 46 35 export async function getUserId() {