Created
May 5, 2024 20:20
-
-
Save mastoj/037d1f4831c0ee31d777edac09f8d8d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use server"; | |
import { createClient } from "@/lib/supabase/server"; | |
import { revalidatePath } from "next/cache"; | |
import { headers } from "next/headers"; | |
import { redirect } from "next/navigation"; | |
export async function login(formData: FormData) { | |
const supabase = createClient(); | |
// type-casting here for convenience | |
// in practice, you should validate your inputs | |
const data = { | |
email: formData.get("email") as string, | |
password: formData.get("password") as string, | |
}; | |
const { error } = await supabase.auth.signInWithPassword(data); | |
if (error) { | |
redirect("/error"); | |
} | |
revalidatePath("/", "layout"); | |
redirect("/"); | |
} | |
export async function signInWithGoogle() { | |
console.log("==> Signing in with Google"); | |
const supabase = createClient(); | |
const origin = headers().get("origin"); | |
const { data, error } = await supabase.auth.signInWithOAuth({ | |
provider: "google", | |
options: { | |
queryParams: { | |
access_type: "offline", | |
prompt: "consent", | |
}, | |
redirectTo: new URL("/auth/callback", origin!).toString(), | |
}, | |
}); | |
console.log("==> Data: ", data, error, new URL("/auth/callback", origin!)); | |
if (error) { | |
redirect("/error"); | |
} | |
redirect(data.url); | |
} | |
const getURL = () => { | |
let url = | |
process?.env?.NEXT_PUBLIC_SITE_URL ?? // Set this to your site URL in production env. | |
process?.env?.NEXT_PUBLIC_VERCEL_URL ?? // Automatically set by Vercel. | |
"http://localhost:3000/"; | |
// Make sure to include `https://` when not localhost. | |
url = url.includes("http") ? url : `https://${url}`; | |
// Make sure to include a trailing `/`. | |
url = url.charAt(url.length - 1) === "/" ? url : `${url}/`; | |
return url; | |
}; | |
export async function signup(formData: FormData) { | |
const supabase = createClient(); | |
// type-casting here for convenience | |
// in practice, you should validate your inputs | |
const data = { | |
email: formData.get("email") as string, | |
password: formData.get("password") as string, | |
}; | |
const firstName = formData.get("firstName") as string; | |
const lastName = formData.get("lastName") as string; | |
const emailRedirectTo = getURL(); | |
console.log( | |
"==> Signing up with data: ", | |
firstName, | |
lastName, | |
emailRedirectTo | |
); | |
const { error, data: signUpResponse } = await supabase.auth.signUp({ | |
...data, | |
options: { | |
data: { | |
first_name: firstName, | |
last_name: lastName, | |
}, | |
emailRedirectTo: emailRedirectTo, | |
}, | |
}); | |
console.log("==> Sign up response: ", error, signUpResponse); | |
if (error) { | |
console.error("=> Error signing up", error.message); | |
redirect("/error"); | |
} | |
revalidatePath("/", "layout"); | |
redirect("/confirmation"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createClient } from "@/lib/supabase/server"; | |
import { type EmailOtpType } from "@supabase/supabase-js"; | |
import { NextResponse, type NextRequest } from "next/server"; | |
export async function GET(request: NextRequest) { | |
const { searchParams } = new URL(request.url); | |
const token_hash = searchParams.get("token_hash"); | |
const type = searchParams.get("type") as EmailOtpType | null; | |
const next = searchParams.get("next") ?? "/"; | |
const redirectTo = request.nextUrl.clone(); | |
redirectTo.pathname = next; | |
redirectTo.searchParams.delete("token_hash"); | |
redirectTo.searchParams.delete("type"); | |
if (token_hash && type) { | |
const supabase = createClient(); | |
const { error } = await supabase.auth.verifyOtp({ | |
type, | |
token_hash, | |
}); | |
if (!error) { | |
redirectTo.searchParams.delete("next"); | |
return NextResponse.redirect(redirectTo); | |
} | |
} | |
// return the user to an error page with some instructions | |
redirectTo.pathname = "/error"; | |
return NextResponse.redirect(redirectTo); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { login, signInWithGoogle } from "@/domains/auth/actions"; | |
import Link from "next/link"; | |
import { Button } from "./ui/button"; | |
import { Input } from "./ui/input"; | |
import { Label } from "./ui/label"; | |
type Props = {}; | |
const LoginForm = async () => { | |
return ( | |
<form action={login}> | |
<div className="grid gap-2 text-center"> | |
<h1 className="text-3xl font-bold">Login</h1> | |
<p className="text-balance text-muted-foreground"> | |
Enter your email below to login to your account | |
</p> | |
</div> | |
<div className="grid gap-4"> | |
<div className="grid gap-2"> | |
<Label htmlFor="email">Email</Label> | |
<Input | |
id="email" | |
name="email" | |
placeholder="[email protected]" | |
required | |
type="email" | |
/> | |
</div> | |
<div className="grid gap-2"> | |
<div className="flex items-center"> | |
<Label htmlFor="password">Password</Label> | |
<Link className="ml-auto inline-block text-sm underline" href="#"> | |
Forgot your password? | |
</Link> | |
</div> | |
<Input id="password" name="password" required type="password" /> | |
</div> | |
<Button className="w-full" type="submit"> | |
Login | |
</Button> | |
</div> | |
</form> | |
); | |
}; | |
const Login = async (props: Props) => { | |
return ( | |
<div className="flex items-center justify-center py-12 h-screen overflow-y-auto"> | |
<div className="mx-auto grid w-[350px] gap-6"> | |
<LoginForm /> | |
<form> | |
<Button | |
className="w-full" | |
variant="outline" | |
formAction={signInWithGoogle} | |
> | |
Log in with Google | |
</Button> | |
</form> | |
<div className="mt-4 text-center text-sm"> | |
Don't have an account? | |
<Link className="underline" href="/signup"> | |
Sign up | |
</Link> | |
</div> | |
</div> | |
</div> | |
); | |
}; | |
export default Login; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Login from "@/components/login"; | |
type Props = {}; | |
const Page = (props: Props) => { | |
return <Login />; | |
}; | |
export default Page; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NextResponse, type NextRequest } from "next/server"; | |
import { updateSession } from "./lib/supabase/middleware"; | |
export async function middleware(request: NextRequest) { | |
const response = | |
request.nextUrl.pathname === "/" | |
? NextResponse.rewrite(new URL("/dashboard", request.url)) | |
: NextResponse.next({ | |
request: { | |
headers: request.headers, | |
}, | |
}); | |
const user = await updateSession(request, response); | |
if (request.nextUrl.pathname === "/" && !user) { | |
return NextResponse.rewrite(new URL("/home", request.url)); | |
} | |
return response; | |
} | |
export const config = { | |
matcher: [ | |
/* | |
* Match all request paths except for the ones starting with: | |
* - api (API routes) | |
* - _next/static (static files) | |
* - _next/image (image optimization files) | |
* - favicon.ico (favicon file) | |
*/ | |
"/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", | |
], | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createServerClient, type CookieOptions } from "@supabase/ssr"; | |
import { cookies } from "next/headers"; | |
export function createClient() { | |
const cookieStore = cookies(); | |
return createServerClient( | |
process.env.NEXT_PUBLIC_SUPABASE_URL!, | |
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, | |
{ | |
cookies: { | |
get(name: string) { | |
return cookieStore.get(name)?.value; | |
}, | |
set(name: string, value: string, options: CookieOptions) { | |
try { | |
cookieStore.set({ name, value, ...options }); | |
} catch (error) { | |
// The `set` method was called from a Server Component. | |
// This can be ignored if you have middleware refreshing | |
// user sessions. | |
} | |
}, | |
remove(name: string, options: CookieOptions) { | |
try { | |
cookieStore.set({ name, value: "", ...options }); | |
} catch (error) { | |
// The `delete` method was called from a Server Component. | |
// This can be ignored if you have middleware refreshing | |
// user sessions. | |
} | |
}, | |
}, | |
} | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createServerClient, type CookieOptions } from "@supabase/ssr"; | |
import { NextResponse, type NextRequest } from "next/server"; | |
export const updateSession = async ( | |
request: NextRequest, | |
response: NextResponse | |
) => { | |
const supabase = createServerClient( | |
process.env.NEXT_PUBLIC_SUPABASE_URL!, | |
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, | |
{ | |
cookies: { | |
get(name: string) { | |
return request.cookies.get(name)?.value; | |
}, | |
set(name: string, value: string, options: CookieOptions) { | |
// If the cookie is updated, update the cookies for the request and response | |
request.cookies.set({ | |
name, | |
value, | |
...options, | |
}); | |
response = NextResponse.next({ | |
request: { | |
headers: request.headers, | |
}, | |
}); | |
response.cookies.set({ | |
name, | |
value, | |
...options, | |
}); | |
}, | |
remove(name: string, options: CookieOptions) { | |
// If the cookie is removed, update the cookies for the request and response | |
request.cookies.set({ | |
name, | |
value: "", | |
...options, | |
}); | |
response = NextResponse.next({ | |
request: { | |
headers: request.headers, | |
}, | |
}); | |
response.cookies.set({ | |
name, | |
value: "", | |
...options, | |
}); | |
}, | |
}, | |
} | |
); | |
// This will refresh session if expired - required for Server Components | |
// https://supabase.com/docs/guides/auth/server-side/nextjs | |
const userResponse = await supabase.auth.getUser(); | |
return userResponse.data.user; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment