Browse Source

ability to disable email verification requirement

Milo Schwartz 9 months ago
parent
commit
29b848fd5d

+ 4 - 0
server/config.ts

@@ -118,5 +118,9 @@ process.env.NEXT_PUBLIC_INTERNAL_API_BASE_URL = new URL(
     `http://${parsedConfig.data.server.internal_hostname}:${parsedConfig.data.server.external_port}`
 ).href;
 process.env.NEXT_PUBLIC_APP_NAME = parsedConfig.data.app.name;
+process.env.NEXT_PUBLIC_FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data
+    .flags?.require_email_verification
+    ? "true"
+    : "false";
 
 export default parsedConfig.data;

+ 8 - 4
server/middlewares/verifyUser.ts

@@ -6,11 +6,12 @@ import { users } from "@server/db/schema";
 import { eq } from "drizzle-orm";
 import createHttpError from "http-errors";
 import HttpCode from "@server/types/HttpCode";
+import config from "@server/config";
 
 export const verifySessionUserMiddleware = async (
     req: any,
     res: Response<ErrorResponse>,
-    next: NextFunction,
+    next: NextFunction
 ) => {
     const { session, user } = await verifySession(req);
     if (!session || !user) {
@@ -24,16 +25,19 @@ export const verifySessionUserMiddleware = async (
 
     if (!existingUser || !existingUser[0]) {
         return next(
-            createHttpError(HttpCode.BAD_REQUEST, "User does not exist"),
+            createHttpError(HttpCode.BAD_REQUEST, "User does not exist")
         );
     }
 
     req.user = existingUser[0];
     req.session = session;
 
-    if (!existingUser[0].emailVerified) {
+    if (
+        !existingUser[0].emailVerified &&
+        config.flags?.require_email_verification
+    ) {
         return next(
-            createHttpError(HttpCode.BAD_REQUEST, "Email is not verified"), // Might need to change the response type?
+            createHttpError(HttpCode.BAD_REQUEST, "Email is not verified") // Might need to change the response type?
         );
     }
 

+ 18 - 14
server/routers/auth/login.ts

@@ -15,6 +15,7 @@ import createHttpError from "http-errors";
 import { z } from "zod";
 import { fromError } from "zod-validation-error";
 import { verifyTotpCode } from "@server/auth/2fa";
+import config from "@server/config";
 
 export const loginBodySchema = z.object({
     email: z.string().email(),
@@ -32,7 +33,7 @@ export type LoginResponse = {
 export async function login(
     req: Request,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
     const parsedBody = loginBodySchema.safeParse(req.body);
 
@@ -40,8 +41,8 @@ export async function login(
         return next(
             createHttpError(
                 HttpCode.BAD_REQUEST,
-                fromError(parsedBody.error).toString(),
-            ),
+                fromError(parsedBody.error).toString()
+            )
         );
     }
 
@@ -67,8 +68,8 @@ export async function login(
             return next(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
-                    "Username or password is incorrect",
-                ),
+                    "Username or password is incorrect"
+                )
             );
         }
 
@@ -82,14 +83,14 @@ export async function login(
                 timeCost: 2,
                 outputLen: 32,
                 parallelism: 1,
-            },
+            }
         );
         if (!validPassword) {
             return next(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
-                    "Username or password is incorrect",
-                ),
+                    "Username or password is incorrect"
+                )
             );
         }
 
@@ -107,15 +108,15 @@ export async function login(
             const validOTP = await verifyTotpCode(
                 code,
                 existingUser.twoFactorSecret!,
-                existingUser.userId,
+                existingUser.userId
             );
 
             if (!validOTP) {
                 return next(
                     createHttpError(
                         HttpCode.BAD_REQUEST,
-                        "The two-factor code you entered is incorrect",
-                    ),
+                        "The two-factor code you entered is incorrect"
+                    )
                 );
             }
         }
