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

fix: credential auth #92

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@trpc/next": "^10.40.0",
"@trpc/react-query": "^10.40.0",
"@trpc/server": "^10.40.0",
"argon2": "^0.31.2",
"classnames": "^2.3.2",
"date-fns": "^2.30.0",
"emoji-picker-react": "^4.5.7",
Expand Down
69 changes: 69 additions & 0 deletions apps/nextjs/src/app/_components/credential-auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use client";

import { useRouter } from "next/navigation";
import { zodResolver } from "@hookform/resolvers/zod";
import { signIn } from "next-auth/react";
import type { SubmitHandler } from "react-hook-form";
import { FormProvider, useForm } from "react-hook-form";
import { toast } from "react-toastify";
import * as z from "zod";

import Button from "./button";
import Field from "./form/Field";

/* Local constants & types
============================================================================= */
export type FormData = z.infer<typeof schemaValidation>;

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

const CredentialAuth = () => {
const router = useRouter();
const methods = useForm<FormData>({
resolver: zodResolver(schemaValidation),
defaultValues: {
email: "",
password: "",
},
});

const onSubmit: SubmitHandler<FormData> = async (data: FormData) => {
try {
const response = await signIn("credentials", {
...data,
redirect: false,
});

console.log({ response });
if (!response?.error) {
router.push("/workspaces");
router.refresh();
}
} catch (error) {
console.error(error);
toast.error("Invalid email or password");
}
};

return (
<form
method="post"
className="flex w-full flex-col gap-4"
onSubmit={methods.handleSubmit(onSubmit)}
>
<FormProvider {...methods}>
<Field label="Email:" name="email" type="email" />
<Field label="Password:" name="password" type="password" />

<Button type="submit" variant="secondary">
Sign In
</Button>
</FormProvider>
</form>
);
};

export default CredentialAuth;
3 changes: 3 additions & 0 deletions apps/nextjs/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { redirect } from "next/navigation";
import { auth } from "@acme/auth";

import { AuthShowcase } from "./_components/auth-showcase";
import CredentialAuth from "./_components/credential-auth";
import Heading from "./_components/heading";
import { Meteors } from "./_components/meteors";
import routes from "./_lib/routes";
Expand All @@ -27,6 +28,8 @@ export default async function HomePage() {
with 2day.report!
</p>

<CredentialAuth />

<p
className="my-6 flex items-center overflow-hidden text-center text-xs uppercase before:relative
before:right-2 before:inline-block before:h-[1px] before:w-1/2
Expand Down
5 changes: 5 additions & 0 deletions packages/api/src/router/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export const userRouter = createTRPCRouter({
.query(({ ctx, input }) => {
return ctx.prisma.user.findFirst({ where: { id: input.id } });
}),
byEmail: protectedProcedure
.input(z.object({ email: z.string().min(1) }))
.query(({ ctx, input }) => {
return ctx.prisma.user.findFirst({ where: { email: input.email } });
}),
create: publicProcedure
.input(
z.object({
Expand Down
150 changes: 143 additions & 7 deletions packages/auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Credentials from "@auth/core/providers/credentials";
import Github from "@auth/core/providers/github";
import Google from "@auth/core/providers/google";
import type { DefaultSession } from "@auth/core/types";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { verify } from "argon2";
import NextAuth from "next-auth";

import { prisma } from "@acme/db";
Expand All @@ -22,6 +24,25 @@ declare module "next-auth" {
}
}

// Function to exclude user password returned from prisma
// const exclude = (user, keys) => {
// for (let key of keys) {
// delete user[key];
// }
// return user;
// };

const validatePassword = async (
plainPassword: string,
hashedPassword?: string | null,
) => {
if (!hashedPassword) {
return false;
}

return await verify(hashedPassword, plainPassword);
};

export const {
handlers: { GET, POST },
auth,
Expand All @@ -42,15 +63,130 @@ export const {
clientSecret: env.GITHUB_CLIENT_SECRET,
allowDangerousEmailAccountLinking: true,
}),
],
callbacks: {
session: ({ session, user }) => ({
...session,
user: {
...session.user,
id: user.id,
Credentials({
name: "Credentials",
credentials: {
email: {
label: "Email: ",
type: "text",
},
password: {
label: "Password: ",
type: "password",
},
},

async authorize(credentials) {
const { email, password } = credentials as {
email: string;
password: string;
};

const user = await prisma.user.findFirst({
where: {
email: {
equals: email,
mode: "insensitive",
},
},
});

const isValidPassword = await validatePassword(
password,
user?.password,
);

if (!user || !isValidPassword) {
throw new Error("Invalid email or password");
}

// TODO: exclude password field
return user;
},

// This is where you need to retrieve user data
// to verify with credentials
// await new Promise((resolve) => setTimeout(resolve, 1000));

// if (
// credentials.email === user.email &&
// credentials.password === user.password
// ) {
// return user;
// }

// return null;
// },
}),
],
callbacks: {
jwt({ token, user, session }) {
console.log(
"jwt-------------------------------------------------------",
session,
token,
user,
);

// if (trigger === "update" && session?.name) {
// token.name = session.name;
// }

if (user) {
return {
...token,
id: user.id,
email: user.email,
};
}

return token;
},
// async signIn({ user, account, profile, email, credentials }) {
// if (credentials) {
// return true;
// }

// console.log("email", email);
// console.log("profile", profile);
// console.log("account", account);
// console.log("user", user);

// const dbUser = await prisma.user.upsert({
// where: { email: user.email! },
// update: {
// name: user.name!,
// image: user.image,
// },
// create: {
// name: user.name!,
// email: user.email!,
// image: user.image,
// password: user.name!,
// },
// });
// // add the userId to the session object
// // user.role = dbUser.role;
// user.id = dbUser.id;

// return true;
// },
session: ({ session, token, user }) => {
console.log(
"session-------------------------------------------------------",
session,
token,
user,
);
return {
...session,
user: {
...session.user,
id: token.id,
name: token.name,
},
};
},

// @TODO - if you wanna have auth on the edge
// jwt: ({ token, profile }) => {
Expand Down
Loading
Loading