From d05fa7e27dd1691406e084473aec3974fa8e712d Mon Sep 17 00:00:00 2001 From: Sianida26 Date: Mon, 15 Jan 2024 01:39:29 +0700 Subject: [PATCH] Added sign in action --- .env | 2 +- package.json | 3 +- pnpm-lock.yaml | 116 ++++++++++++------ src/app/(auth)/login/page.tsx | 60 ++------- src/app/(auth)/register/page.tsx | 22 +--- src/app/api/auth/[...nextauth]/route.ts | 1 - src/app/api/trpc/[...trpc]/route.ts | 12 -- src/app/layout.tsx | 3 - src/features/auth/AuthError.ts | 3 +- src/features/auth/actions/signIn.ts | 68 +++++++--- src/features/auth/authUtils.ts | 9 ++ src/features/auth/index.ts | 50 -------- .../auth/providers/emailPasswordProvider.ts | 47 ------- src/features/auth/types/UserClaims.d.ts | 7 ++ src/trpc/TrpcProvider.tsx | 37 ------ src/trpc/index.ts | 6 - src/trpc/routes/_app.ts | 21 ---- src/trpc/routes/auth.ts | 46 ------- src/trpc/utils.ts | 25 ---- 19 files changed, 167 insertions(+), 371 deletions(-) delete mode 100644 src/app/api/auth/[...nextauth]/route.ts delete mode 100644 src/app/api/trpc/[...trpc]/route.ts delete mode 100644 src/features/auth/providers/emailPasswordProvider.ts create mode 100644 src/features/auth/types/UserClaims.d.ts delete mode 100644 src/trpc/TrpcProvider.tsx delete mode 100644 src/trpc/index.ts delete mode 100644 src/trpc/routes/_app.ts delete mode 100644 src/trpc/routes/auth.ts delete mode 100644 src/trpc/utils.ts diff --git a/.env b/.env index a273a63..32f917e 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ DATABASE_URL=mysql://root:root@localhost:3306/dashboard_template -NEXTAUTH_SECRET= \ No newline at end of file +JWT_SECRET= \ No newline at end of file diff --git a/package.json b/package.json index 44a522b..be8dbee 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,10 @@ "@trpc/react-query": "^10.45.0", "@trpc/server": "^10.45.0", "@types/bcrypt": "^5.0.2", + "@types/jsonwebtoken": "^9.0.5", "bcrypt": "^5.1.1", + "jsonwebtoken": "^9.0.2", "next": "14.0.4", - "next-auth": "beta", "react": "^18.2.0", "react-dom": "^18.2.0", "superjson": "^2.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c946c4f..c59a4e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,15 +41,18 @@ dependencies: '@types/bcrypt': specifier: ^5.0.2 version: 5.0.2 + '@types/jsonwebtoken': + specifier: ^9.0.5 + version: 9.0.5 bcrypt: specifier: ^5.1.1 version: 5.1.1 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 next: specifier: 14.0.4 version: 14.0.4(react-dom@18.2.0)(react@18.2.0) - next-auth: - specifier: beta - version: 5.0.0-beta.4(next@14.0.4)(react@18.2.0) react: specifier: ^18.2.0 version: 18.2.0 @@ -113,22 +116,6 @@ packages: engines: {node: '>=10'} dev: true - /@auth/core@0.18.4: - resolution: {integrity: sha512-GsNhsP1xE/3FoNS3dVkPjqRljLNJ4iyL2OLv3klQGNvw3bMpROFcK4lqhx7+pPHiamnVaYt2vg1xbB+lsNaevg==} - peerDependencies: - nodemailer: ^6.8.0 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@panva/hkdf': 1.1.1 - cookie: 0.6.0 - jose: 5.2.0 - oauth4webapi: 2.4.3 - preact: 10.11.3 - preact-render-to-string: 5.2.3(preact@10.11.3) - dev: false - /@auth/core@0.20.0: resolution: {integrity: sha512-04lQH58H5d/9xQ63MOTDTOC7sXWYlr/RhJ97wfFLXzll7nYyCKbkrT3ZMdzdLC5O+qt90sQDK85TAtLlcZ2WBg==} peerDependencies: @@ -646,6 +633,12 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/jsonwebtoken@9.0.5: + resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} + dependencies: + '@types/node': 20.10.6 + dev: false + /@types/node@20.10.6: resolution: {integrity: sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==} dependencies: @@ -1009,6 +1002,10 @@ packages: update-browserslist-db: 1.0.13(browserslist@4.22.2) dev: true + /buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + dev: false + /busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -1231,6 +1228,12 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true + /ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + dependencies: + safe-buffer: 5.2.1 + dev: false + /electron-to-chromium@1.4.623: resolution: {integrity: sha512-lKoz10iCYlP1WtRYdh5MvocQPWVRoI7ysp6qf18bmeBgR8abE6+I2CsfyNKztRDZvhdWc+krKT6wS7Neg8sw3A==} dev: true @@ -2203,6 +2206,22 @@ packages: minimist: 1.2.8 dev: true + /jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.5.4 + dev: false + /jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -2213,6 +2232,21 @@ packages: object.values: 1.1.7 dev: true + /jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + dev: false + + /jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + dev: false + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -2264,10 +2298,38 @@ packages: p-locate: 5.0.0 dev: true + /lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + dev: false + + /lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + dev: false + + /lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + dev: false + + /lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + dev: false + + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: false + + /lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + dev: false + /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true + /lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + dev: false + /loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2357,7 +2419,6 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: true /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2376,21 +2437,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /next-auth@5.0.0-beta.4(next@14.0.4)(react@18.2.0): - resolution: {integrity: sha512-vgocjvwPA8gxd/zrIP/vr9lJ/HeNe+C56lPP1D3sdyenHt8KncQV6ro7q0xCsDp1fcOKx7WAWVZH5o8aMxDzgw==} - peerDependencies: - next: ^14 - nodemailer: ^6.6.5 - react: ^18.2.0 - peerDependenciesMeta: - nodemailer: - optional: true - dependencies: - '@auth/core': 0.18.4 - next: 14.0.4(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - dev: false - /next@14.0.4(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-qbwypnM7327SadwFtxXnQdGiKpkuhaRLE2uq62/nRul9cj9KhQ5LhHmlziTNqUidZotw/Q1I9OjirBROdUJNgA==} engines: {node: '>=18.17.0'} diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index ac4fef7..d88dfb2 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { signIn } from "next-auth/react"; +import signIn from "@/features/auth/actions/signIn"; import { Paper, PasswordInput, @@ -12,51 +12,17 @@ import { Button, Alert, } from "@mantine/core"; -import { useForm } from "@mantine/form"; -import React, { useState } from "react"; +import React from "react"; +import { useFormState } from "react-dom"; -/** - * Type definition for login form values. - */ -interface LoginFormType { - email: string; - password: string; -} +const initialState = { + errors: { + message: "", + }, +}; -/** - * LoginPage component: Renders a login form allowing users to authenticate using their credentials. - * Utilizes Mantine for UI components and Next-Auth for authentication handling. - * - * @returns React functional component representing the login page. - */ export default function LoginPage() { - const form = useForm({ - initialValues: { - email: "", - password: "", - }, - }); - - const [errorMessage, setErrorMessage] = useState(""); - - /** - * Handles form submission by calling Next-Auth signIn function with credentials. - * - * @param values - Object containing email and password entered by the user. - */ - const handleFormSubmit = async (values: LoginFormType) => { - try { - await signIn("credentials", { - email: values.email, - password: values.password, - callbackUrl: "/", - redirect: false, - }); - } catch (e) { - // TODO: Handle proper error message - setErrorMessage("Email/Password does not match"); - } - }; + const [state, formAction] = useFormState(signIn, initialState); return (
@@ -64,16 +30,16 @@ export default function LoginPage() { Welcome -
+ - {errorMessage ? ( + {state.errors.message ? ( - {errorMessage} + {state.errors.message} ) : null} diff --git a/src/app/(auth)/register/page.tsx b/src/app/(auth)/register/page.tsx index deb5c3c..5578d3b 100644 --- a/src/app/(auth)/register/page.tsx +++ b/src/app/(auth)/register/page.tsx @@ -1,7 +1,5 @@ "use client"; -import { auth } from "@/features/auth"; -import { signIn } from "next-auth/react"; import { Paper, PasswordInput, @@ -14,7 +12,6 @@ import { } from "@mantine/core"; import { useForm } from "@mantine/form"; import React, { useEffect } from "react"; -import { api } from "@/trpc/utils"; export interface RegisterFormSchema { email: string, @@ -40,30 +37,13 @@ export default function RegisterPage() { } }); - const registerMutation = api.auth.register.useMutation({ - onSuccess: async () => { - console.log("success. signing in") - await signIn("credentials", { - email: form.values.email, - password: form.values.password, - callbackUrl: "/dashboard" - }) - console.log("signed in") - } - }) - - const handleFormSubmit = (values: RegisterFormSchema) => { - // await - registerMutation.mutate(values) - } - return (
Register - + {})}> - fetchRequestHandler({ - endpoint: '/api/trpc', - req, - router: appRouter, - createContext: () => ({ }) - }); - -export { handler as GET, handler as POST }; \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a8d39c9..b16587e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,7 +5,6 @@ import "./globals.css" import '@mantine/core/styles.css'; import { ColorSchemeScript, MantineProvider } from '@mantine/core'; -import { TrpcProvider } from '@/trpc/TrpcProvider'; const inter = Inter({ subsets: ['latin'] }) @@ -26,9 +25,7 @@ export default function RootLayout({ - {children} - diff --git a/src/features/auth/AuthError.ts b/src/features/auth/AuthError.ts index 33a87f8..da0f4d7 100644 --- a/src/features/auth/AuthError.ts +++ b/src/features/auth/AuthError.ts @@ -4,7 +4,8 @@ export enum AuthErrorCode { EMAIL_NOT_FOUND = "EMAIL_NOT_FOUND", EMPTY_USER_HASH = "EMPTY_USER_HASH", INVALID_CREDENTIALS = "INVALID_CREDENTIALS", - USER_ALREADY_EXISTS = "USER_ALREADY_EXISTS" + JWT_SECRET_EMPTY = "JWT_SECRET_NOT_EMPTY", + USER_ALREADY_EXISTS = "USER_ALREADY_EXISTS", } export default class AuthError extends BaseError { diff --git a/src/features/auth/actions/signIn.ts b/src/features/auth/actions/signIn.ts index db4b9f6..8f48ae1 100644 --- a/src/features/auth/actions/signIn.ts +++ b/src/features/auth/actions/signIn.ts @@ -1,7 +1,10 @@ +"use server" import prisma from "@/db"; import { User } from "@prisma/client"; import AuthError, { AuthErrorCode } from "../AuthError"; -import { comparePassword } from "../authUtils"; +import { comparePassword, createJwtToken } from "../authUtils"; +import { cookies } from "next/headers"; +import { redirect } from 'next/navigation'; /** * Validates the user by their email and password. @@ -13,22 +16,55 @@ import { comparePassword } from "../authUtils"; * @returns The authenticated user object. * @throws {AuthError} - EMAIL_NOT_FOUND if no user is found, INVALID_CREDENTIALS if the password doesn't match, or other auth-related errors. */ -export default async function signIn(email: string, password: string): Promise { - // Retrieve user from the database by email - const user = await prisma.user.findUnique({ - where: { email } - }); +export default async function signIn(prevState: any, rawFormData: FormData) { + //TODO: Add Throttling + try { + const formData = { + email: rawFormData.get("email") as string, + password: rawFormData.get("password") as string + } - // Throw if user not found - if (!user) throw new AuthError(AuthErrorCode.EMAIL_NOT_FOUND, 401); + // Retrieve user from the database by email + const user = await prisma.user.findUnique({ + where: { email: formData.email } + }); - // Throw if user has no password hash - // TODO: Add check if the user uses another provider - if (!user.passwordHash) throw new AuthError(AuthErrorCode.EMPTY_USER_HASH, 500); - - // Compare the provided password with the user's stored password hash - const isMatch = await comparePassword(password, user.passwordHash); - if (!isMatch) throw new AuthError(AuthErrorCode.INVALID_CREDENTIALS, 401); + // Throw if user not found + if (!user) throw new AuthError(AuthErrorCode.EMAIL_NOT_FOUND, 401); - return user; + // Throw if user has no password hash + // TODO: Add check if the user uses another provider + if (!user.passwordHash) throw new AuthError(AuthErrorCode.EMPTY_USER_HASH, 500); + + // Compare the provided password with the user's stored password hash + const isMatch = await comparePassword(formData.password, user.passwordHash); + if (!isMatch) throw new AuthError(AuthErrorCode.INVALID_CREDENTIALS, 401); + + //Set cookie + //TODO: Auth: Add expiry + const token = createJwtToken({id: user.id}); + + cookies().set("token",token); + + redirect("/dashboard"); + + } catch (e: unknown){ + if (e instanceof AuthError){ + if ([ + AuthErrorCode.EMAIL_NOT_FOUND, AuthErrorCode.INVALID_CREDENTIALS + ]) { + return { + errors: { + message: "Email/Password combination is not match. Please try again" + } + } + } + } + + return { + errors: { + message: "There's something wrong happened on the server. Please try again or contact administrator" + } + } + } } diff --git a/src/features/auth/authUtils.ts b/src/features/auth/authUtils.ts index 8cde8f1..d116ce8 100644 --- a/src/features/auth/authUtils.ts +++ b/src/features/auth/authUtils.ts @@ -3,6 +3,8 @@ import { User } from "@prisma/client"; import * as bcrypt from "bcrypt"; import AuthError, { AuthErrorCode } from "./AuthError"; import authConfig from "@/config/auth"; +import jwt from "jsonwebtoken" +import UserClaims from "./types/UserClaims"; /** * Hashes a plain text password using bcrypt. @@ -24,3 +26,10 @@ export async function hashPassword(password: string): Promise { export async function comparePassword(password: string, hash: string): Promise { return bcrypt.compare(password, hash); } + +export function createJwtToken(userclaims: UserClaims, options?: jwt.SignOptions){ + const secret = process.env.JWT_SECRET; + if (!secret) throw new AuthError(AuthErrorCode.JWT_SECRET_EMPTY); + const token = jwt.sign(userclaims, secret, options); + return token; +} \ No newline at end of file diff --git a/src/features/auth/index.ts b/src/features/auth/index.ts index 5b7e553..e69de29 100644 --- a/src/features/auth/index.ts +++ b/src/features/auth/index.ts @@ -1,50 +0,0 @@ -import NextAuth from "next-auth"; -import emailPasswordProvider from "./providers/emailPasswordProvider"; -import prisma from "@/db"; -import { PrismaAdapter } from "@auth/prisma-adapter"; - -const nextAuth = NextAuth({ - adapter: PrismaAdapter(prisma), - session: { - strategy: "jwt" - }, - providers:[ - emailPasswordProvider - ], - callbacks: { - // signIn: ({ user, account, profile, email, credentials}) => { - // console.log("sign in callback") - // console.table({user, account, profile, email, credentials}) - // return true; - // }, - // session: async({session, user, token}) => { - // if (session.user){ - // session.user.id = token.userId as string; - // } - // return session; - // }, - // jwt: async ({ token, user, account, profile }) => { - // if(account && account.type === "credentials") { - // token.userId = account.providerAccountId; // this is Id that coming from authorize() callback - // } - // return token - // }, - // redirect: async ({url, baseUrl}) => { - // console.log("redirect callback called") - // console.table({url, baseUrl}) - // return url - // } - }, - pages: { - signIn: "/login" - } -}); - -export const { - signIn, - signOut, - handlers: { GET, POST}, - auth -} = nextAuth; - -export default nextAuth; diff --git a/src/features/auth/providers/emailPasswordProvider.ts b/src/features/auth/providers/emailPasswordProvider.ts deleted file mode 100644 index 81efc07..0000000 --- a/src/features/auth/providers/emailPasswordProvider.ts +++ /dev/null @@ -1,47 +0,0 @@ -import CredentialsProvider from "next-auth/providers/credentials" -import AuthError, { AuthErrorCode } from "../AuthError"; -import BaseError from "@/BaseError"; -import signIn from "../actions/signIn"; - -/** - * Factory function to create a credential provider. - * It defines the structure of the credentials and includes an authorization function - * to validate the user's credentials. - * - * @returns A CredentialsProvider instance configured for email-password authentication. - */ -const credential = CredentialsProvider({ - name: "email-password", - credentials: { - email: { - label: "Email", - type: "email", - }, - password: { - label: "password", - type: "password" - } - }, - authorize: async (credentials) => { - try { - // Ensure credentials are properly formatted strings - if (typeof credentials.email !== "string" || typeof credentials.password !== "string"){ - throw new AuthError(AuthErrorCode.INVALID_CREDENTIALS, 401); - } - - // Validate user with provided credentials - const user = await signIn(credentials.email, credentials.password); - return user; - } catch (e: unknown){ - // Handle specific authentication errors, re-throw others - if (e instanceof AuthError){ - // Auth invalid - if ([AuthErrorCode.EMAIL_NOT_FOUND, AuthErrorCode.EMPTY_USER_HASH, AuthErrorCode.INVALID_CREDENTIALS].includes(e.errorCode as AuthErrorCode)) - return null; - } - throw e; - } - }, -}) - -export default credential; \ No newline at end of file diff --git a/src/features/auth/types/UserClaims.d.ts b/src/features/auth/types/UserClaims.d.ts new file mode 100644 index 0000000..5997a53 --- /dev/null +++ b/src/features/auth/types/UserClaims.d.ts @@ -0,0 +1,7 @@ +import { User } from "@prisma/client" + +type UserClaims = { + id: User["id"] +} + +export default UserClaims \ No newline at end of file diff --git a/src/trpc/TrpcProvider.tsx b/src/trpc/TrpcProvider.tsx deleted file mode 100644 index 44599f3..0000000 --- a/src/trpc/TrpcProvider.tsx +++ /dev/null @@ -1,37 +0,0 @@ -"use client"; - -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { httpBatchLink, getFetch, loggerLink } from "@trpc/client"; -import { useState } from "react"; -import superjson from "superjson"; -import { api as trpc } from "@/trpc/utils"; -import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; - -export const TrpcProvider: React.FC<{ children: React.ReactNode }> = ({ - children, -}) => { - const [queryClient] = useState(() => new QueryClient()); - const [trpcClient] = useState(() => - trpc.createClient({ - links: [ - httpBatchLink({ - url: 'http://localhost:3000/api/trpc', - // You can pass any HTTP headers you wish here - async headers() { - return { - // authorization: getAuthCookie(), - }; - }, - }), - ], - }), - ); - return ( - - - {children} - - - - ); -}; diff --git a/src/trpc/index.ts b/src/trpc/index.ts deleted file mode 100644 index 07aee84..0000000 --- a/src/trpc/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { initTRPC } from '@trpc/server'; -const t = initTRPC.create(); - -// Base router and procedure helpers -export const router = t.router; -export const procedure = t.procedure; \ No newline at end of file diff --git a/src/trpc/routes/_app.ts b/src/trpc/routes/_app.ts deleted file mode 100644 index 3a16cfe..0000000 --- a/src/trpc/routes/_app.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { z } from 'zod'; -import { procedure, router } from '..'; -import authRouter from './auth'; - -export const appRouter = router({ - hello: procedure - .input( - z.object({ - text: z.string(), - }), - ) - .query((opts) => { - return { - greeting: `hello ${opts.input.text}`, - }; - }), - auth: authRouter, -}); - -// export type definition of API -export type AppRouter = typeof appRouter; \ No newline at end of file diff --git a/src/trpc/routes/auth.ts b/src/trpc/routes/auth.ts deleted file mode 100644 index 64de428..0000000 --- a/src/trpc/routes/auth.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { z } from "zod"; -import { procedure, router } from ".."; -import prisma from "@/db"; -import createUser from "@/features/auth/actions/createUser"; -import { AuthError } from "next-auth"; -import { TRPCError } from "@trpc/server"; - -const authRouter = router({ - register: procedure - .input( - z.object({ - name: z.string(), - email: z.string().email(), - password: z.string(), - passwordConfirmation: z.string(), - }) - .refine(data => data.password === data.passwordConfirmation, { - message: "Password don't match", - path: ["passwordConfirmation"] - }) - ) - .mutation(async ({input}) => { - try { - const user = await createUser({ - email: input.email, - name: input.name, - plainPassword: input.password - }) - - return "ok" - } catch (e: unknown) { - if (e instanceof AuthError){ - throw new TRPCError({ - code: "BAD_REQUEST", - message: e.message, - cause: e - }) - } - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR" - }) - } - }) -}) - -export default authRouter; diff --git a/src/trpc/utils.ts b/src/trpc/utils.ts deleted file mode 100644 index 3f2d712..0000000 --- a/src/trpc/utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { httpBatchLink } from '@trpc/client'; -import { createTRPCNext } from '@trpc/next'; -import type { AppRouter } from './routes/_app'; -import { createTRPCReact } from '@trpc/react-query'; - -function getBaseUrl() { - if (typeof window !== 'undefined') - // browser should use relative path - return ''; - - if (process.env.VERCEL_URL) - // reference for vercel.com - return `https://${process.env.VERCEL_URL}`; - - if (process.env.RENDER_INTERNAL_HOSTNAME) - // reference for render.com - return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`; - - // assume localhost - return `http://localhost:${process.env.PORT ?? 3000}`; -} - -export const api = createTRPCReact({ - -}) \ No newline at end of file