@@ -126,7 +127,10 @@ export async function login(
 
         res.appendHeader("Set-Cookie", cookie);
 
-        if (!existingUser.emailVerified) {
+        if (
+            !existingUser.emailVerified &&
+            config.flags?.require_email_verification
+        ) {
             return response<LoginResponse>(res, {
                 data: { emailVerificationRequired: true },
                 success: true,
@@ -147,8 +151,8 @@ export async function login(
         return next(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
-                "Failed to authenticate user",
-            ),
+                "Failed to authenticate user"
+            )
         );
     }
 }

+ 15 - 5
server/routers/auth/requestEmailVerificationCode.ts

@@ -4,6 +4,7 @@ import HttpCode from "@server/types/HttpCode";
 import { response } from "@server/utils";
 import { User } from "@server/db/schema";
 import { sendEmailVerificationCode } from "./sendEmailVerificationCode";
+import config from "@server/config";
 
 export type RequestEmailVerificationCodeResponse = {
     codeSent: boolean;
@@ -12,8 +13,17 @@ export type RequestEmailVerificationCodeResponse = {
 export async function requestEmailVerificationCode(
     req: Request,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
+    if (!config.flags?.require_email_verification) {
+        return next(
+            createHttpError(
+                HttpCode.BAD_REQUEST,
+                "Email verification is not enabled"
+            )
+        );
+    }
+
     try {
         const user = req.user as User;
 
@@ -21,8 +31,8 @@ export async function requestEmailVerificationCode(
             return next(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
-                    "Email is already verified",
-                ),
+                    "Email is already verified"
+                )
             );
         }
 
@@ -41,8 +51,8 @@ export async function requestEmailVerificationCode(
         return next(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
-                "Failed to send email verification code",
-            ),
+                "Failed to send email verification code"
+            )
         );
     }
 }

+ 38 - 18
server/routers/auth/signup.ts

@@ -19,6 +19,7 @@ import {
     serializeSessionCookie,
 } from "@server/auth";
 import { ActionsEnum } from "@server/auth/actions";
+import config from "@server/config";
 
 export const signupBodySchema = z.object({
     email: z.string().email(),
@@ -28,13 +29,13 @@ export const signupBodySchema = z.object({
 export type SignUpBody = z.infer<typeof signupBodySchema>;
 
 export type SignUpResponse = {
-    emailVerificationRequired: boolean;
+    emailVerificationRequired?: boolean;
 };
 
 export async function signup(
     req: Request,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
     const parsedBody = signupBodySchema.safeParse(req.body);
 
@@ -42,8 +43,8 @@ export async function signup(
         return next(
             createHttpError(
                 HttpCode.BAD_REQUEST,
-                fromError(parsedBody.error).toString(),
-            ),
+                fromError(parsedBody.error).toString()
+            )
         );
     }
 
@@ -64,6 +65,15 @@ export async function signup(
             .where(eq(users.email, email));
 
         if (existing && existing.length > 0) {
+            if (!config.flags?.require_email_verification) {
+                return next(
+                    createHttpError(
+                        HttpCode.BAD_REQUEST,
+                        "A user with that email address already exists"
+                    )
+                );
+            }
+
             const user = existing[0];
 
             // If the user is already verified, we don't want to create a new user
@@ -71,8 +81,8 @@ export async function signup(
                 return next(
                     createHttpError(
                         HttpCode.BAD_REQUEST,
-                        "A user with that email address already exists",
-                    ),
+                        "A user with that email address already exists"
+                    )
                 );
             }
 
@@ -85,8 +95,8 @@ export async function signup(
                 return next(
                     createHttpError(
                         HttpCode.BAD_REQUEST,
-                        "A verification email was already sent to this email address. Please check your email for the verification code.",
-                    ),
+                        "A verification email was already sent to this email address. Please check your email for the verification code."
+                    )
                 );
             } else {
                 // If the user was created more than 2 hours ago, we want to delete the old user and create a new one
@@ -101,7 +111,7 @@ export async function signup(
             dateCreated: moment().toISOString(),
         });
 
-        // give the user their default permissions: 
+        // give the user their default permissions:
         // await db.insert(userActions).values({
         //     userId: userId,
         //     actionId: ActionsEnum.createOrg,
@@ -113,15 +123,25 @@ export async function signup(
         const cookie = serializeSessionCookie(token);
         res.appendHeader("Set-Cookie", cookie);
 
-        sendEmailVerificationCode(email, userId);
+        if (config.flags?.require_email_verification) {
+            sendEmailVerificationCode(email, userId);
+
+            return response<SignUpResponse>(res, {
+                data: {
+                    emailVerificationRequired: true,
+                },
+                success: true,
+                error: false,
+                message: `User created successfully. We sent an email to ${email} with a verification code.`,
+                status: HttpCode.OK,
+            });
+        }
 
         return response<SignUpResponse>(res, {
-            data: {
-                emailVerificationRequired: true,
-            },
+            data: {},
             success: true,
             error: false,
-            message: `User created successfully. We sent an email to ${email} with a verification code.`,
+            message: "User created successfully",
             status: HttpCode.OK,
         });
     } catch (e) {
@@ -129,15 +149,15 @@ export async function signup(
             return next(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
-                    "A user with that email address already exists",
-                ),
+                    "A user with that email address already exists"
+                )
             );
         } else {
             return next(
                 createHttpError(
                     HttpCode.INTERNAL_SERVER_ERROR,
-                    "Failed to create user",
-                ),
+                    "Failed to create user"
+                )
             );
         }
     }

+ 18 - 8
server/routers/auth/verifyEmail.ts

@@ -8,6 +8,7 @@ import { db } from "@server/db";
 import { User, emailVerificationCodes, users } from "@server/db/schema";
 import { eq } from "drizzle-orm";
 import { isWithinExpirationDate } from "oslo";
+import config from "@server/config";
 
 export const verifyEmailBody = z.object({
     code: z.string(),
@@ -22,16 +23,25 @@ export type VerifyEmailResponse = {
 export async function verifyEmail(
     req: Request,
     res: Response,
-    next: NextFunction,
+    next: NextFunction
 ): Promise<any> {
+    if (!config.flags?.require_email_verification) {
+        return next(
+            createHttpError(
+                HttpCode.BAD_REQUEST,
+                "Email verification is not enabled"
+            )
+        );
+    }
+
     const parsedBody = verifyEmailBody.safeParse(req.body);
 
     if (!parsedBody.success) {
         return next(
             createHttpError(
                 HttpCode.BAD_REQUEST,
-                fromError(parsedBody.error).toString(),
-            ),
+                fromError(parsedBody.error).toString()
+            )
         );
     }
 
@@ -41,7 +51,7 @@ export async function verifyEmail(
 
     if (user.emailVerified) {
         return next(
-            createHttpError(HttpCode.BAD_REQUEST, "Email is already verified"),
+            createHttpError(HttpCode.BAD_REQUEST, "Email is already verified")
         );
     }
 
@@ -63,8 +73,8 @@ export async function verifyEmail(
             return next(
                 createHttpError(
                     HttpCode.BAD_REQUEST,
-                    "Invalid verification code",
-                ),
+                    "Invalid verification code"
+                )
             );
         }
 
@@ -81,8 +91,8 @@ export async function verifyEmail(
         return next(
             createHttpError(
                 HttpCode.INTERNAL_SERVER_ERROR,
-                "Failed to verify email",
-            ),
+                "Failed to verify email"
+            )
         );
     }
 }

+ 6 - 4
src/app/auth/verify-email/page.tsx

@@ -2,11 +2,13 @@ import VerifyEmailForm from "@app/app/auth/verify-email/VerifyEmailForm";
 import { verifySession } from "@app/lib/auth/verifySession";
 import { redirect } from "next/navigation";
 
-export default async function Page(
-    props: {
-        searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
+export default async function Page(props: {
+    searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
+}) {
+    if (!process.env.NEXT_PUBLIC_FLAGS_EMAIL_VERIFICATION_REQUIRED) {
+        redirect("/");
     }
-) {
+
     const searchParams = await props.searchParams;
     const user = await verifySession();