DEV Community

Cover image for Authentication with Firebase
Melih Şahin
Melih Şahin

Posted on

Authentication with Firebase

Hello there
In today's article, I will tell you how to authenticate using firebase in the Next js project.

First of all, without doing this process, we need to log in to firebase with our google account and create a project. Let's perform these steps in order.

Let's go to https://firebase.google.com and log in with our google account. A screen like the one below will greet us.

Image description

By clicking on the Create a project button, we create our project by naming it.

Image description

When we complete the steps and create the project, it shows us a screen like the one below.

Image description

When we come to the console panel, we save our web application by selecting the web section I show with the red arrow and get the information to add the firebase sdk to our project.

Image description

Image description

Image description

We create a next js project in our local and then include the firebase package in our project. We will use the firebase config information it gives us on the screen by adding it to our environment file.

After completing the above process, we have one last action to do on the console screen. We need to go to the Authentication section and select our authentication method from the "sign-in method" tab. Since we want to authenticate with email and password in this article, we select "email/password" from the native providers section.

Image description

We need to click on the "email/password" field and activate it. Then we save and complete our operations with the console.

Image description

Now let's go back to our next js project and create the protected route that only logged in users can see.

login.tsx

"use client";
import * as Yup from "yup";
import Link from "next/link";
import { useFormik } from "formik";
import { redirect } from "next/navigation";
import toast, { Toaster } from "react-hot-toast";
import { Button } from "@nextui-org/react";
import EmailInput from "@/components/emailInput/EmailInput";
import PasswordInput from "@/components/passwordInput/PasswordInput";
import { useAuthState } from "react-firebase-hooks/auth";
import { signInWithEmailAndPassword } from "firebase/auth";

import { auth } from "@/lib/firebaseConfig";

const Login = () => {
  const [user, loading] = useAuthState(auth);

  const validationSchema = Yup.object().shape({
    email: Yup.string()
      .email("invalid or incomplete email")
      .max(50, "email is too long")
      .required("required"),
    password: Yup.string()
      .min(6, "password is too short must be at least 6 characters")
      .required("required"),
  });

  const formik = useFormik({
    initialValues: {
      email: "",
      password: "",
    },
    validationSchema,
    onSubmit: async ({
      email,
      password,
    }: {
      email: string;
      password: string;
    }) => {
      const res = await signInWithEmailAndPassword(auth, email, password);
      if (res?.user) {
        sessionStorage.setItem("user", "true");
        redirect("/");
      } else {
        toast.error("Check your user information!");
      }
    },
  });

  const { errors, values, touched, handleSubmit, handleChange, isSubmitting } =
    formik;

  if (typeof window !== "undefined") {
    if (user && sessionStorage.getItem("user")) return redirect("/");
  }

  if (loading) return <p>Checking...</p>;

  return (
    <div className="flex min-h-screen items-center justify-center bg-black">
      <div className="flex w-full max-w-md flex-col rounded-lg bg-white p-8 shadow-md">
        <div className="text-black-1 mb-12 flex flex-col items-center justify-center gap-y-3">
          <p className="text-2xl font-bold">Example App</p>
        </div>
        <form
          noValidate
          onSubmit={handleSubmit}
          className="flex flex-col gap-y-6"
        >
          <Toaster position="top-right" />
          <EmailInput
            handleChange={handleChange}
            values={values}
            errors={errors}
            touched={touched}
          />
          <PasswordInput
            handleChange={handleChange}
            values={values}
            errors={errors}
            touched={touched}
          />
          <Button
            fullWidth={true}
            type="submit"
            color="success"
            className="bg-primary rounded-lg px-6 py-3.5 text-lg text-white hover:bg-blue-600"
            isLoading={isSubmitting}
          >
            Login
          </Button>
        </form>
        <p className="mt-4 text-center text-sm text-gray-600">
          <Link href="/signup" className="text-blue-500">
            Sign up
          </Link>
        </p>
      </div>
    </div>
  );
};

export default Login;

Enter fullscreen mode Exit fullscreen mode

signup.tsx

"use client";
import * as Yup from "yup";
import Link from "next/link";
import { useFormik } from "formik";
import { redirect } from "next/navigation";
import { auth } from "@/lib/firebaseConfig";
import { updateProfile } from "firebase/auth";
import toast, { Toaster } from "react-hot-toast";
import { Button, Input } from "@nextui-org/react";
import { useAuthState } from "react-firebase-hooks/auth";
import { createUserWithEmailAndPassword } from "@firebase/auth";

