From b5a40d07cfe01e1eaccd03adbf62cd766b3c9d2a Mon Sep 17 00:00:00 2001
From: Pablo Lara
Date: Tue, 27 Aug 2024 18:37:45 +0200
Subject: [PATCH] feat: Nextauth is working
---
actions/auth.ts | 42 +-
app/(auth)/layout.tsx | 10 +-
app/(prowler)/profile/page.tsx | 24 +
app/api/auth/[...nextauth]/route.ts | 3 +
app/providers.tsx | 9 +-
auth.config.ts | 83 +-
components/auth/AuthButton.tsx | 3 +-
components/auth/AuthForm.tsx | 53 +-
components/ui/custom/CustomInput.tsx | 38 +-
components/ui/form/Form.tsx | 2 +-
components/ui/sidebar/SidebarWrap.tsx | 17 +-
lib/seed.ts | 37 +-
middleware.ts | 10 +
package-lock.json | 2019 ++++++++++++++++++-------
package.json | 4 +-
types/auth.ts | 4 +-
16 files changed, 1728 insertions(+), 630 deletions(-)
create mode 100644 app/(prowler)/profile/page.tsx
create mode 100644 app/api/auth/[...nextauth]/route.ts
create mode 100644 middleware.ts
diff --git a/actions/auth.ts b/actions/auth.ts
index 34e7fa36c4..3c93021a12 100644
--- a/actions/auth.ts
+++ b/actions/auth.ts
@@ -3,25 +3,47 @@
import { AuthError } from "next-auth";
import { signIn, signOut } from "@/auth.config";
+// import { authFormSchema } from "@/types";
-export async function authenticate(
- prevState: string | undefined,
- formData: FormData,
-) {
+// const formSchema = authFormSchema("sign-in");
+
+const defaultValues = {
+ email: "",
+ password: "",
+};
+
+// Fix TS types.
+export async function authenticate(prevState: any, formData: any) {
try {
- // await new Promise((resolve) => setTimeout(resolve, 2000));
- console.log(formData);
- await signIn("credentials", formData);
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ await signIn("credentials", {
+ ...formData,
+ redirect: false,
+ });
+ return {
+ message: "Success",
+ };
} catch (error) {
if (error instanceof AuthError) {
switch (error.type) {
case "CredentialsSignin":
- return "Invalid credentials.";
+ return {
+ message: "Credentials error",
+ errors: {
+ ...defaultValues,
+ credentials: "Incorrect email or password",
+ },
+ };
default:
- return "Something went wrong.";
+ return {
+ message: "Unknown error",
+ errors: {
+ ...defaultValues,
+ unknown: "Unknown error",
+ },
+ };
}
}
- throw error;
}
}
diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx
index f1f003744e..a9fdc8c394 100644
--- a/app/(auth)/layout.tsx
+++ b/app/(auth)/layout.tsx
@@ -1,7 +1,9 @@
import "@/styles/globals.css";
import { Metadata, Viewport } from "next";
+import { redirect } from "next/navigation";
+import { auth } from "@/auth.config";
import { Toaster } from "@/components/ui";
import { fontSans } from "@/config/fonts";
import { siteConfig } from "@/config/site";
@@ -27,11 +29,17 @@ export const viewport: Viewport = {
],
};
-export default function RootLayout({
+export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
+ const session = await auth();
+
+ if (session?.user) {
+ redirect("/");
+ }
+
return (
diff --git a/app/(prowler)/profile/page.tsx b/app/(prowler)/profile/page.tsx
new file mode 100644
index 0000000000..76c72a8800
--- /dev/null
+++ b/app/(prowler)/profile/page.tsx
@@ -0,0 +1,24 @@
+import { Spacer } from "@nextui-org/react";
+import { redirect } from "next/navigation";
+import React from "react";
+
+import { auth } from "@/auth.config";
+import { Header } from "@/components/ui";
+
+export default async function Profile() {
+ const session = await auth();
+
+ if (!session?.user) {
+ // redirect("/sign-in?returnTo=/profile");
+ redirect("/sign-in");
+ }
+
+ return (
+ <>
+
+
+
+
{JSON.stringify(session.user, null, 2)}
+ >
+ );
+}
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
new file mode 100644
index 0000000000..316cf49c14
--- /dev/null
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -0,0 +1,3 @@
+import { handlers } from "@/auth.config";
+
+export const { GET, POST } = handlers;
diff --git a/app/providers.tsx b/app/providers.tsx
index 294f0647a6..942bce43dc 100644
--- a/app/providers.tsx
+++ b/app/providers.tsx
@@ -2,6 +2,7 @@
import { NextUIProvider } from "@nextui-org/system";
import { useRouter } from "next/navigation";
+import { SessionProvider } from "next-auth/react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ThemeProviderProps } from "next-themes/dist/types";
import * as React from "react";
@@ -15,8 +16,10 @@ export function Providers({ children, themeProps }: ProvidersProps) {
const router = useRouter();
return (
-
- {children}
-
+
+
+ {children}
+
+
);
}
diff --git a/auth.config.ts b/auth.config.ts
index 1171b2ae42..587b64197f 100644
--- a/auth.config.ts
+++ b/auth.config.ts
@@ -3,39 +3,86 @@ import NextAuth, { type NextAuthConfig } from "next-auth";
import Credentials from "next-auth/providers/credentials";
import { z } from "zod";
+import { userMockData } from "./lib";
+
+async function getUser(email: string, password: string): Promise {
+ // Check if the user exists in the userMockData array.
+ const user = userMockData.find((user) => user.email === email);
+ if (!user) return null;
+
+ if (!bcryptjs.compareSync(password, user.password)) return null;
+
+ return {
+ id: user.id,
+ name: user.name,
+ companyName: user.companyName,
+ email: user.email,
+ role: user.role,
+ image: user.image,
+ };
+}
+
export const authConfig = {
+ session: {
+ strategy: "jwt",
+ },
pages: {
signIn: "/sign-in",
newUser: "/sign-up",
},
+ callbacks: {
+ authorized({ auth, request: { nextUrl } }) {
+ const isLoggedIn = !!auth?.user;
+ // console.log({ auth }, "llegas o no");
+ const isOnDashboard = nextUrl.pathname.startsWith("/");
+ if (isOnDashboard) {
+ if (isLoggedIn) return true;
+ return false; // Redirect unauthenticated users to login page
+ } else if (isLoggedIn) {
+ return Response.redirect(new URL("/", nextUrl));
+ }
+ return true;
+ },
+
+ jwt({ token, user }) {
+ if (user) {
+ token.data = user;
+ }
+ return token;
+ },
+
+ session({ session, token }) {
+ session.user = token.data as any;
+ return session;
+ },
+ },
providers: [
Credentials({
+ name: "credentials",
+ credentials: {
+ email: { label: "email", type: "text" },
+ password: { label: "password", type: "password" },
+ },
async authorize(credentials) {
const parsedCredentials = z
- .object({ email: z.string().email(), password: z.string().min(6) })
+ .object({
+ email: z.string().email(),
+ password: z.string().min(6),
+ })
.safeParse(credentials);
- if (!parsedCredentials.success) return null;
-
+ if (!parsedCredentials.success) {
+ return null;
+ }
const { email, password } = parsedCredentials.data;
- console.log({ email, password }, "from AuthConfig.ts");
- // Check the user using the email
-
- // const user = await getUser(email);
- // if (!user) return null;
-
- // Compare passwords
-
- // if (!bcryptjs.compareSync(password, user.password)) return null;
-
- // Return the user object without the password field
-
- // const { password: _, ...rest } = user;
- // return rest;
+ const user = await getUser(email, password);
+ if (!user) return null;
+ // console.log({ user });
+ return user;
},
}),
],
} satisfies NextAuthConfig;
-export const { signIn, signOut, auth } = NextAuth(authConfig);
+export const { signIn, signOut, auth, handlers } = NextAuth(authConfig);
diff --git a/components/auth/AuthButton.tsx b/components/auth/AuthButton.tsx
index 821891f1b2..0eda81782a 100644
--- a/components/auth/AuthButton.tsx
+++ b/components/auth/AuthButton.tsx
@@ -6,10 +6,11 @@ import { useFormStatus } from "react-dom";
export const AuthButton = ({ type }: { type: string }) => {
const { pending } = useFormStatus();
+
return (