Skip to content

Commit 58ca465

Browse files
feat: add github and google OAuth
1 parent 988bdad commit 58ca465

File tree

4 files changed

+78
-18
lines changed

4 files changed

+78
-18
lines changed

src/app/auth/callback/route.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { NextResponse } from "next/server";
2+
// The client you created from the Server-Side Auth instructions
3+
import { createClient } from "@/lib/supabase/server";
4+
export async function GET(request: Request) {
5+
const { searchParams, origin } = new URL(request.url);
6+
const code = searchParams.get("code");
7+
// if "next" is in param, use it as the redirect URL
8+
let next = searchParams.get("next") ?? "/";
9+
if (!next.startsWith("/")) {
10+
// if "next" is not a relative URL, use the default
11+
next = "/";
12+
}
13+
14+
if (code) {
15+
const supabase = await createClient();
16+
const { error } = await supabase.auth.exchangeCodeForSession(code);
17+
if (!error) {
18+
const forwardedHost = request.headers.get("x-forwarded-host"); // original origin before load balancer
19+
const isLocalEnv = process.env.NODE_ENV === "development";
20+
if (isLocalEnv) {
21+
// we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host
22+
return NextResponse.redirect(`${origin}${next}`);
23+
} else if (forwardedHost) {
24+
return NextResponse.redirect(`https://${forwardedHost}${next}`);
25+
} else {
26+
return NextResponse.redirect(`${origin}${next}`);
27+
}
28+
}
29+
}
30+
31+
// return the user to an error page with instructions
32+
return NextResponse.redirect(`${origin}/auth/auth-code-error`);
33+
}

src/app/login/page.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from "react";
44
import { useRouter } from "next/navigation";
55
import { createClient } from "../../lib/supabase/client";
6+
import handleOAuthLogin from "@/lib/supabase/handle-oauth-login";
67

78
export default function AuthPage() {
89
const router = useRouter();
@@ -99,14 +100,20 @@ export default function AuthPage() {
99100

100101
{/* Google */}
101102
<div>
102-
<button className="w-full p-2 rounded-md bg-transparent border border-white/40 focus:border-indigo-400 focus:shadow-[0_0_12px_rgba(140,120,255,0.8)] outline-none transition">
103+
<button
104+
className="w-full p-2 rounded-md bg-transparent border border-white/40 focus:border-indigo-400 focus:shadow-[0_0_12px_rgba(140,120,255,0.8)] outline-none transition"
105+
onClick={() => handleOAuthLogin("google")}
106+
>
103107
Google
104108
</button>
105109
</div>
106110

107111
{/* Github */}
108112
<div>
109-
<button className="w-full p-2 rounded-md bg-transparent border border-white/40 focus:border-indigo-400 focus:shadow-[0_0_12px_rgba(140,120,255,0.8)] outline-none transition">
113+
<button
114+
className="w-full p-2 rounded-md bg-transparent border border-white/40 focus:border-indigo-400 focus:shadow-[0_0_12px_rgba(140,120,255,0.8)] outline-none transition"
115+
onClick={() => handleOAuthLogin("github")}
116+
>
110117
Github
111118
</button>
112119
</div>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"use server";
2+
3+
import { redirect } from "next/navigation";
4+
import { createClient } from "./server";
5+
import { headers } from "next/headers";
6+
export default async function handleOAuthLogin(provider: "github" | "google") {
7+
const supabase = await createClient();
8+
const headersList = await headers();
9+
const origin = headersList.get("origin") ?? `https://${headersList.get("host")}`;;
10+
const { error, data } = await supabase.auth.signInWithOAuth({
11+
provider: provider,
12+
options: { redirectTo: `${origin}/auth/callback?next=/dashboard` },
13+
});
14+
15+
if (error) throw error;
16+
else {
17+
return redirect(data.url);
18+
}
19+
}

src/lib/supabase/middleware.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,47 @@
1-
import { createServerClient } from '@supabase/ssr'
2-
import { NextResponse, type NextRequest } from 'next/server'
1+
import { createServerClient } from "@supabase/ssr";
2+
import { NextResponse, type NextRequest } from "next/server";
33

44
export async function updateSession(request: NextRequest) {
55
let supabaseResponse = NextResponse.next({
66
request,
7-
})
7+
});
88

99
const supabase = createServerClient(
1010
process.env.NEXT_PUBLIC_SUPABASE_URL!,
1111
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
1212
{
1313
cookies: {
1414
getAll() {
15-
return request.cookies.getAll()
15+
return request.cookies.getAll();
1616
},
1717
setAll(cookiesToSet) {
18-
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value))
18+
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value));
1919
supabaseResponse = NextResponse.next({
2020
request,
21-
})
21+
});
2222
cookiesToSet.forEach(({ name, value, options }) =>
2323
supabaseResponse.cookies.set(name, value, options)
24-
)
24+
);
2525
},
2626
},
2727
}
28-
)
28+
);
2929

3030
const {
3131
data: { user },
32-
} = await supabase.auth.getUser()
32+
} = await supabase.auth.getUser();
3333

3434
if (
3535
!user &&
36-
!request.nextUrl.pathname.startsWith('/login') &&
37-
!request.nextUrl.pathname.startsWith('/error')
36+
!request.nextUrl.pathname.startsWith("/login") &&
37+
!request.nextUrl.pathname.startsWith("/error") &&
38+
!request.nextUrl.pathname.startsWith("/auth")
3839
) {
3940
// no user, potentially respond by redirecting the user to the login page
40-
const url = request.nextUrl.clone()
41-
url.pathname = '/login'
42-
return NextResponse.redirect(url)
41+
const url = request.nextUrl.clone();
42+
url.pathname = "/login";
43+
return NextResponse.redirect(url);
4344
}
4445

45-
return supabaseResponse
46-
}
46+
return supabaseResponse;
47+
}

0 commit comments

Comments
 (0)