import EmailInput from "@/components/emailInput/EmailInput";
import PasswordInput from "@/components/passwordInput/PasswordInput";

const Signup = () => {
  const [user, loading] = useAuthState(auth);

  const validationSchema = Yup.object().shape({
    fullName: Yup.string().required("required"),
    email: Yup.string()
      .email("invalid or incomplete email")
      .max(50, "email is too long")
      .required("required"),
    password: Yup.string()
      .min(6, "password is too short must be at least 6 characters")
      .required("required"),
  });

  const formik = useFormik({
    initialValues: {
      email: "",
      fullName: "",
      password: "",
    },
    validationSchema,
    onSubmit: async ({
      email,
      password,
      fullName,
    }: {
      email: string;
      password: string;
      fullName: string;
    }) => {
      const res = await createUserWithEmailAndPassword(auth, email, password);
      await updateProfile(auth?.currentUser as any, {
        displayName: fullName,
        photoURL: "https://i.pravatar.cc/150?u=a04258114e29026302d",
      });

      res?.user ? sessionStorage.setItem("user", "true") : toast.error("error");
    },
  });

  const { errors, values, touched, handleSubmit, handleChange, isSubmitting } =
    formik;

  if (typeof window !== "undefined") {
    if (user && sessionStorage.getItem("user")) return redirect("/login");
  }

  if (loading) return <p>Checking...</p>;

  return (
    <div className="flex min-h-screen items-center justify-center bg-black">
      <div className="flex w-full max-w-md flex-col rounded-lg bg-white p-8 shadow-md">
        <div className="text-black-1 mb-12 flex flex-col items-center justify-center gap-y-3">
          <p className="text-2xl font-bold">Example App</p>
        </div>
        <form
          noValidate
          onSubmit={handleSubmit}
          className="flex flex-col gap-y-6"
        >
          <Toaster position="top-right" />
          <div>
            <Input
              size="lg"
              type="text"
              label="User name"
              name="fullName"
              labelPlacement="outside"
              placeholder="Enter your name"
              onChange={handleChange}
              value={values?.fullName}
              errorMessage={errors?.fullName}
              isInvalid={(errors?.fullName && touched?.fullName) as boolean}
            />
          </div>
          <EmailInput
            handleChange={handleChange}
            values={values}
            errors={errors}
            touched={touched}
          />
          <PasswordInput
            handleChange={handleChange}
            values={values}
            errors={errors}
            touched={touched}
          />
          <Button
            fullWidth={true}
            type="submit"
            color="success"
            className="green-1 rounded-lg px-6 py-3.5 text-lg text-white hover:bg-green-600"
            isLoading={isSubmitting}
          >
            Sign up
          </Button>
        </form>
        <p className="mt-4 text-center text-sm text-gray-600">
          <Link href="/login" className="text-blue-500">
            Sign in
          </Link>
        </p>
      </div>
    </div>
  );
};

export default Signup;
Enter fullscreen mode Exit fullscreen mode

The part that integrates the firebase service into our project:

/lib/firebaseConfig.ts

import { getApp, getApps, initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

const app = !getApps().length ? initializeApp(firebaseConfig) : getApp();

const auth = getAuth(app);

export { app, auth };
Enter fullscreen mode Exit fullscreen mode

getApp() is the function that returns us our application registered in firebase.
initializeApp() is a function that takes our firebase environment information as a parameter to create an application object.

const app = !getApps().length ? initializeApp(firebaseConfig) : getApp();
Enter fullscreen mode Exit fullscreen mode

getAuth() function returns the authentication provider according to the firebase application object we created (in our application we chose this provider as email/password)

signup.tsx

const res = await createUserWithEmailAndPassword(auth, email, password);
Enter fullscreen mode Exit fullscreen mode

createUserWithEmailAndPassword() is used to create a new user on firebase side according to email and password.
updateProfile() is used to assign properties such as profile picture, full name to the created user.

Image description

Using the "react-firebase-hooks" package, we can access the logged in user information.

login.tsx

const [user, loading] = useAuthState(auth);
Enter fullscreen mode Exit fullscreen mode

Image description

Image description

Image description

You can access the source codes of the sample project from this link. 🔗

Continue with content 🚀

You can access my other content by clicking this link. I would be very happy if you like and leave a comment 😇

Top comments (0)