Skip to content

Commit efd9e47

Browse files
committed
Add google signin
1 parent 7935572 commit efd9e47

File tree

5 files changed

+151
-30
lines changed

5 files changed

+151
-30
lines changed

app/(auth)/signin/form.tsx

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import {
3131
} from "@/components/auth-form-footers";
3232
import { SocialFooter } from "@/components/social-footer";
3333
import Link from "next/link";
34+
import { Separator } from "@/components/ui/separator";
35+
import { GoogleOAuthButton } from "@/components/google-oauth-button";
3436

3537
const formSchema = z.object({
3638
email: z.string(),
@@ -81,6 +83,22 @@ const SignInForm = () => {
8183
});
8284
};
8385

86+
const handleGoogleSignIn = async () => {
87+
const { error } = await supabase.auth.signInWithOAuth({
88+
provider: "google",
89+
options: {
90+
redirectTo: `${window.location.origin}/api/auth/callback`,
91+
},
92+
});
93+
94+
if (error) {
95+
console.error(error);
96+
toast.error("Could not Sign In", {
97+
position: "top-right",
98+
});
99+
}
100+
};
101+
84102
return (
85103
<main className="flex flex-col gap-6 items-center w-full h-screen pt-8 px-4">
86104
<Link href="/">
@@ -91,9 +109,19 @@ const SignInForm = () => {
91109
<CardTitle className="text-2xl">Sign In</CardTitle>
92110
</CardHeader>
93111

94-
<Form {...form}>
95-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
96-
<CardContent className="grid gap-6">
112+
<CardContent className="grid gap-4">
113+
<GoogleOAuthButton onClick={handleGoogleSignIn}>
114+
Sign in with Google
115+
</GoogleOAuthButton>
116+
117+
<div className="flex items-center gap-4">
118+
<Separator className="flex-1" />
119+
<span className="text-neutral-500 text-sm">OR</span>
120+
<Separator className="flex-1" />
121+
</div>
122+
123+
<Form {...form}>
124+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
97125
<FormField
98126
control={form.control}
99127
name="email"
@@ -131,14 +159,14 @@ const SignInForm = () => {
131159

132160
{formStatus !== FormStatus.Loading && "Sign In"}
133161
</Button>
134-
</CardContent>
135-
136-
<CardFooter className="flex flex-col gap-2">
137-
<ForgotPasswordFooter />
138-
<SignUpFooter />
139-
</CardFooter>
140-
</form>
141-
</Form>
162+
</form>
163+
</Form>
164+
</CardContent>
165+
166+
<CardFooter className="flex flex-col gap-2">
167+
<ForgotPasswordFooter />
168+
<SignUpFooter />
169+
</CardFooter>
142170
</Card>
143171

144172
<SocialFooter />

app/(auth)/signup/form.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import toast from "react-hot-toast";
2828
import { Loader2Icon } from "lucide-react";
2929
import { SocialFooter } from "@/components/social-footer";
3030
import Link from "next/link";
31+
import { GoogleOAuthButton } from "@/components/google-oauth-button";
32+
import { Separator } from "@/components/ui/separator";
3133

3234
enum FormStatus {
3335
Idle,
@@ -85,19 +87,46 @@ const SignUpForm = () => {
8587
setFormStatus(FormStatus.Success);
8688
};
8789

90+
const handleGoogleSignUp = async () => {
91+
const { error } = await supabase.auth.signInWithOAuth({
92+
provider: "google",
93+
options: {
94+
redirectTo: `${window.location.origin}/api/auth/callback`,
95+
},
96+
});
97+
98+
if (error) {
99+
console.error(error);
100+
toast.error("Could not Sign Up", {
101+
position: "top-right",
102+
});
103+
}
104+
};
105+
88106
return (
89107
<main className="flex flex-col gap-6 items-center w-full h-screen pt-8 px-4">
90108
<Link href="/">
91109
<h1 className="text-4xl font-bold">Open Artifacts</h1>
92110
</Link>
111+
93112
<Card className="max-w-sm w-full">
94113
<CardHeader className="space-y-1">
95114
<CardTitle className="text-2xl">Create an account</CardTitle>
96115
</CardHeader>
97116

98-
<Form {...form}>
99-
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
100-
<CardContent className="grid gap-6">
117+
<CardContent className="grid gap-4">
118+
<GoogleOAuthButton onClick={handleGoogleSignUp}>
119+
Sign up with Google
120+
</GoogleOAuthButton>
121+
122+
<div className="flex items-center gap-4">
123+
<Separator className="flex-1" />
124+
<span className="text-neutral-500 text-sm">OR</span>
125+
<Separator className="flex-1" />
126+
</div>
127+
128+
<Form {...form}>
129+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
101130
<FormField
102131
control={form.control}
103132
name="full_name"
@@ -149,13 +178,13 @@ const SignUpForm = () => {
149178

150179
{formStatus !== FormStatus.Loading && "Sign Up"}
151180
</Button>
152-
</CardContent>
181+
</form>
182+
</Form>
183+
</CardContent>
153184

154-
<CardFooter>
155-
<SignInFooter />
156-
</CardFooter>
157-
</form>
158-
</Form>
185+
<CardFooter className="flex items-center justify-center">
186+
<SignInFooter />
187+
</CardFooter>
159188
</Card>
160189

161190
<SocialFooter />

app/api/auth/callback/route.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
1-
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
2-
import { cookies } from 'next/headers'
3-
import { NextResponse } from 'next/server'
1+
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
2+
import { cookies } from "next/headers";
3+
import { NextResponse } from "next/server";
44

5-
export const dynamic = 'force-dynamic'
5+
export const dynamic = "force-dynamic";
66

77
export async function GET(request: Request) {
88
// The `/auth/callback` route is required for the server-side auth flow implemented
99
// by the Auth Helpers package. It exchanges an auth code for the user's session.
1010
// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange
11-
const requestUrl = new URL(request.url)
12-
const code = requestUrl.searchParams.get('code')
11+
const { searchParams, origin } = new URL(request.url);
12+
const code = searchParams.get("code");
13+
// if "next" is in param, use it as the redirect URL
14+
const next = searchParams.get("next") ?? "/";
1315

1416
if (code) {
15-
const supabase = createRouteHandlerClient({ cookies })
16-
await supabase.auth.exchangeCodeForSession(code)
17+
const supabase = createRouteHandlerClient({ cookies });
18+
const { error } = await supabase.auth.exchangeCodeForSession(code);
19+
20+
if (!error) {
21+
return NextResponse.redirect(`${origin}${next}`);
22+
}
1723
}
1824

1925
// URL to redirect to after sign in process completes
20-
return NextResponse.redirect(requestUrl.origin)
26+
return NextResponse.redirect(origin);
2127
}

app/page.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,21 @@ import Image from "next/image";
22
import Link from "next/link";
33
import { Button } from "@/components/ui/button";
44
import { GithubIcon, RocketIcon, MenuIcon } from "lucide-react";
5+
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
6+
import { cookies } from "next/headers";
7+
import { redirect } from "next/navigation";
8+
9+
export default async function LandingPage() {
10+
const supabase = createServerComponentClient({ cookies });
11+
12+
const {
13+
data: { user },
14+
} = await supabase.auth.getUser();
15+
16+
if (user) {
17+
redirect("/new");
18+
}
519

6-
export default function LandingPage() {
720
return (
821
<div className="min-h-screen flex flex-col">
922
<header className="bg-white shadow-sm sticky top-0 z-10">

components/google-oauth-button.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Button } from "@/components/ui";
2+
import { ButtonHTMLAttributes } from "react";
3+
import { twMerge } from "tailwind-merge";
4+
5+
type Props = ButtonHTMLAttributes<HTMLButtonElement>;
6+
7+
export const GoogleOAuthButton = ({
8+
onClick,
9+
className = "",
10+
children,
11+
}: Props) => {
12+
return (
13+
<Button
14+
onClick={onClick}
15+
className={twMerge(
16+
"w-full bg-white text-gray-700 font-semibold py-2 px-4 border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 flex items-center justify-center",
17+
className
18+
)}
19+
>
20+
<svg
21+
className="w-5 h-5 mr-2"
22+
viewBox="0 0 24 24"
23+
xmlns="http://www.w3.org/2000/svg"
24+
>
25+
<path
26+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
27+
fill="#4285F4"
28+
/>
29+
<path
30+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
31+
fill="#34A853"
32+
/>
33+
<path
34+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
35+
fill="#FBBC05"
36+
/>
37+
<path
38+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
39+
fill="#EA4335"
40+
/>
41+
</svg>
42+
{children}
43+
</Button>
44+
);
45+
};

0 commit comments

Comments
 (0)