create-auth-js-boiler
Version:
Create a new auth-js-boiler project
284 lines (258 loc) • 7.33 kB
text/typescript
"use server";
import {
registerSchemaType,
registerSchema,
loginSchema,
loginSchemaType,
forgotPasswordSchema,
forgotPasswordSchemaType,
newPasswordSchema,
newPasswordSchemaType,
} from "@/schemas/auth.schema";
import { signIn } from "@/auth";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/prisma";
import { AuthError } from "next-auth";
import {
generateVerificationToken,
generatePasswordResetToken,
generateTwoFactorToken,
} from "@/lib/tokens";
import {
sendVerificationEmail,
sendPasswordResetEmail,
sendTwoFactorTokenEmail,
} from "@/lib/mail";
import { getVerificationTokenByToken } from "@/lib/verification-token";
import { getPasswordResetTokenByToken } from "@/lib/passwordResetToken";
import { getTwoFactorTokenByEmail } from "@/lib/two-factor-token";
import { getTwoFactorConfirmationByUserId } from "@/lib/two-factor-confirmation";
export const googleSignIn = async () => {
await signIn("google");
};
export const register = async (values: registerSchemaType) => {
const validatedFields = registerSchema.safeParse(values);
if (!validatedFields.success) {
return { error: "Invalid fields!" };
}
const { email, name, password } = validatedFields.data;
const hashedPassword = await bcrypt.hash(password, 10);
const user = await prisma.user.findUnique({
where: {
email,
},
});
if (user) {
return { error: "Something went wrong" };
}
await prisma.user.create({
data: {
email,
name,
password: hashedPassword,
},
});
const verificationToken = await generateVerificationToken(email);
await sendVerificationEmail(verificationToken.email, verificationToken.token);
return { success: "Confirmation email sent!" };
};
export const login = async (values: loginSchemaType) => {
const validatedFields = loginSchema.safeParse(values);
if (!validatedFields.success) {
return { error: "Invalid fields!" };
}
const { email, password, code } = validatedFields.data;
const existingUser = await prisma.user.findUnique({
where: {
email,
},
});
if (!existingUser || !existingUser.password || !existingUser.email) {
return { error: "Invalid credentials!" };
}
const passwordMatch = await bcrypt.compare(password, existingUser.password);
if (!passwordMatch) {
return { error: "Invalid credentials!" };
}
if (!existingUser.emailVerified) {
const verificationToken = await generateVerificationToken(
existingUser.email,
);
await sendVerificationEmail(
verificationToken.email,
verificationToken.token,
);
return {
success: "Confirmation email sent!",
redirectUrl: "/auth/login",
};
}
if (existingUser.isTwoFactorEnabled && existingUser.email) {
if (code) {
const twoFactorToken = await getTwoFactorTokenByEmail(existingUser.email);
if (!twoFactorToken) {
return { error: "Invalid two factor token!" };
}
if (twoFactorToken.token !== code) {
return { error: "Invalid two factor token!" };
}
const hasExpired = new Date(twoFactorToken.expires) < new Date();
if (hasExpired) {
return { error: "Token expired!" };
}
await prisma.twoFactorToken.delete({
where: {
id: twoFactorToken.id,
},
});
const existingConfirmation = await getTwoFactorConfirmationByUserId(
existingUser.id,
);
if (existingConfirmation) {
await prisma.twoFactorConfirmation.delete({
where: {
id: existingConfirmation.id,
},
});
}
await prisma.twoFactorConfirmation.create({
data: {
userId: existingUser.id,
},
});
} else {
const twoFactorToken = await generateTwoFactorToken(existingUser.email);
await sendTwoFactorTokenEmail(twoFactorToken.email, twoFactorToken.token);
return {
twoFactor: true,
};
}
}
try {
await signIn("credentials", {
email,
password,
redirect: false,
});
} catch (error) {
if (error instanceof AuthError) {
switch (error.type) {
case "CredentialsSignin":
return { error: "Invalid credentials!" };
default:
return { error: "Something went wrong!" };
}
}
throw error;
}
let redirectUrl;
if (existingUser.role == "MODERATOR") {
redirectUrl = "/moderator";
} else if (existingUser.role == "ADMIN") {
redirectUrl = "/admin";
} else if (existingUser.role == "USER") {
redirectUrl = "/";
}
return {
success: "Logged in successfully!",
redirectUrl: redirectUrl, // Include the redirect URL
};
};
export const newVerification = async (token: string) => {
const existingToken = await getVerificationTokenByToken(token);
if (!existingToken) {
return { error: "Invalid token!" };
}
const hasExpired = new Date(existingToken.expires) < new Date();
if (hasExpired) {
return { error: "Token expired!" };
}
const existingUser = await prisma.user.findUnique({
where: {
email: existingToken.email,
},
});
if (!existingUser) {
return { error: "Something went wrong!" };
}
await prisma.user.update({
where: {
id: existingUser.id,
},
data: {
emailVerified: new Date(),
email: existingToken.email,
},
});
await prisma.verificationToken.delete({
where: {
id: existingToken.id,
},
});
return { success: "Email verified successfully!" };
};
export const forgotPassword = async (values: forgotPasswordSchemaType) => {
const validatedFields = forgotPasswordSchema.safeParse(values);
if (!validatedFields.success) {
return { error: "Invalid fields!" };
}
const { email } = validatedFields.data;
const existingUser = await prisma.user.findUnique({
where: {
email,
},
});
if (!existingUser) {
return { error: "Something went wrong!" };
}
const passwordResetToken = await generatePasswordResetToken(email);
await sendPasswordResetEmail(
passwordResetToken.email,
passwordResetToken.token,
);
return { success: "Reset email sent!" };
};
export const newPassword = async (
values: newPasswordSchemaType,
token?: string,
) => {
if (!token) {
return { error: "Missing token!" };
}
const validatedFields = newPasswordSchema.safeParse(values);
if (!validatedFields.success) {
return { error: "Invalid fields!" };
}
const { password } = validatedFields.data;
const existingToken = await getPasswordResetTokenByToken(token);
if (!existingToken) {
return { error: "Invalid token!" };
}
const hasExpired = new Date(existingToken.expires) < new Date();
if (hasExpired) {
return { error: "Token expired!" };
}
const existingUser = await prisma.user.findUnique({
where: {
email: existingToken.email,
},
});
if (!existingUser) {
return { error: "Something went wrong!" };
}
const hashedPassword = await bcrypt.hash(password, 10);
await prisma.user.update({
where: {
id: existingUser.id,
},
data: {
password: hashedPassword,
},
});
await prisma.passwordResetToken.delete({
where: {
id: existingToken.id,
},
});
return { success: "Password updated successfully!" };
};