Setting auth.currentUser with session cookies: A guide

I am currently working on a Next.js 14 app with Firebase as my backend for hosting, authentication, and Firestore. In order to implement server-side rendering (SSR) in Next.js, I have learned that I need to use session cookies for authentication. I generate the session cookie using auth.createSessionCookie() from the Firebase SDK.

However, despite using auth.createSessionCookie(), I encounter an issue where I am not kept logged in on the client side, resulting in auth.currentUser being null. As a consequence, any Firestore security rules that rely on authentication fail since request.auth is also consistently null.

I have looked into solutions such as implementing signInWithSessionCookie(), passing the session cookie to Firestore in my request, or finding workarounds. Despite my research, which included references to other Stack Overflow posts and the official Firebase documentation, I have yet to find a viable solution. It is puzzling because this authentication approach is likely common among many Next.js apps using Firebase; therefore, one would expect better support. Am I missing something crucial here?

Answer №1

Utilizing cookies for authentication requires managing the user's auth state independently rather than relying on the Firebase client SDK. Essentially, this method mirrors the traditional approach of establishing cookie-based authentication, with Firebase handling tokens/cookies and their revocation when necessary.

To implement this, you will need to create an API that accepts the user's Firebase ID Token upon their login through the client SDK and subsequently sets the session cookie.

import "server-only";

import { NextResponse } from "next/server";
import { authAdmin } from "@/utils/firebase-admin";
import { cookies } from "next/headers";

// /api/auth/login
export async function POST(request: Request) {
  const authorization = request.headers.get("authorization")?.split(" ")[1];

  const sessionCookie = await authAdmin.createSessionCookie(authorization, {
    expiresIn: 14 * 24 * 60 * 60 * 1000, // 14 days in ms (maximum)
  });

  // setting the session cookie
  cookies().set("session", sessionCookie, {
    httpOnly: true,
    secure: true,
    domain: "localhost",
  });

  return NextResponse.json({ message: "Logged in" });
}

It is recommended to configure the Firebase auth persistence as NONE to ensure users are logged out of the Firebase client SDK later on. Subsequently, you can invoke the previously created API in the following manner:

// Sample login page
"use client";

import { auth } from "../../utils/firebase";
import { createUserWithEmailAndPassword, getIdToken } from "firebase/auth";
import { useState } from "react";

