Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth #5

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
DEV_GOOGLE_CLIENT_ID= # Get it from Google Cloud APIs & Services pages on the Credentials tab
DEV_GOOGLE_CLIENT_SECRET= # Get it from Google Cloud APIs & Services pages on the Credentials tab
7 changes: 6 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"extends": "next/core-web-vitals"
"extends": "next/core-web-vitals",
"plugins": ["simple-import-sort"],
"rules": {
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn"
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
.env*.local

# vercel
Expand Down
34 changes: 15 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
# CHOOSELIFE

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started
## Local development

First, setup the `.env` file. See [How to setup Google oAuth](#how-to-setup-google-oauth).

First, run the development server:
Then, run the development server:

```bash
npm run dev
# or
yarn install
npx supabase start
yarn dev
# or
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

## Learn More

To learn more about Next.js, take a look at the following resources:
### How to setup Google oAuth

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can follow [this tutorial](https://docs.retool.com/data-sources/guides/authentication/google-oauth) to get your Google oAuth credentials.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
For local development:

## Deploy on Vercel
- Add `http://localhost:3000` to **Authorized JavaScript origins**
- Add `http://localhost:54321/auth/v1/callback` to **Authorized redirect URIs**

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
For prod:

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
- Add `https://${SUPABASE_PROJECT_ID}.supabase.co/auth/v1/callback` to **Authorized redirect URIs**
5 changes: 3 additions & 2 deletions app/[locale]/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

import type { ReactNode } from "react";
import { ThemeProvider } from "next-themes";
import { type AbstractIntlMessages, NextIntlClientProvider } from "next-intl";
import { NextIntlClientProvider, type AbstractIntlMessages } from "next-intl";

import { ReactQueryProvider } from "@/components/ReactQueryProvider";

interface Props {
locale: "en" | "pt";
messages: AbstractIntlMessages;
messages: AbstractIntlMessages | undefined;
children: ReactNode;
}

function Providers({ locale, messages, children }: Props) {
return (
<ReactQueryProvider>
Expand Down
27 changes: 27 additions & 0 deletions app/[locale]/auth/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Database } from "@/utils/database.types";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";

export const dynamic = "force-dynamic";

export async function GET(request: Request) {
// The `/auth/callback` route is required for the server-side auth flow implemented
// by the Auth Helpers package. It exchanges an auth code for the user's session.
// https://supabase.com/docs/guides/auth/auth-helpers/nextjs#managing-sign-in-with-code-exchange
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get("code");

if (code) {
const supabase = createRouteHandlerClient<Database>({ cookies });
const { data, error } = await supabase.auth.exchangeCodeForSession(code);

// Redirect to profile page so the user can update it
if (!data.user?.user_metadata.age) {
return NextResponse.redirect(requestUrl.origin + "/profile");
}
}

// URL to redirect to after sign in process completes
return NextResponse.redirect(requestUrl.origin);
}
37 changes: 20 additions & 17 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,54 @@
import "./globals.css";
import { Inter } from "next/font/google";

import { Analytics } from "@vercel/analytics/react";
import { Inter } from "next/font/google";
import { notFound } from "next/navigation";
import { useLocale, useMessages } from "next-intl";

import Providers from "./Providers";
import Footer from "@/components/Footer";
import NavBar from "@/components/layout/navbar";

const inter = Inter({ subsets: ["latin"] });
import Providers from "./Providers";

const inter = Inter({
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
});

export const metadata = {
title: "Festival Chooselife",
description: "Site oficial do festival Chooselife",
};

export function generateStaticParams() {
return [{ locale: "en" }, { locale: "pt" }];
}

export default async function RootLayout({
export default function RootLayout({
children,
params: { locale },
params,
}: {
children: React.ReactNode;
params: { locale: "en" | "pt" };
}) {
let messages;
try {
messages = (await import(`../../messages/${locale}.json`)).default;
} catch (error) {
const locale = useLocale();
// Show a 404 error if the user requests an unknown locale
if (params.locale !== locale) {
notFound();
}

const messages = useMessages();

return (
// suppressHydrationWarning because of `next-themes`
// refer to https://github.com/pacocoursey/next-themes#with-app
<html lang={locale} suppressHydrationWarning>
<body
className="min-h-screen bg-gradient-to-b from-white via-transparent to-gray-300 dark:from-gray-900 dark:to-black md:px-0"
style={inter.style}
className={`min-h-screen bg-gray-50 dark:bg-gray-900 md:px-0 ${inter.variable}`}
>
<Providers locale={locale} messages={messages}>
<>
<div className="flex h-full min-h-screen flex-col">
<NavBar />
{children}
<Footer />
</>
</div>
</Providers>
<Analytics />
</body>
Expand Down
31 changes: 31 additions & 0 deletions app/[locale]/login/_components/google-oauth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";

import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";

import type { Database } from "@/utils/database.types";

import { GoogleIcon } from "@/assets";
import Button from "@/components/ui/Button";

export default function GoogleAuthLogin() {
const supabase = createClientComponentClient<Database>();

async function handleSignInWithGoogle() {
await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: `${window.location.origin}/auth/callback`,
},
});
}

return (
<Button
label="Sign in with Google"
icon={<GoogleIcon className="-ml-1 mr-2 h-5 w-5" />}
variant="outlined"
color="secondary"
onClick={handleSignInWithGoogle}
/>
);
}
103 changes: 103 additions & 0 deletions app/[locale]/login/_components/login-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"use client";

import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
import { useRouter } from "next/navigation";

import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/Form";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Input } from "@/components/ui/Input";
import Button from "@/components/ui/Button";
import Checkbox from "@/components/ui/Checkbox";

const loginFormSchema = z.object({
email: z.string().email(),
password: z.string(),
});

type LoginFormSchema = z.infer<typeof loginFormSchema>;

export default function LoginForm() {
const router = useRouter();
const supabase = createClientComponentClient();

const form = useForm<LoginFormSchema>({
resolver: zodResolver(loginFormSchema),
defaultValues: {
email: "",
password: "",
},
});

async function onSubmit(data: LoginFormSchema) {
const { error } = await supabase.auth.signInWithPassword({
email: data.email,
password: data.password,
});
if (!error) router.refresh();
// TODO: Check if there is a profile with this email and tell the user to Log In with Google
}

return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4 md:space-y-6"
>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input placeholder={"Your email"} {...field} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
type="password"
placeholder={"Your password"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<Checkbox id="remember" />
<label
htmlFor="remember"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Remember me
</label>
</div>
<a
href="#"
className="text-sm font-medium text-blue-600 hover:underline dark:text-blue-500"
>
Forgot passoword?
</a>
</div>
<Button type="submit" label="Log In" />
</form>
</Form>
);
}
57 changes: 57 additions & 0 deletions app/[locale]/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Link from "next/link";
import { Metadata } from "next";
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";

import type { Database } from "@/utils/database.types";
import LoginForm from "./_components/login-form";
import GoogleAuthLogin from "./_components/google-oauth";

export const metadata: Metadata = {
title: "Login",
description: "Login to your account",
};

export default async function Login() {
const cookieStore = cookies();
const supabase = createServerComponentClient<Database>({
cookies: () => cookieStore,
});

const {
data: { session },
} = await supabase.auth.getSession();

if (session) {
redirect("/");
}

return (
<section className="mx-auto flex h-auto w-full max-w-xl flex-col items-center justify-center px-6 py-8 lg:py-0">
<div className="w-full rounded-lg bg-white shadow-xl dark:border dark:border-gray-700 dark:bg-gray-800 sm:max-w-md md:mt-0 xl:p-0">
<div className="space-y-4 p-6 sm:p-8 md:space-y-6">
<h1 className="text-xl font-bold leading-tight tracking-tight text-gray-900 dark:text-white md:text-2xl">
Sign in to your account
</h1>
<GoogleAuthLogin />
<div className="my-4 flex items-center before:mt-0.5 before:flex-1 before:border-t before:border-gray-600 after:mt-0.5 after:flex-1 after:border-t after:border-gray-600">
<p className="mx-4 mb-0 text-center font-semibold dark:text-gray-500">
or
</p>
</div>
<LoginForm />
<p className="text-sm font-light text-gray-500 dark:text-gray-400">
Don’t have an account yet?{" "}
<Link
href={"/sign-up"}
className="font-medium text-blue-600 hover:underline dark:text-blue-500"
>
Sign up
</Link>
</p>
</div>
</div>
</section>
);
}
Loading
Loading