0
\$\begingroup\$

I'm working on a Next 14 application where I want to protect certain pages. I implemented client auth using Firebase so currently the authentication logic runs client-side to check if the user is logged in or not, and based on that, either allow access to the page or redirect to the login page. Note that my pages are server components when child is client where I check whether user is logged in or not. Which solutions can I consider to make it better then my current approach.

Current solution:

Auth:

const Auth = ({ type }: { type: AuthType }) => {
  const {
    register,
    handleSubmit,
    formState: { isValid },
  } = useForm<Inputs>();

  const { mutate, isPending, error } = useAuth(type);

  const onSubmit: SubmitHandler<Inputs> = (data) => mutate(data);

  return (
    <div className={styles.authForm}>
      <div className={styles.header}>
        <h1>{type}</h1>
        <p>
          {type === "Login"
            ? "Login to get back to your account"
            : "Register to start chatting with people"}
        </p>
      </div>
      <form onSubmit={handleSubmit(onSubmit)}>
        {type === "Register" && (
          <Field
            placeholder="Name"
            name="name"
            register={register}
            rules={{
              required: true,
              minLength: 2,
              maxLength: 25,
            }}
            isPending={isPending}
          />
        )}
        <Field
          placeholder="Email address"
          name="email"
          isPending={isPending}
          register={register}
          rules={{
            required: true,
            pattern: {
              value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
              message: "Invalid email address",
            },
          }}
        />
        <Field
          placeholder="Password"
          type="password"
          name="password"
          register={register}
          isPending={isPending}
          rules={{
            required: true,
          }}
        />
        {error && <ErrorMessage>{error.message}</ErrorMessage>}
        <Button isPending={isPending} isValid={isValid}>
          Continue
        </Button>
        {type === "Login" ? (
          <Link href="register">Register</Link>
        ) : (
          <Link href="login">Login</Link>
        )}
      </form>
    </div>
  );
};

Service:

class AuthService {
  async registerUser(data: Inputs) {
    try {
      const userCredential = await createUserWithEmailAndPassword(
        auth,
        data.email,
        data.password
      );
      const user = userCredential.user;
      await setDoc(doc(db, "users", user.uid), {
        uid: user.uid,
        name: data.name,
        email: user.email,
      });
      updateProfile(user, {
        displayName: data.name,
      });

      return user;
    } catch (error) {
      throw error;
    }
  }

  async loginUser(data: Inputs) {
    try {
      const userCredential = await signInWithEmailAndPassword(
        auth,
        data.email,
        data.password
      );
      const user = userCredential.user;
      return user;
    } catch (error) {
      throw error;
    }
  }

  logoutUser(router: AppRouterInstance) {
    signOut(auth)
      .then(() => router.push("/login"))
      .catch((error) => alert(error));
  }
}

withAuth:

const withAuth = (Component: React.FC) => {
  return (props: any) => {
    const { currentUser, loading } = useCurrentUserContext();
    const router = useRouter();

    useEffect(() => {
      if (!loading && !currentUser) {
        router.push("/login");
      }
    }, [currentUser, loading, router]);

    if (loading) {
      return <div>Loading...</div>;
    }

    return currentUser ? <Component {...props} /> : null;
  };
};

Page:

export const metadata: Metadata = {
  title: "Users",
};

const Users = () => {
  return (
    <main className={styles.usersPage}>
      <UsersList />
    </main>
  );
};

export default Users;

UsersList:

const UsersList = () => {
  const { users, isLoading, isError, isSuccess } = useUsers();

  return (
    <>
      {(isLoading || isError) && (
        <div className={styles.status}>
          <Status isLoading={isLoading} isError={isError} />
        </div>
      )}
      {isSuccess && (
        <div className={styles.usersList}>
          {users?.map((user) => (
            <UserCard key={user.uid} data={user} />
          ))}
        </div>
      )}
    </>
  );
};

export default withAuth(UsersList);
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.