export default function Login() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const signUpUser = async (event: any) => {
    event.preventDefault();

    try {
      const userCredential = await createUserWithEmailAndPassword(
        auth,
        email,
        password
      );

      // call POST /api/auth/login with user's token
      const token = await getIdToken(userCredential.user);

      const response = await fetch("/api/auth/login", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${token}`,
          "Content-Type": "application/json",
        },
      });

      const data = await response.json();
     
      // redirect user
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <form className="max-w-sm mx-auto" onSubmit={signUpUser}>
      <div className="mb-5">
        <label>Your email</label>
        <input
          type="email"
          id="email"
          onChange={(event) => setEmail(event.target.value)}
          value={email}
          required
        />
      </div>
      <div className="mb-5">
        <label>Your password</label>
        <input
          type="password"
          id="password"
          onChange={(event) => setPassword(event.target.value)}
          value={password}
          required
        />
      </div>
      <button type="submit">Sign Up</button>
    </form>
  );
}

Upon successful login, it is essential to verify the user's auth states using cookies. This validation can be achieved utilizing Next Middlewares like so:

// middleware.ts

import { NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { authAdmin } from "./utils/firebase-admin";

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  if (pathname.startsWith("/_next") || pathname.startsWith("/favicon.ico")) {
    return NextResponse.next();
  }

  const authorization = request.headers.get("authorization")?.split(" ")[1];

  if (!authorization) {
    return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
  }

  try {
    const { uid } = await authAdmin.verifyIdToken(authorization);
    console.log("Current user:", uid)

    return response;
  } catch (e) {
    console.log("failed to decode session cookie")
    // Unauthorized user..
  }
}

I advise creating an additional API endpoint to retrieve user details, which can then be utilized to display current user information in sections like the navigation bar.

In conclusion, it's crucial to monitor the user's auth state on the server side, preferably within a middleware, to appropriately guide users to login or dashboard pages based on their authentication status.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Will the .replaceWith() method alter the code visible to search engines?

After successfully modifying the content of specific H1 elements to not return the value from a global variable using the following code; <script type="text/javascript"> $(document).ready(function() { $("H1").filter(function() { return $(this).text ...

Error: The object does not have a method called 'to top scroller' and it is causing an uncaught type error

A plugin was obtained from the following link: http://www.jqueryscript.net/other/jQuery-Plugin-For-Smooth-Scroll-To-Top-Bottom-scrollToTop.html.i and the code was attached below in the index.html file. Several jQuery plugins were attempted, but none work ...

What is the best way to incorporate a subcategory within a string and separate them by commas using Vue.js?

Is it possible to post subcategories in the following format now? Here is the current result: subcategory[] : Healthcare subcategory[] : education However, I would like to have them as a string separated by commas. This is my HTML code: <div id="sub ...

An issue arises when attempting to utilize v-model with a file input

Is there a way to reset a file input field in Vue.js after uploading a file? I attempted to set the v-model value to null, but encountered an error message that said: File inputs are read only. Use a v-on:change listener instead. This is my current cod ...

Styling buttons with different colors using React onClick event.getSelectedItem() method

I am having an issue with adding a border when the class is set to "transportation active" in my React code. When I click on one of the icons, all of them become active instead of just the clicked one. This behavior is not what I want, as I only want the ...

Using an environment variable for the `projectId` in a Firebase config can cause it to malfunction

Something really strange is happening with my Firebase config object and Vercel environment variables. I have successfully built my entire Firebase config object using Vercel environment variables, but when I try to use an environment variable for the val ...

Attempting to invoke a static function from a imported ES6 module

Currently, I am utilizing Firefox 56 with the setting dom.moduleScripts.enabled enabled. This configuration allows me to engage with native ES6 modules seamlessly. In my Vue2 component, there is a method that I have defined: import StorageZonesAjaxMethod ...

What are the steps to troubleshoot server-side TypeScript code in NextJS with WebStorm?

I am struggling to debug the NextJS API in WebStorm while using TypeScript and navigating through the app route. It is proving to be quite challenging to efficiently debug the API without relying heavily on console.log(). ...

Ensuring Proper Tabulator Width Adjustment Across All Browser Zoom Levels

<div id="wormGearTabulatorTable" style="max-height: 100%; max-width: 100%; position: relative;" class="tabulator" role="grid" tabulator-layout="fitDataTable"><div class="tabulator-header" role="rowgroup"><div class="tabulator-header-co ...

JavaScript Dynamic Array for Generating GoogleChart JSON Data

I am attempting to create a line chart using Google Charts ( ), but I am struggling with formatting the array for the chart. The required array format is as follows : var data = google.visualization.arrayToDataTable([ ['Year', 'Sales&ap ...

Retrieving data from Ajax and passing it to PHP

I am currently facing an issue with my Ajax code. When I try to access a textfield variable in PHP after alerting the value successfully, I receive an error stating Undefined index. Javascript Code <SCRIPT> function sendData(UserID) { var name = en ...

Issue with UI not updating when calling parent controller function from mdDialog

Hey there, I'm experiencing an issue with displaying a mdDialog from Angular Material. I'm using my directive's controller as the controller for the dialog to easily call a specific function without needing to pass additional data back and a ...

Issue with image magnification on Internet Explorer 9 using the Cloud Zoom plugin extension for Joomla

Recently, I've been utilizing a Joomla plugin called cloud zoom to enhance our gallery by providing an image magnification effect when hovering over the large image. You can see it in action on this link here. While it works flawlessly on most browser ...

Utilizing AngularJS to show content based on regular expressions using ng-show

With two images available, I need to display one image at a time based on an input regex pattern. Here is the code snippet: <input type="password" ng-model="password" placeholder="Enter Password"/> <img src="../close.png" ng-show="password != [ ...

Combining Meshes in three.js LOD

I am searching for an efficient way to utilize the LOD Object from three.js (view example here). My concept involves creating an LOD method similar to the one outlined in chapter 2.1 (found here). The structure consists of 3 levels: a 3D model in close ...

creating a Vuejs button function that will add together two given numbers

Help needed with VueJs code to display the sum of two numbers. Seeking assistance in developing a feature that calculates the sum only when the user clicks a button. Any guidance would be greatly appreciated! <!DOCTYPE html> <html lang="en"> ...

HTML script tag without a specified type

Exploring asp.net and frontend technologies for the first time. The code snippet I'm working with is as follows: <ul> @foreach (var item in Model) { <li> <img src="@item" alt="file here" width="100" height=" ...

Encountering obstacles with asynchronous requests while attempting to retrieve an Excel file using ajax

I am coding JavaScript for a SharePoint project, focusing on creating a function to retrieve data from an Excel document in a library. While I have successfully implemented the basic functionality, I am facing an issue with large file sizes causing the dat ...

Adding a URL link to a mentioned user from angular2-mentions within an Angular 4 application can be achieved in the following way:

Need help with adding a URL RouterLink to mention a user using angular2-mentions. This is the code snippet I currently have: <div class="col-sm-12"> <input type="text" [mention]="contactNames" [mentionConfig]="{triggerChar:'@',maxI ...

Looking to implement a star rating feature in Vue.js 2?

My perspective is as follows : <div class="col-md-8"> ... <star-rating :value="3"></star-rating> ... </div> This is how my star-rating component looks like : <template> <span class="rating"> &l ...