firebase-tools: context.auth is undefined in callable function using emulator

[REQUIRED] Environment info

firebase-tools: 7.16.0

firebase-tools: 7.16.0

Platform: macOS

[REQUIRED] Test case

Callable function:

import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import * as tools from "firebase-tools";

admin.initializeApp();

/**
 * Deletes a book and all its children forever.
 */
export const deleteBookForever = functions
  .runWith({
    timeoutSeconds: 540,
    memory: "1GB",
  })
  .https.onCall((data, context) => {
    const path = data.path;
    console.log("Context auth ", context.auth); // undefined
    if (
      !context.auth ||
      !context.auth.uid ||
      context.auth.uid !== data.userUid
    ) {
      return Promise.reject("Forbidden. The user could not be authenticated.");
    }
    return tools.firestore
      .delete(path, {
        project: process.env.GCLOUD_PROJECT,
        recursive: true,
        yes: true,
        token: functions.config()?.fb?.token,
      })
      .then(() => path)
      .catch(err => {
        console.error("Delete ERROR:", err.message);
      });
  });

Emulator setup client side:

if (window.location.hostname === "localhost") {
   app.firestore().settings({
        host: "localhost:8081",
        ssl: false,
      });
  app.functions().useFunctionsEmulator("http://localhost:5001");
}

[REQUIRED] Steps to reproduce

[REQUIRED] Expected behavior

context.auth.uid should be defined with the uid of the currently signed in user

[REQUIRED] Actual behavior

context.auth is undefined

console.log(context.rawRequest.headers["x-callable-context-auth"]) outputs:

{
    req: {
      token: {
        name: "maierson",
        iss: "https://securetoken.google.com/<project-id>",
        aud: "<project-id>",
        auth_time: 1583853288,
        user_id: "<my-user-id>",
        sub: "<my-user-id>",
        iat: 1585058399,
        exp: 1585061999,
        email: "<my-email>",
        email_verified: false,
        firebase: {
          identities: { email: ["<my-email>"] },
          sign_in_provider: "password",
        },
      },
    },
  };

Note. The authenticated user is obtained from the production system (no emulator). All the substituted placeholders are correct (<my-project>, <my-user-id> and <my-email>) as per the production environment.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 20 (9 by maintainers)

Most upvoted comments

[REQUIRED] Environment info

firebase-tools: 7.16.0

firebase-tools: 7.16.0

Platform: macOS

[REQUIRED] Test case

Callable function:

import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
import * as tools from "firebase-tools";

admin.initializeApp();

/**
 * Deletes a book and all its children forever.
 */
export const deleteBookForever = functions
  .runWith({
    timeoutSeconds: 540,
    memory: "1GB",
  })
  .https.onCall((data, context) => {
    const path = data.path;
    console.log("Context auth ", context.auth); // undefined
    if (
      !context.auth ||
      !context.auth.uid ||
      context.auth.uid !== data.userUid
    ) {
      return Promise.reject("Forbidden. The user could not be authenticated.");
    }
    return tools.firestore
      .delete(path, {
        project: process.env.GCLOUD_PROJECT,
        recursive: true,
        yes: true,
        token: functions.config()?.fb?.token,
      })
      .then(() => path)
      .catch(err => {
        console.error("Delete ERROR:", err.message);
      });
  });

Emulator setup client side:

if (window.location.hostname === "localhost") {
   app.firestore().settings({
        host: "localhost:8081",
        ssl: false,
      });
  app.functions().useFunctionsEmulator("http://localhost:5001");
}

[REQUIRED] Steps to reproduce

[REQUIRED] Expected behavior

context.auth.uid should be defined with the uid of the currently signed in user

[REQUIRED] Actual behavior

context.auth is undefined

console.log(context.rawRequest.headers["x-callable-context-auth"]) outputs:

{
    req: {
      token: {
        name: "maierson",
        iss: "https://securetoken.google.com/<project-id>",
        aud: "<project-id>",
        auth_time: 1583853288,
        user_id: "<my-user-id>",
        sub: "<my-user-id>",
        iat: 1585058399,
        exp: 1585061999,
        email: "<my-email>",
        email_verified: false,
        firebase: {
          identities: { email: ["<my-email>"] },
          sign_in_provider: "password",
        },
      },
    },
  };

Note. The authenticated user is obtained from the production system (no emulator). All the substituted placeholders are correct (<my-project>, <my-user-id> and <my-email>) as per the production environment.

Hey @maierson , I was wondering if you can share how (and if) did you work around the undefined context in firebase emulators issue? I’m running into the same problem with the recent versions of the sdk. Thanks!

OH! I figured it out.

It’s how we are stubbing .onCall. It doesn’t currently work with runWith … I directly stubbed https.onCall I need to do it in a more advanced way.

I’m still seeing this issue on version 9.10.0 Was the bug some how re-introduced? Or am I doing something wrong?

Here is my function:

export const onDilemmaCreated = functions.firestore
  .document('dilemmas/{dilemmaId}')
  .onCreate(async (snap, context) => {
    const MAX_RECENT_SIZE = 10;
    const dilemma = snap.data();
    console.log(context.auth); // <- This is always undefined
    if (context.auth) {
      const uid = context.auth.uid;
      const userDataDoc = await admin
        .firestore()
        .collection('users')
        .doc(uid)
        .get();
      const recent = userDataDoc.data()?.recent || [];

      await userDataDoc.ref.update({
        recent: [
          {
            id: dilemma.id,
            name: dilemma.name,
            type: dilemma.type,
            metadata: dilemma.metadata,
          },
          ...recent.splice(0, MAX_RECENT_SIZE - 1),
        ],
      });
    }
  });

@samtstern I got same behaviour as @maierson v7.16.1 a context.auth still undefined. Why this issue mark as closed?